From a49584739ab900f652da42abdaf6755610b5e5e5 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Tue, 2 Aug 2022 15:29:48 -0700 Subject: [PATCH] Use count/scan objects shrinking interface Move to the more recent interfaces for counting and scanning cached objects to shrink. Signed-off-by: Zach Brown --- kmod/src/Makefile | 4 ++++ kmod/src/Makefile.kernelcompat | 10 ++++++++++ kmod/src/block.c | 36 +++++++++++++++++++++------------- kmod/src/counters.h | 2 ++ kmod/src/kernelcompat.c | 23 ++++++++++++++++++++++ kmod/src/kernelcompat.h | 32 ++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 kmod/src/kernelcompat.c diff --git a/kmod/src/Makefile b/kmod/src/Makefile index 975ec319..7d81aaf1 100644 --- a/kmod/src/Makefile +++ b/kmod/src/Makefile @@ -46,6 +46,10 @@ scoutfs-y += \ volopt.o \ xattr.o +ifdef KC_BUILD_KERNELCOMPAT +scoutfs-y += kernelcompat.o +endif + # # The raw types aren't available in userspace headers. Make sure all # the types we use in the headers are the exported __ versions. diff --git a/kmod/src/Makefile.kernelcompat b/kmod/src/Makefile.kernelcompat index c4466151..70719d28 100644 --- a/kmod/src/Makefile.kernelcompat +++ b/kmod/src/Makefile.kernelcompat @@ -73,3 +73,13 @@ endif ifneq (,$(shell grep 'bi_status' include/linux/blk_types.h)) ccflags-y += -DKC_BIO_BI_STATUS endif + +# +# v3.11-8765-ga0b02131c5fc +# +# Remove the old ->shrink() API, ->{scan,count}_objects is preferred. +# +ifneq (,$(shell grep '(*shrink)' include/linux/shrinker.h)) +ccflags-y += -DKC_SHRINKER_SHRINK +KC_BUILD_KERNELCOMPAT=1 +endif diff --git a/kmod/src/block.c b/kmod/src/block.c index 63a7b8e6..8a01bde6 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -58,7 +58,7 @@ struct block_info { atomic64_t access_counter; struct rhashtable ht; wait_queue_head_t waitq; - struct shrinker shrinker; + KC_DEFINE_SHRINKER(shrinker); struct work_struct free_work; struct llist_head free_llist; }; @@ -1040,6 +1040,17 @@ u64 scoutfs_block_writer_dirty_bytes(struct super_block *sb, return wri->nr_dirty_blocks * SCOUTFS_BLOCK_LG_SIZE; } +static unsigned long block_count_objects(struct shrinker *shrink, struct shrink_control *sc) +{ + struct block_info *binf = container_of(shrink, struct block_info, shrinker); + struct super_block *sb = binf->sb; + + scoutfs_inc_counter(sb, block_cache_scan_objects); + + return min_t(u64, (u64)atomic_read(&binf->total_inserted) * SCOUTFS_BLOCK_LG_PAGES_PER, + ULONG_MAX / 2); /* magic numbers as we approach ~0UL :/ */ +} + /* * Remove a number of cached blocks that haven't been used recently. * @@ -1060,23 +1071,19 @@ u64 scoutfs_block_writer_dirty_bytes(struct super_block *sb, * atomically remove blocks when the only references are ours and the * hash table. */ -static int block_shrink(struct shrinker *shrink, struct shrink_control *sc) +static unsigned long block_scan_objects(struct shrinker *shrink, struct shrink_control *sc) { - struct block_info *binf = container_of(shrink, struct block_info, - shrinker); + struct block_info *binf = container_of(shrink, struct block_info, shrinker); struct super_block *sb = binf->sb; struct rhashtable_iter iter; struct block_private *bp; + unsigned long freed = 0; unsigned long nr; u64 recently; - nr = sc->nr_to_scan; - if (nr == 0) - goto out; + scoutfs_inc_counter(sb, block_cache_scan_objects); - scoutfs_inc_counter(sb, block_cache_shrink); - - nr = DIV_ROUND_UP(nr, SCOUTFS_BLOCK_LG_PAGES_PER); + nr = DIV_ROUND_UP(sc->nr_to_scan, SCOUTFS_BLOCK_LG_PAGES_PER); restart: recently = accessed_recently(binf); @@ -1119,6 +1126,7 @@ restart: if (block_remove_solo(sb, bp)) { scoutfs_inc_counter(sb, block_cache_shrink_remove); TRACE_BLOCK(shrink, bp); + freed++; nr--; } block_put(sb, bp); @@ -1127,9 +1135,8 @@ restart: rhashtable_walk_stop(&iter); rhashtable_walk_exit(&iter); -out: - return min_t(u64, (u64)atomic_read(&binf->total_inserted) * SCOUTFS_BLOCK_LG_PAGES_PER, - INT_MAX); + + return freed; } struct sm_block_completion { @@ -1255,7 +1262,8 @@ int scoutfs_block_setup(struct super_block *sb) atomic_set(&binf->total_inserted, 0); atomic64_set(&binf->access_counter, 0); init_waitqueue_head(&binf->waitq); - binf->shrinker.shrink = block_shrink; + KC_INIT_SHRINKER_FUNCS(struct block_info, shrinker, + &binf->shrinker, block_count_objects, block_scan_objects); binf->shrinker.seeks = DEFAULT_SEEKS; register_shrinker(&binf->shrinker); INIT_WORK(&binf->free_work, block_free_work); diff --git a/kmod/src/counters.h b/kmod/src/counters.h index 79978510..137b4c24 100644 --- a/kmod/src/counters.h +++ b/kmod/src/counters.h @@ -30,6 +30,8 @@ EXPAND_COUNTER(block_cache_free) \ EXPAND_COUNTER(block_cache_free_work) \ EXPAND_COUNTER(block_cache_remove_stale) \ + EXPAND_COUNTER(block_cache_count_objects) \ + EXPAND_COUNTER(block_cache_scan_objects) \ EXPAND_COUNTER(block_cache_shrink) \ EXPAND_COUNTER(block_cache_shrink_next) \ EXPAND_COUNTER(block_cache_shrink_recent) \ diff --git a/kmod/src/kernelcompat.c b/kmod/src/kernelcompat.c new file mode 100644 index 00000000..22b39f2f --- /dev/null +++ b/kmod/src/kernelcompat.c @@ -0,0 +1,23 @@ + +#include "kernelcompat.h" + +#ifdef KC_SHRINKER_SHRINK +#include +/* + * If a target doesn't have that .{count,scan}_objects() interface then + * we have a .shrink() helper that performs the shrink work in terms of + * count/scan. + */ +int kc_shrink_wrapper(struct shrinker *shrink, struct shrink_control *sc) +{ + struct kc_shrinker_funcs *funcs = KC_SHRINKER_FUNCS(shrink); + unsigned long nr; + + if (sc->nr_to_scan != 0) + funcs->scan_objects(shrink, sc); + + nr = funcs->count_objects(shrink, sc); + + return min_t(unsigned long, nr, INT_MAX); +} +#endif diff --git a/kmod/src/kernelcompat.h b/kmod/src/kernelcompat.h index 8c0021f5..0b6c0359 100644 --- a/kmod/src/kernelcompat.h +++ b/kmod/src/kernelcompat.h @@ -91,4 +91,36 @@ do { \ #define kc_bio_get_errno(bio) ({ (int)((void)(bio), _error_arg); }) #endif +#ifndef KC_SHRINKER_SHRINK +#define KC_DEFINE_SHRINKER(name) struct shrinker name +#define KC_INIT_SHRINKER_FUNCS(type, name, shrink, count, scan) do { \ + __typeof__(shrink) _shrink = (shrink); \ + _shrink->count_objects = count; \ + _shrink->scan_objects = scan; \ +} while (0) +#else +#include +struct kc_shrinker_funcs { + unsigned long (*count_objects)(struct shrinker *, struct shrink_control *sc); + unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *sc); +}; +/* using adjacent member of an unnamed struct */ +#define KC_DEFINE_SHRINKER(name) \ + { \ + struct kc_shrinker_funcs shrinker_funcs; \ + struct shinker name; \ + } +#define KC_SHRINKER_FUNCS(shrinker) \ + ((void *)((long)(shrink) - sizeof(struct kc_shrinker_funcs))) +#define KC_INIT_SHRINKER_FUNCS(type, name, shrink, count, scan) do { \ + BUILD_BUG_ON(offsetof(cont, shrink_funcs) + sizeof(struct kc_shrinker_funcs)) != \ + offsetof(cont, name) + sizeof(struct kc_shrinker_funcs); \ + struct kc_shrinker_funcs *_funcs = KC_SHRINKER_FUNCS(shrink) \ + __typeof__(shrink) _shrink = (shrink); \ + _funcs->count_objects = count; \ + _funcs->scan_objects = scan; \ + _shrink->shrink = kc_shrink_wrapper; \ +} while (0) +#endif + #endif