@@ -34,6 +34,7 @@
#include "xfs_health.h"
#include "xfs_trace.h"
#include "xfs_ag.h"
+#include "xfs_verity.h"
#include "scrub/stats.h"
static DEFINE_MUTEX(xfs_uuid_table_mutex);
@@ -813,6 +814,10 @@ xfs_mountfs(
if (error)
goto out_fail_wait;
+ error = xfs_verity_register_shrinker(mp);
+ if (error)
+ goto out_inodegc_shrinker;
+
/*
* Log's mount-time initialization. The first part of recovery can place
* some items on the AIL, to be handled when recovery is finished or
@@ -823,7 +828,7 @@ xfs_mountfs(
XFS_FSB_TO_BB(mp, sbp->sb_logblocks));
if (error) {
xfs_warn(mp, "log mount failed");
- goto out_inodegc_shrinker;
+ goto out_verity_shrinker;
}
/* Enable background inode inactivation workers. */
@@ -1018,6 +1023,8 @@ xfs_mountfs(
xfs_unmount_flush_inodes(mp);
out_log_dealloc:
xfs_log_mount_cancel(mp);
+ out_verity_shrinker:
+ xfs_verity_unregister_shrinker(mp);
out_inodegc_shrinker:
shrinker_free(mp->m_inodegc_shrinker);
out_fail_wait:
@@ -1100,6 +1107,7 @@ xfs_unmountfs(
#if defined(DEBUG)
xfs_errortag_clearall(mp);
#endif
+ xfs_verity_unregister_shrinker(mp);
shrinker_free(mp->m_inodegc_shrinker);
xfs_free_perag(mp);
@@ -255,6 +255,12 @@ typedef struct xfs_mount {
/* Hook to feed dirent updates to an active online repair. */
struct xfs_hooks m_dir_update_hooks;
+
+#ifdef CONFIG_FS_VERITY
+ /* shrinker and cached blocks count for merkle trees */
+ struct shrinker *m_verity_shrinker;
+ struct percpu_counter m_verity_blocks;
+#endif
} xfs_mount_t;
#define M_IGEO(mp) (&(mp)->m_ino_geo)
@@ -4797,6 +4797,26 @@ DEFINE_EVENT(xfs_verity_cache_class, name, \
DEFINE_XFS_VERITY_CACHE_EVENT(xfs_verity_cache_load);
DEFINE_XFS_VERITY_CACHE_EVENT(xfs_verity_cache_store);
DEFINE_XFS_VERITY_CACHE_EVENT(xfs_verity_cache_drop);
+
+TRACE_EVENT(xfs_verity_shrinker_count,
+ TP_PROTO(struct xfs_mount *mp, unsigned long long count,
+ unsigned long caller_ip),
+ TP_ARGS(mp, count, caller_ip),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(unsigned long long, count)
+ __field(void *, caller_ip)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->count = count;
+ __entry->caller_ip = (void *)caller_ip;
+ ),
+ TP_printk("dev %d:%d count %llu caller %pS",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->count,
+ __entry->caller_ip)
+)
#endif /* CONFIG_XFS_VERITY */
#endif /* _TRACE_XFS_H */
@@ -18,6 +18,7 @@
#include "xfs_trans.h"
#include "xfs_attr_leaf.h"
#include "xfs_trace.h"
+#include "xfs_icache.h"
#include <linux/fsverity.h>
/*
@@ -217,6 +218,82 @@ xfs_fsverity_merkle_key_from_disk(
return be64_to_cpu(key->merkleoff);
}
+/* Count the merkle tree blocks that we might be able to reclaim. */
+static unsigned long
+xfs_verity_shrinker_count(
+ struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ struct xfs_mount *mp = shrink->private_data;
+ s64 count;
+
+ if (!xfs_has_verity(mp))
+ return SHRINK_EMPTY;
+
+ count = percpu_counter_sum_positive(&mp->m_verity_blocks);
+
+ trace_xfs_verity_shrinker_count(mp, count, _RET_IP_);
+ return min_t(s64, ULONG_MAX, count);
+}
+
+/* Actually try to reclaim merkle tree blocks. */
+static unsigned long
+xfs_verity_shrinker_scan(
+ struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ struct xfs_mount *mp = shrink->private_data;
+
+ if (!xfs_has_verity(mp))
+ return SHRINK_STOP;
+
+ return 0;
+}
+
+/* Register a shrinker so we can release cached merkle tree blocks. */
+int
+xfs_verity_register_shrinker(
+ struct xfs_mount *mp)
+{
+ int error;
+
+ if (!xfs_has_verity(mp))
+ return 0;
+
+ error = percpu_counter_init(&mp->m_verity_blocks, 0, GFP_KERNEL);
+ if (error)
+ return error;
+
+ mp->m_verity_shrinker = shrinker_alloc(0, "xfs-verity:%s",
+ mp->m_super->s_id);
+ if (!mp->m_verity_shrinker) {
+ percpu_counter_destroy(&mp->m_verity_blocks);
+ return -ENOMEM;
+ }
+
+ mp->m_verity_shrinker->count_objects = xfs_verity_shrinker_count;
+ mp->m_verity_shrinker->scan_objects = xfs_verity_shrinker_scan;
+ mp->m_verity_shrinker->seeks = 0;
+ mp->m_verity_shrinker->private_data = mp;
+
+ shrinker_register(mp->m_verity_shrinker);
+
+ return 0;
+}
+
+/* Unregister the merkle tree block shrinker. */
+void
+xfs_verity_unregister_shrinker(struct xfs_mount *mp)
+{
+ if (!xfs_has_verity(mp))
+ return;
+
+ ASSERT(percpu_counter_sum(&mp->m_verity_blocks) == 0);
+
+ shrinker_free(mp->m_verity_shrinker);
+ percpu_counter_destroy(&mp->m_verity_blocks);
+}
+
static int
xfs_verity_get_descriptor(
struct inode *inode,
@@ -10,11 +10,16 @@ void xfs_verity_cache_init(struct xfs_inode *ip);
void xfs_verity_cache_drop(struct xfs_inode *ip);
void xfs_verity_cache_destroy(struct xfs_inode *ip);
+int xfs_verity_register_shrinker(struct xfs_mount *mp);
+void xfs_verity_unregister_shrinker(struct xfs_mount *mp);
+
extern const struct fsverity_operations xfs_verity_ops;
#else
# define xfs_verity_cache_init(ip) ((void)0)
# define xfs_verity_cache_drop(ip) ((void)0)
# define xfs_verity_cache_destroy(ip) ((void)0)
+# define xfs_verity_register_shrinker(mp) (0)
+# define xfs_verity_unregister_shrinker(mp) ((void)0)
#endif /* CONFIG_FS_VERITY */
#endif /* __XFS_VERITY_H__ */