From 1d6cdb63f3a1493addadddb997320025780ce610 Mon Sep 17 00:00:00 2001 From: James Simmons Date: Mon, 7 Apr 2025 09:33:22 -0400 Subject: [PATCH] LU-18175 lustre: back port shrinker debugfs support. The Linux kernel has developed a shrinker API to help deal with a system that under memory pressure. Lustre uses this shrinker API for several subsystems. While it helps this API lacked the ability to manually trigger a cleanup. Now with newer kernels a debugfs interface has been developed just for this purpose. Back port this interface so Lustre adminstrators can use this new feature to lighten the load related to Lustre. The following link explains this new debugfs interface: https://lwn.net/Articles/896274 and how to use it: https://docs.kernel.org/admin-guide/mm/shrinker_debugfs.html For older kernels since Lustre shrinkers are not memcg aware we remove that code since older kernels lack newer memcg features. Sadly memcg functionality is not exported so even for newer kernels we can't support it. Note this is the basic functionality present in newer kernels. A follow on patch will provide support in our tools for this. Test-Parameter: trivial Signed-off-by: James Simmons Change-Id: I70aeb4af15c46036e51e362a69602babfd5ea05f Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/56174 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin Reviewed-by: Timothy Day Reviewed-by: Brian Atkinson --- include/lustre_compat/linux/shrinker.h | 42 ++++ libcfs/include/libcfs/linux/linux-misc.h | 8 + libcfs/libcfs/Makefile.in | 12 +- libcfs/libcfs/linux/linux-prim.c | 29 ++- lustre/include/lustre_compat.h | 86 ++------ lustre/ldlm/ldlm_pool.c | 7 +- lustre/obdclass/lu_object.c | 5 +- lustre/obdclass/page_pools.c | 3 +- lustre/osc/osc_request.c | 5 +- lustre_compat/lib/Makefile | 2 +- lustre_compat/mm/Makefile | 7 + lustre_compat/mm/shrinker_debug.c | 338 +++++++++++++++++++++++++++++++ 12 files changed, 457 insertions(+), 87 deletions(-) create mode 100644 include/lustre_compat/linux/shrinker.h create mode 100644 lustre_compat/mm/Makefile create mode 100644 lustre_compat/mm/shrinker_debug.c diff --git a/include/lustre_compat/linux/shrinker.h b/include/lustre_compat/linux/shrinker.h new file mode 100644 index 0000000..9d73079 --- /dev/null +++ b/include/lustre_compat/linux/shrinker.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_SHRINKER_LUSTRE_H +#define _LINUX_SHRINKER_LUSTRE_H + +#include +#include +#include +#include +#include + +struct ll_shrinker_ops { +#ifdef HAVE_SHRINKER_COUNT + unsigned long (*count_objects)(struct shrinker *shrinker, + struct shrink_control *sc); + unsigned long (*scan_objects)(struct shrinker *shrinker, + struct shrink_control *sc); +#else + int (*shrink)(struct shrinker *shrinker, struct shrink_control *sc); +#endif + int seeks; /* seeks to recreate an obj */ +}; + +#ifndef CONFIG_SHRINKER_DEBUG +struct ll_shrinker { + struct shrinker ll_shrinker; + + void *private_data; + + int debugfs_id; + const char *name; + struct dentry *debugfs_entry; +}; +#endif + +void ll_shrinker_free(struct shrinker *shrinker); + +/* allocate and register a shrinker, return should be checked with IS_ERR() */ +struct shrinker *ll_shrinker_create(struct ll_shrinker_ops *ops, + unsigned int flags, + const char *fmt, ...); + +#endif /* _LINUX_SHRINKER_LUSTRE_H */ diff --git a/libcfs/include/libcfs/linux/linux-misc.h b/libcfs/include/libcfs/linux/linux-misc.h index 8e488ae..43228e4 100644 --- a/libcfs/include/libcfs/linux/linux-misc.h +++ b/libcfs/include/libcfs/linux/linux-misc.h @@ -346,4 +346,12 @@ void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits); #define KOBJ_ATTRIBUTE_GROUPS(_name) ATTRIBUTE_GROUPS(_name) #endif +#ifndef CONFIG_SHRINKER_DEBUG +void shrinker_debugfs_fini(void); +int shrinker_debugfs_init(void); +#else +static inline void shrinker_debugfs_fini(void) {}; +static inline int shrinker_debugfs_init(void) { return 0; }; +#endif + #endif /* __LIBCFS_LINUX_MISC_H__ */ diff --git a/libcfs/libcfs/Makefile.in b/libcfs/libcfs/Makefile.in index 0984486..3692050 100644 --- a/libcfs/libcfs/Makefile.in +++ b/libcfs/libcfs/Makefile.in @@ -6,16 +6,22 @@ MODULES = libcfs -COMPAT := @top_srcdir@/lustre_compat/lib/ libcfs_dir := $(dir $(lastword $(MAKEFILE_LIST))) + +libcfs-compat-objs := + +COMPAT_MM := @top_srcdir@/lustre_compat/mm/ +include $(libcfs_dir)/../../lustre_compat/mm/Makefile +libcfs-compat-objs += $(patsubst %,$(COMPAT_MM)%,$(mm_objs)) + +COMPAT_LIB := @top_srcdir@/lustre_compat/lib/ include $(libcfs_dir)/../../lustre_compat/lib/Makefile +libcfs-compat-objs += $(patsubst %,$(COMPAT_LIB)%,$(lib_objs)) libcfs-linux-objs := linux-prim.o libcfs-linux-objs += linux-wait.o -libcfs-compat-objs += $(patsubst %,$(COMPAT)%,$(compat_objs)) - EXTRA_DIST = $(libcfs-compat-objs:.o=.c) libcfs-crypto-objs := crypto.o fname.o hkdf.o hooks.o keyring.o diff --git a/libcfs/libcfs/linux/linux-prim.c b/libcfs/libcfs/linux/linux-prim.c index ee2c0ba..d50db3d 100644 --- a/libcfs/libcfs/linux/linux-prim.c +++ b/libcfs/libcfs/linux/linux-prim.c @@ -188,6 +188,8 @@ void __init init_libcfs_vfree_atomic(void) int __init cfs_arch_init(void) { + int rc = 0; + init_libcfs_vfree_atomic(); #ifndef HAVE_WAIT_VAR_EVENT @@ -205,7 +207,26 @@ int __init cfs_arch_init(void) SLAB_PANIC | SLAB_RECLAIM_ACCOUNT, xarray_node_ctor); #endif - return llcrypt_init(); + rc = shrinker_debugfs_init(); + if (rc < 0) + goto free_xcache; + +#ifdef CONFIG_LL_ENCRYPTION + rc = llcrypt_init(); + if (rc < 0) + goto free_shrinker; +#endif + return rc; + +#ifdef CONFIG_LL_ENCRYPTION +free_shrinker: + shrinker_debugfs_fini(); +#endif +free_xcache: +#ifndef HAVE_XARRAY_SUPPORT + kmem_cache_destroy(xarray_cachep); +#endif + return rc; } void __exit cfs_arch_exit(void) @@ -213,7 +234,13 @@ void __exit cfs_arch_exit(void) /* exit_libcfs_vfree_atomic */ __flush_workqueue(system_wq); +#ifndef HAVE_XARRAY_SUPPORT + kmem_cache_destroy(xarray_cachep); +#endif + shrinker_debugfs_fini(); +#ifdef CONFIG_LL_ENCRYPTION llcrypt_exit(); +#endif } int cfs_kernel_write(struct file *filp, const void *buf, size_t count, diff --git a/lustre/include/lustre_compat.h b/lustre/include/lustre_compat.h index 7a98d63..144fa9c 100644 --- a/lustre/include/lustre_compat.h +++ b/lustre/include/lustre_compat.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -657,83 +658,20 @@ static inline ssize_t iov_iter_get_pages_alloc2(struct iov_iter *i, #define migrate_folio migratepage #endif -struct ll_shrinker_ops { -#ifdef HAVE_SHRINKER_COUNT - unsigned long (*count_objects)(struct shrinker *, - struct shrink_control *sc); - unsigned long (*scan_objects)(struct shrinker *, - struct shrink_control *sc); -#else - int (*shrink)(struct shrinker *, struct shrink_control *sc); -#endif - int seeks; /* seeks to recreate an obj */ -}; - -#ifndef HAVE_SHRINKER_ALLOC -static inline void shrinker_free(struct shrinker *shrinker) +static inline const char *shrinker_debugfs_path(struct shrinker *shrinker) { - unregister_shrinker(shrinker); - OBD_FREE_PTR(shrinker); -} -#endif - -/* allocate and register a shrinker, return should be checked with IS_ERR() */ -static inline struct shrinker * -ll_shrinker_create(struct ll_shrinker_ops *ops, unsigned int flags, - const char *fmt, ...) -{ - struct shrinker *shrinker; - int rc = 0; - -#if defined(HAVE_REGISTER_SHRINKER_FORMAT_NAMED) || defined(HAVE_SHRINKER_ALLOC) - struct va_format vaf; - va_list args; -#endif - -#ifdef HAVE_SHRINKER_ALLOC - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - shrinker = shrinker_alloc(flags, "%pV", &vaf); - va_end(args); -#else - OBD_ALLOC_PTR(shrinker); -#endif - if (!shrinker) - return ERR_PTR(-ENOMEM); - -#ifdef HAVE_SHRINKER_COUNT - shrinker->count_objects = ops->count_objects; - shrinker->scan_objects = ops->scan_objects; -#else - shrinker->shrink = ops->shrink; -#endif - shrinker->seeks = ops->seeks; - -#ifdef HAVE_SHRINKER_ALLOC - shrinker_register(shrinker); -#else - #ifdef HAVE_REGISTER_SHRINKER_FORMAT_NAMED - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - rc = register_shrinker(shrinker, "%pV", &vaf); - va_end(args); - #elif defined(HAVE_REGISTER_SHRINKER_RET) - rc = register_shrinker(shrinker); +#ifndef CONFIG_SHRINKER_DEBUG + #ifndef HAVE_SHRINKER_ALLOC + struct ll_shrinker *s = container_of(shrinker, struct ll_shrinker, + ll_shrinker); #else - register_shrinker(shrinker); + struct ll_shrinker *s = shrinker->private_data; #endif -#endif - if (rc) { -#ifdef HAVE_SHRINKER_ALLOC - shrinker_free(shrinker); -#else - OBD_FREE_PTR(shrinker); -#endif - shrinker = ERR_PTR(rc); - } - return shrinker; +#else /* !CONFIG_SHRINKER_DEBUG */ + struct shrinker *s = shrinker; +#endif /* CONFIG_SHRINKER_DEBUG */ + + return s->debugfs_entry->d_name.name; } #ifndef fallthrough diff --git a/lustre/ldlm/ldlm_pool.c b/lustre/ldlm/ldlm_pool.c index 39cdc6f..4cb4ba3 100644 --- a/lustre/ldlm/ldlm_pool.c +++ b/lustre/ldlm/ldlm_pool.c @@ -74,6 +74,7 @@ #define DEBUG_SUBSYSTEM S_LDLM #include +#include #include #include #include @@ -1434,7 +1435,7 @@ int ldlm_pools_init(void) RETURN(0); out_shrinker: - shrinker_free(ldlm_pools_srv_shrinker); + ll_shrinker_free(ldlm_pools_srv_shrinker); out: RETURN(rc); } @@ -1444,8 +1445,8 @@ void ldlm_pools_fini(void) if (ldlm_pools_init_done) { cancel_delayed_work_sync(&ldlm_pools_recalc_work); - shrinker_free(ldlm_pools_srv_shrinker); - shrinker_free(ldlm_pools_cli_shrinker); + ll_shrinker_free(ldlm_pools_srv_shrinker); + ll_shrinker_free(ldlm_pools_cli_shrinker); } ldlm_pools_init_done = false; diff --git a/lustre/obdclass/lu_object.c b/lustre/obdclass/lu_object.c index b4cf710..032711d 100644 --- a/lustre/obdclass/lu_object.c +++ b/lustre/obdclass/lu_object.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -2167,7 +2168,7 @@ int lu_global_init(void) return result; out_shrinker: - shrinker_free(lu_site_shrinker); + ll_shrinker_free(lu_site_shrinker); out_env: /* ordering here is explained in lu_global_fini() */ lu_context_key_degister(&lu_global_key); @@ -2181,7 +2182,7 @@ out: /* Dual to lu_global_init(). */ void lu_global_fini(void) { - shrinker_free(lu_site_shrinker); + ll_shrinker_free(lu_site_shrinker); lu_context_key_degister(&lu_global_key); diff --git a/lustre/obdclass/page_pools.c b/lustre/obdclass/page_pools.c index 6be8b7e..edc4b0b 100644 --- a/lustre/obdclass/page_pools.c +++ b/lustre/obdclass/page_pools.c @@ -15,6 +15,7 @@ #define DEBUG_SUBSYSTEM S_SEC +#include #include #include @@ -1145,7 +1146,7 @@ void obd_pool_fini(void) for (pool_order = 0; pool_order < pools_count; pool_order++) { pool = page_pools[pool_order]; - shrinker_free(pool->pool_shrinker); + ll_shrinker_free(pool->pool_shrinker); LASSERT(pool->opp_ptr_pages); LASSERT(pool->opp_total_objects == pool->opp_free_objects); diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c index c5a1daf..736cc7c 100644 --- a/lustre/osc/osc_request.c +++ b/lustre/osc/osc_request.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -4181,7 +4182,7 @@ out_stop_grant: out_req_pool: ptlrpc_free_rq_pool(osc_rq_pool); out_shrinker: - shrinker_free(osc_cache_shrinker); + ll_shrinker_free(osc_cache_shrinker); out_kmem: lu_kmem_fini(osc_caches); @@ -4193,7 +4194,7 @@ static void __exit osc_exit(void) class_unregister_type(LUSTRE_OSC_NAME); ptlrpc_free_rq_pool(osc_rq_pool); osc_stop_grant_work(); - shrinker_free(osc_cache_shrinker); + ll_shrinker_free(osc_cache_shrinker); lu_kmem_fini(osc_caches); } diff --git a/lustre_compat/lib/Makefile b/lustre_compat/lib/Makefile index 6a36e8b..3be7ee0 100644 --- a/lustre_compat/lib/Makefile +++ b/lustre_compat/lib/Makefile @@ -4,4 +4,4 @@ # This file is part of Lustre, http://www.lustre.org/ # -compat_objs := xarray.o glob.o generic-radix-tree.o +lib_objs := xarray.o glob.o generic-radix-tree.o diff --git a/lustre_compat/mm/Makefile b/lustre_compat/mm/Makefile new file mode 100644 index 0000000..36451bf --- /dev/null +++ b/lustre_compat/mm/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +# +# This file is part of Lustre, http://www.lustre.org/ +# + +mm_objs := shrinker_debug.o diff --git a/lustre_compat/mm/shrinker_debug.c b/lustre_compat/mm/shrinker_debug.c new file mode 100644 index 0000000..55d2ea3 --- /dev/null +++ b/lustre_compat/mm/shrinker_debug.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* This is taken from kernel commit: + * + * 8a0e8bb11 ("mm: shrinker: convert shrinker_rwsem to mutex") + * + * at kernel verison 6.6-rc4 + */ +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_SHRINKER_DEBUG +/* RHEL7 is sooooo old and we really don't support it */ +#ifdef HAVE_SHRINKER_COUNT +static DEFINE_IDA(shrinker_debugfs_ida); +#else +static int seq; +#endif +static struct dentry *shrinker_debugfs_root; + +#ifndef SHRINK_EMPTY +#define SHRINK_EMPTY (~0UL - 1) +#endif + +static unsigned long shrinker_count_objects(struct shrinker *shrinker, + struct mem_cgroup *memcg, + unsigned long *count_per_node) +{ + unsigned long nr, total = 0; + int node_id; + + for_each_node(node_id) { +#ifdef HAVE_SHRINKER_COUNT + if (node_id == 0 || (shrinker->flags & SHRINKER_NUMA_AWARE)) { + struct shrink_control sc = { + .gfp_mask = GFP_KERNEL, + .nid = node_id, + .memcg = memcg, + }; + + nr = shrinker->count_objects(shrinker, &sc); +#else + if (node_id == 0) { + struct shrink_control sc = { + .gfp_mask = GFP_KERNEL, + }; + + nr = shrinker->shrink(shrinker, &sc); +#endif + if (nr == SHRINK_EMPTY) + nr = 0; + } else { + nr = 0; + } + + count_per_node[node_id] = nr; + total += nr; + } + + return total; +} + +static int shrinker_debugfs_count_show(struct seq_file *m, void *v) +{ + struct shrinker *shrinker = m->private; + unsigned long *count_per_node; + unsigned long total; + int node_id; + + count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), + GFP_KERNEL); + if (!count_per_node) + return -ENOMEM; + + rcu_read_lock(); + + /* Lustre shrinker's don't support memcg aware shrinkers so + * we simplify this code for older platforms. Sadly newer + * kernels don't export the memcg functions we need so even + * for the latest kernels we can't support memcg. + */ + total = shrinker_count_objects(shrinker, NULL, count_per_node); + if (total) { + /* Lustre doesn't support memcg aware shrinkers + * so just print 0 + */ + seq_putc(m, '0'); + for_each_node(node_id) + seq_printf(m, " %lu", count_per_node[node_id]); + seq_putc(m, '\n'); + } + + rcu_read_unlock(); + + kfree(count_per_node); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count); + +static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return nonseekable_open(inode, file); +} + +static ssize_t shrinker_debugfs_scan_write(struct file *file, + const char __user *buf, + size_t size, loff_t *pos) +{ + struct shrinker *shrinker = file->private_data; + unsigned long nr_to_scan = 0, ino, read_len; + struct shrink_control sc = { + .gfp_mask = GFP_KERNEL, + }; + char kbuf[72]; + int node_id; + + read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1); + if (copy_from_user(kbuf, buf, read_len)) + return -EFAULT; + kbuf[read_len] = '\0'; + + if (sscanf(kbuf, "%lu %d %lu", &ino, &node_id, &nr_to_scan) != 3) + return -EINVAL; + + if (node_id < 0 || node_id >= nr_node_ids) + return -EINVAL; + + if (nr_to_scan == 0) + return size; + + /* Lustre doesn't support memcg aware shrinkers */ + if (ino != 0) + return -EINVAL; + + sc.nr_to_scan = nr_to_scan; +#ifdef HAVE_SHRINKER_COUNT + sc.nr_scanned = nr_to_scan; + sc.nid = node_id; + sc.memcg = NULL; + + shrinker->scan_objects(shrinker, &sc); +#else + shrinker->shrink(shrinker, &sc); +#endif + return size; +} + +static const struct file_operations shrinker_debugfs_scan_fops = { + .owner = THIS_MODULE, + .open = shrinker_debugfs_scan_open, + .write = shrinker_debugfs_scan_write, +}; + +static int shrinker_add_debugfs(struct shrinker *shrinker) +{ +#ifndef HAVE_SHRINKER_ALLOC + struct ll_shrinker *s = container_of(shrinker, struct ll_shrinker, + ll_shrinker); +#else + struct ll_shrinker *s = shrinker->private_data; +#endif + struct dentry *entry; + char buf[128]; + int id; + + /* debugfs isn't initialized yet, add debugfs entries later. */ + if (!shrinker_debugfs_root) + return 0; + +#ifdef HAVE_SHRINKER_COUNT + id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL); + if (id < 0) + return id; +#else + id = seq++; +#endif + s->debugfs_id = id; + + snprintf(buf, sizeof(buf), "%s-%d", s->name, id); + + /* create debugfs entry */ + entry = debugfs_create_dir(buf, shrinker_debugfs_root); + if (IS_ERR(entry)) { +#ifdef HAVE_SHRINKER_COUNT + ida_free(&shrinker_debugfs_ida, id); +#else + seq--; +#endif + return PTR_ERR(entry); + } + s->debugfs_entry = entry; + + debugfs_create_file("count", 0440, entry, shrinker, + &shrinker_debugfs_count_fops); + debugfs_create_file("scan", 0220, entry, shrinker, + &shrinker_debugfs_scan_fops); + return 0; +} +#endif /* !CONFIG_SHRINKER_DEBUG */ + +void ll_shrinker_free(struct shrinker *shrinker) +{ +#ifndef CONFIG_SHRINKER_DEBUG +#ifndef HAVE_SHRINKER_ALLOC + struct ll_shrinker *s = container_of(shrinker, struct ll_shrinker, + ll_shrinker); +#else + struct ll_shrinker *s = shrinker->private_data; +#endif /* HAVE_SHRINKER_ALLOC */ + +#ifdef HAVE_SHRINKER_COUNT + if (s->debugfs_entry) + ida_free(&shrinker_debugfs_ida, s->debugfs_id); +#endif + debugfs_remove_recursive(s->debugfs_entry); + kfree(s->name); +#endif /* !CONFIG_SHRINKER_DEBUG */ + +#ifdef HAVE_SHRINKER_ALLOC + shrinker_free(shrinker); +#else /* !HAVE_SHRINKER_ALLOC */ + unregister_shrinker(shrinker); +#endif /* !HAVE_SHRINKER_ALLOC */ + +#ifndef CONFIG_SHRINKER_DEBUG + LIBCFS_FREE_PRE(s, sizeof(*s), "kfreed"); + kfree(s); +#endif +} +EXPORT_SYMBOL(ll_shrinker_free); + +/* allocate and register a shrinker, return should be checked with IS_ERR() */ +struct shrinker *ll_shrinker_create(struct ll_shrinker_ops *ops, + unsigned int flags, + const char *fmt, ...) +{ + struct shrinker *shrinker; +#ifndef CONFIG_SHRINKER_DEBUG + struct ll_shrinker *s; +#endif +#if defined(HAVE_REGISTER_SHRINKER_FORMAT_NAMED) || defined(HAVE_SHRINKER_ALLOC) + struct va_format vaf; +#endif + va_list args; + int rc = 0; + + va_start(args, fmt); +#ifdef HAVE_SHRINKER_ALLOC + vaf.fmt = fmt; + vaf.va = &args; + shrinker = shrinker_alloc(flags, "%pV", &vaf); + va_end(args); + #ifndef CONFIG_SHRINKER_DEBUG + LIBCFS_ALLOC(s, sizeof(*s)); + if (!s) { + shrinker_free(shrinker); + shrinker = NULL; + } else { + s->name = kvasprintf_const(GFP_KERNEL, fmt, args); + shrinker->private_data = s; + } + #endif +#else /* !HAVE_SHRINKER_ALLOC */ + LIBCFS_ALLOC(s, sizeof(*s)); + #if !defined(CONFIG_SHRINKER_DEBUG) + if (s) + s->name = kvasprintf_const(GFP_KERNEL, fmt, args); + #endif + va_end(args); + shrinker = (struct shrinker *)s; +#endif + if (!shrinker) + return ERR_PTR(-ENOMEM); + +#ifdef HAVE_SHRINKER_COUNT + shrinker->count_objects = ops->count_objects; + shrinker->scan_objects = ops->scan_objects; +#else + shrinker->shrink = ops->shrink; +#endif + shrinker->seeks = ops->seeks; + +#ifdef HAVE_SHRINKER_ALLOC + shrinker_register(shrinker); +#else + #ifdef HAVE_REGISTER_SHRINKER_FORMAT_NAMED + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + rc = register_shrinker(shrinker, "%pV", &vaf); + va_end(args); + #else + if (rc == 0) { + #if defined(HAVE_REGISTER_SHRINKER_RET) + rc = register_shrinker(shrinker); + #else + register_shrinker(shrinker); + #endif + } + #endif +#endif + #ifndef CONFIG_SHRINKER_DEBUG + if (rc == 0 && s->name && + strncmp(s->name, "ldlm_pools", strlen("ldlm_pools")) != 0) + rc = shrinker_add_debugfs(shrinker); + #endif + if (rc) { + ll_shrinker_free(shrinker); + shrinker = ERR_PTR(rc); + } + return shrinker; +} +EXPORT_SYMBOL(ll_shrinker_create); + +#ifndef CONFIG_SHRINKER_DEBUG +void shrinker_debugfs_fini(void) +{ + debugfs_remove_recursive(shrinker_debugfs_root); +} + +int __init shrinker_debugfs_init(void) +{ + struct dentry *dentry; + int ret = 0; + + dentry = debugfs_create_dir("shrinker", NULL); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + shrinker_debugfs_root = dentry; + + return ret; +} +#endif /* CONFIG_SHRINKER_DEBUG */ -- 1.8.3.1