From 90307a014faf0d7cbfc8714cddc53edae22532fd Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 22 Apr 2025 19:15:22 -0400 Subject: [PATCH] Shrinker API v4 Yet another major shrinker API evolution in v6.6-rc4-53-gc42d50aefd17. The struct shrinker now has to be dynamically allocated. This is purposely a backwards incompatible break. Collapse the previous KC_ALLOC_SHRINKER, KC_INIT_SHRINKER_FUNCS, and KC_REGISTER_SHRINKER macros into a single KC_SETUP_SHRINKER macro. The three operations have to happen in different orders on different kernel APIs (the name is needed at alloc time on el10 and at register time on KC_SHRINKER_NAME kernels), so coupling them keeps the ordering correct per kernel. Add KC_SHRINKER_IS_NULL so callers can detect shrinker_alloc() failure on el10 and return -ENOMEM. The macro compiles to a constant 0 on older kernels where the shrinker is an embedded struct that cannot fail allocation. Signed-off-by: Auke Kok --- kmod/src/Makefile.kernelcompat | 9 ++++++ kmod/src/block.c | 11 ++++--- kmod/src/item.c | 10 +++---- kmod/src/kernelcompat.h | 53 +++++++++++++++++++++++++++------- kmod/src/quota.c | 12 +++++--- kmod/src/wkic.c | 11 +++++-- 6 files changed, 79 insertions(+), 27 deletions(-) diff --git a/kmod/src/Makefile.kernelcompat b/kmod/src/Makefile.kernelcompat index 0a27287c..b81200f8 100644 --- a/kmod/src/Makefile.kernelcompat +++ b/kmod/src/Makefile.kernelcompat @@ -265,6 +265,15 @@ ifneq (,$(shell grep -s 'define __assign_str.dst, src' \ ccflags-y += -DKC_HAVE_ASSIGN_STR_PARMS endif +# +# v6.6-rc4-53-gc42d50aefd17 +# +# el10 yet again modifies the shrinker API significantly, breaking our current +# implementation. +ifneq (,$(shell grep 'struct shrinker .shrinker_alloc' include/linux/shrinker.h)) +ccflags-y += -DKC_SHRINKER_ALLOC +endif + # # v6.15-13744-g41cb08555c41 # diff --git a/kmod/src/block.c b/kmod/src/block.c index 81c43836..115acc97 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -1300,9 +1300,12 @@ int scoutfs_block_setup(struct super_block *sb) binf->sb = sb; init_waitqueue_head(&binf->waitq); - KC_INIT_SHRINKER_FUNCS(&binf->shrinker, block_count_objects, - block_scan_objects); - KC_REGISTER_SHRINKER(&binf->shrinker, "scoutfs-block:" SCSBF, SCSB_ARGS(sb)); + KC_SETUP_SHRINKER(binf->shrinker, binf, 0, block_count_objects, + block_scan_objects, "scoutfs-block:" SCSBF, SCSB_ARGS(sb)); + if (KC_SHRINKER_IS_NULL(binf->shrinker)) { + ret = -ENOMEM; + goto out; + } INIT_WORK(&binf->free_work, block_free_work); init_llist_head(&binf->free_llist); @@ -1324,7 +1327,7 @@ void scoutfs_block_destroy(struct super_block *sb) struct block_info *binf = SCOUTFS_SB(sb)->block_info; if (binf) { - KC_UNREGISTER_SHRINKER(&binf->shrinker); + KC_UNREGISTER_SHRINKER(binf->shrinker); block_shrink_all(sb); flush_work(&binf->free_work); rhashtable_destroy(&binf->ht); diff --git a/kmod/src/item.c b/kmod/src/item.c index 1ad4dcbe..650e261c 100644 --- a/kmod/src/item.c +++ b/kmod/src/item.c @@ -2607,10 +2607,10 @@ int scoutfs_item_setup(struct super_block *sb) for_each_possible_cpu(cpu) init_pcpu_pages(cinf, cpu); - - KC_INIT_SHRINKER_FUNCS(&cinf->shrinker, item_cache_count_objects, - item_cache_scan_objects); - KC_REGISTER_SHRINKER(&cinf->shrinker, "scoutfs-item:" SCSBF, SCSB_ARGS(sb)); + KC_SETUP_SHRINKER(cinf->shrinker, cinf, 0, item_cache_count_objects, + item_cache_scan_objects, "scoutfs-item:" SCSBF, SCSB_ARGS(sb)); + if (KC_SHRINKER_IS_NULL(cinf->shrinker)) + return -ENOMEM; sbi->item_cache_info = cinf; return 0; @@ -2628,7 +2628,7 @@ void scoutfs_item_destroy(struct super_block *sb) int cpu; if (cinf) { - KC_UNREGISTER_SHRINKER(&cinf->shrinker); + KC_UNREGISTER_SHRINKER(cinf->shrinker); for_each_possible_cpu(cpu) drop_pcpu_pages(sb, cinf, cpu); diff --git a/kmod/src/kernelcompat.h b/kmod/src/kernelcompat.h index e93dfbe0..e8182868 100644 --- a/kmod/src/kernelcompat.h +++ b/kmod/src/kernelcompat.h @@ -4,22 +4,53 @@ #include #include -#define KC_DEFINE_SHRINKER(name) struct shrinker name -#define KC_INIT_SHRINKER_FUNCS(name, countfn, scanfn) do { \ - __typeof__(name) _shrink = (name); \ - _shrink->count_objects = (countfn); \ - _shrink->scan_objects = (scanfn); \ - _shrink->seeks = DEFAULT_SEEKS; \ -} while (0) +#ifdef KC_SHRINKER_ALLOC +// el10+ +#define KC_DEFINE_SHRINKER(name) struct shrinker *(name) +#define KC_SHRINKER_CONTAINER_OF(ptr, type) ptr->private_data +#define KC_SETUP_SHRINKER(ptr, priv, flags, countfn, scanfn, fmt, args) \ +do { \ + ptr = shrinker_alloc(flags, fmt, args); \ + if (ptr) { \ + ptr->private_data = (priv); \ + ptr->seeks = DEFAULT_SEEKS; \ + ptr->count_objects = countfn; \ + ptr->scan_objects = scanfn; \ + shrinker_register(ptr); \ + } \ +} while (0) +#define KC_UNREGISTER_SHRINKER(ptr) shrinker_free(ptr) +#define KC_SHRINKER_FN(ptr) (ptr) +#define KC_SHRINKER_IS_NULL(ptr) (!(ptr)) + +#else /* KC_SHRINKER_ALLOC */ +// el9, el8 + +#define KC_DEFINE_SHRINKER(name) struct shrinker (name) #define KC_SHRINKER_CONTAINER_OF(ptr, type) container_of(ptr, type, shrinker) #ifdef KC_SHRINKER_NAME -#define KC_REGISTER_SHRINKER register_shrinker +#define KC_SETUP_SHRINKER(ptr, priv, flags, countfn, scanfn, fmt, args) \ +do { \ + (ptr).count_objects = (countfn); \ + (ptr).scan_objects = (scanfn); \ + (ptr).seeks = DEFAULT_SEEKS; \ + register_shrinker(&(ptr), fmt, args); \ +} while (0) #else -#define KC_REGISTER_SHRINKER(ptr, fmt, ...) (register_shrinker(ptr)) +#define KC_SETUP_SHRINKER(ptr, priv, flags, countfn, scanfn, fmt, args) \ +do { \ + (ptr).count_objects = (countfn); \ + (ptr).scan_objects = (scanfn); \ + (ptr).seeks = DEFAULT_SEEKS; \ + register_shrinker(&(ptr)); \ +} while (0) #endif /* KC_SHRINKER_NAME */ -#define KC_UNREGISTER_SHRINKER(ptr) (unregister_shrinker(ptr)) -#define KC_SHRINKER_FN(ptr) (ptr) +#define KC_UNREGISTER_SHRINKER(ptr) (unregister_shrinker(&(ptr))) +#define KC_SHRINKER_FN(ptr) (&ptr) +#define KC_SHRINKER_IS_NULL(ptr) (0) + +#endif /* KC_SHRINKER_ALLOC */ #ifdef KC_GENERIC_PERFORM_WRITE_KIOCB_IOV_ITER static inline int kc_generic_perform_write(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) diff --git a/kmod/src/quota.c b/kmod/src/quota.c index 4b3df75b..ac6752f2 100644 --- a/kmod/src/quota.c +++ b/kmod/src/quota.c @@ -270,7 +270,7 @@ static void shrink_all_cached_checks(struct squota_info *qtinf) { struct shrink_control sc = { .nr_to_scan = LONG_MAX, }; - scan_cached_checks(KC_SHRINKER_FN(&qtinf->shrinker), &sc); + scan_cached_checks(KC_SHRINKER_FN(qtinf->shrinker), &sc); } static u8 ns_is_attr(u8 ns) @@ -1235,8 +1235,12 @@ int scoutfs_quota_setup(struct super_block *sb) spin_lock_init(&qtinf->lock); init_waitqueue_head(&qtinf->waitq); - KC_INIT_SHRINKER_FUNCS(&qtinf->shrinker, count_cached_checks, scan_cached_checks); - KC_REGISTER_SHRINKER(&qtinf->shrinker, "scoutfs-quota:" SCSBF, SCSB_ARGS(sb)); + KC_SETUP_SHRINKER(qtinf->shrinker, qtinf, 0, count_cached_checks, + scan_cached_checks, "scoutfs-quota:" SCSBF, SCSB_ARGS(sb)); + if (KC_SHRINKER_IS_NULL(qtinf->shrinker)) { + ret = -ENOMEM; + goto out; + } sbi->squota_info = qtinf; @@ -1260,7 +1264,7 @@ void scoutfs_quota_destroy(struct super_block *sb) if (qtinf) { debugfs_remove(qtinf->drop_dentry); - KC_UNREGISTER_SHRINKER(&qtinf->shrinker); + KC_UNREGISTER_SHRINKER(qtinf->shrinker); spin_lock(&qtinf->lock); rs = rcu_dereference_protected(qtinf->ruleset, lockdep_is_held(&qtinf->lock)); diff --git a/kmod/src/wkic.c b/kmod/src/wkic.c index 3109dc61..9129f16e 100644 --- a/kmod/src/wkic.c +++ b/kmod/src/wkic.c @@ -1169,8 +1169,13 @@ int scoutfs_wkic_setup(struct super_block *sb) } winf->sb = sb; - KC_INIT_SHRINKER_FUNCS(&winf->shrinker, wkic_shrink_count, wkic_shrink_scan); - KC_REGISTER_SHRINKER(&winf->shrinker, "scoutfs-weak_item:" SCSBF, SCSB_ARGS(sb)); + KC_SETUP_SHRINKER(winf->shrinker, winf, 0, wkic_shrink_count, + wkic_shrink_scan, "scoutfs-weak_item:" SCSBF, SCSB_ARGS(sb)); + if (KC_SHRINKER_IS_NULL(winf->shrinker)) { + debugfs_remove(winf->drop_dentry); + kfree(winf); + return -ENOMEM; + } sbi->wkic_info = winf; return 0; @@ -1198,7 +1203,7 @@ void scoutfs_wkic_destroy(struct super_block *sb) if (winf) { debugfs_remove(winf->drop_dentry); - KC_UNREGISTER_SHRINKER(&winf->shrinker); + KC_UNREGISTER_SHRINKER(winf->shrinker); /* trees are in sync so tearing down one frees all pages */ rbtree_postorder_for_each_entry_safe(wpage, tmp, &winf->wpage_roots[0], nodes[0]) {