diff --git a/config/spl-build.m4 b/config/spl-build.m4 index 72bb3258e..94c7f3e74 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -76,6 +76,8 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_3ARGS_FILE_FSYNC SPL_AC_EXPORTED_RWSEM_IS_LOCKED SPL_AC_KERNEL_INVALIDATE_INODES + SPL_AC_SHRINK_DCACHE_MEMORY + SPL_AC_SHRINK_ICACHE_MEMORY ]) AC_DEFUN([SPL_AC_MODULE_SYMVERS], [ @@ -1717,3 +1719,33 @@ AC_DEFUN([SPL_AC_KERNEL_INVALIDATE_INODES], [ [invalidate_inodes() is available])], []) ]) + +dnl # +dnl # 2.6.xx API compat, +dnl # There currently exists no exposed API to partially shrink the dcache. +dnl # The expected mechanism to shrink the cache is a registered shrinker +dnl # which is called during memory pressure. +dnl # +AC_DEFUN([SPL_AC_SHRINK_DCACHE_MEMORY], [ + SPL_CHECK_SYMBOL_EXPORT( + [shrink_dcache_memory], + [fs/dcache.c], + [AC_DEFINE(HAVE_SHRINK_DCACHE_MEMORY, 1, + [shrink_dcache_memory() is available])], + []) +]) + +dnl # +dnl # 2.6.xx API compat, +dnl # There currently exists no exposed API to partially shrink the icache. +dnl # The expected mechanism to shrink the cache is a registered shrinker +dnl # which is called during memory pressure. +dnl # +AC_DEFUN([SPL_AC_SHRINK_ICACHE_MEMORY], [ + SPL_CHECK_SYMBOL_EXPORT( + [shrink_icache_memory], + [fs/inode.c], + [AC_DEFINE(HAVE_SHRINK_ICACHE_MEMORY, 1, + [shrink_icache_memory() is available])], + []) +]) diff --git a/configure b/configure index f5c6a063a..317ab8ea7 100755 --- a/configure +++ b/configure @@ -15462,6 +15462,88 @@ _ACEOF fi + + { $as_echo "$as_me:$LINENO: checking whether symbol shrink_dcache_memory is exported" >&5 +$as_echo_n "checking whether symbol shrink_dcache_memory is exported... " >&6; } + grep -q -E '[[:space:]]shrink_dcache_memory[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in fs/dcache.c; do + grep -q -E "EXPORT_SYMBOL.*(shrink_dcache_memory)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_DCACHE_MEMORY 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_DCACHE_MEMORY 1 +_ACEOF + + fi + + + + { $as_echo "$as_me:$LINENO: checking whether symbol shrink_icache_memory is exported" >&5 +$as_echo_n "checking whether symbol shrink_icache_memory is exported... " >&6; } + grep -q -E '[[:space:]]shrink_icache_memory[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in fs/inode.c; do + grep -q -E "EXPORT_SYMBOL.*(shrink_icache_memory)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_ICACHE_MEMORY 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_ICACHE_MEMORY 1 +_ACEOF + + fi + + ;; user) @@ -19285,6 +19367,88 @@ _ACEOF + { $as_echo "$as_me:$LINENO: checking whether symbol shrink_dcache_memory is exported" >&5 +$as_echo_n "checking whether symbol shrink_dcache_memory is exported... " >&6; } + grep -q -E '[[:space:]]shrink_dcache_memory[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in fs/dcache.c; do + grep -q -E "EXPORT_SYMBOL.*(shrink_dcache_memory)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_DCACHE_MEMORY 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_DCACHE_MEMORY 1 +_ACEOF + + fi + + + + { $as_echo "$as_me:$LINENO: checking whether symbol shrink_icache_memory is exported" >&5 +$as_echo_n "checking whether symbol shrink_icache_memory is exported... " >&6; } + grep -q -E '[[:space:]]shrink_icache_memory[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in fs/inode.c; do + grep -q -E "EXPORT_SYMBOL.*(shrink_icache_memory)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_ICACHE_MEMORY 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_SHRINK_ICACHE_MEMORY 1 +_ACEOF + + fi + + + if test "x$AWK" != xgawk; then diff --git a/include/linux/mm_compat.h b/include/linux/mm_compat.h index 7e2f6b8a5..522db827f 100644 --- a/include/linux/mm_compat.h +++ b/include/linux/mm_compat.h @@ -56,6 +56,42 @@ extern invalidate_inodes_t invalidate_inodes_fn; #define invalidate_inodes(sb) invalidate_inodes_fn(sb) #endif /* HAVE_INVALIDATE_INODES */ +/* + * 2.6.xx API compat, + * There currently exists no exposed API to partially shrink the dcache. + * The expected mechanism to shrink the cache is a registered shrinker + * which is called during memory pressure. + */ +#ifndef HAVE_SHRINK_DCACHE_MEMORY +# ifdef HAVE_3ARGS_SHRINKER_CALLBACK +typedef int (*shrink_dcache_memory_t)(struct shrinker *, int, gfp_t); +extern shrink_dcache_memory_t shrink_dcache_memory_fn; +# define shrink_dcache_memory(nr, gfp) shrink_dcache_memory_fn(NULL, nr, gfp) +# else +typedef int (*shrink_dcache_memory_t)(int, gfp_t); +extern shrink_dcache_memory_t shrink_dcache_memory_fn; +# define shrink_dcache_memory(nr, gfp) shrink_dcache_memory_fn(nr, gfp) +# endif /* HAVE_3ARGS_SHRINKER_CALLBACK */ +#endif /* HAVE_SHRINK_DCACHE_MEMORY */ + +/* + * 2.6.xx API compat, + * There currently exists no exposed API to partially shrink the icache. + * The expected mechanism to shrink the cache is a registered shrinker + * which is called during memory pressure. + */ +#ifndef HAVE_SHRINK_ICACHE_MEMORY +# ifdef HAVE_3ARGS_SHRINKER_CALLBACK +typedef int (*shrink_icache_memory_t)(struct shrinker *, int, gfp_t); +extern shrink_icache_memory_t shrink_icache_memory_fn; +# define shrink_icache_memory(nr, gfp) shrink_icache_memory_fn(NULL, nr, gfp) +# else +typedef int (*shrink_icache_memory_t)(int, gfp_t); +extern shrink_icache_memory_t shrink_icache_memory_fn; +# define shrink_icache_memory(nr, gfp) shrink_icache_memory_fn(nr, gfp) +# endif /* HAVE_3ARGS_SHRINKER_CALLBACK */ +#endif /* HAVE_SHRINK_ICACHE_MEMORY */ + #ifdef HAVE_SET_SHRINKER typedef struct spl_shrinker { struct shrinker *shrinker; diff --git a/include/sys/dnlc.h b/include/sys/dnlc.h index 693e3d294..b63c94fef 100644 --- a/include/sys/dnlc.h +++ b/include/sys/dnlc.h @@ -25,6 +25,22 @@ #ifndef _SPL_DNLC_H #define _SPL_DNLC_H -#define dnlc_reduce_cache(percent) ((void)0) +/* + * Reduce the dcache and icache then reap the free'd slabs. Note the + * interface takes a reclaim percentage but we don't have easy access to + * the total number of entries to calculate the reclaim count. However, + * in practice this doesn't need to be even close to correct. We simply + * need to reclaim some useful fraction of the cache. The caller can + * determine if more needs to be done. + */ +static inline void +dnlc_reduce_cache(void *reduce_percent) +{ + int nr = (uintptr_t)reduce_percent * 10000; + + shrink_dcache_memory(nr, GFP_KERNEL); + shrink_icache_memory(nr, GFP_KERNEL); + kmem_reap(); +} #endif /* SPL_DNLC_H */ diff --git a/module/spl/spl-kmem.c b/module/spl/spl-kmem.c index 87594bde9..d96456cbc 100644 --- a/module/spl/spl-kmem.c +++ b/module/spl/spl-kmem.c @@ -185,6 +185,16 @@ invalidate_inodes_t invalidate_inodes_fn = SYMBOL_POISON; EXPORT_SYMBOL(invalidate_inodes_fn); #endif /* HAVE_INVALIDATE_INODES */ +#ifndef HAVE_SHRINK_DCACHE_MEMORY +shrink_dcache_memory_t shrink_dcache_memory_fn = SYMBOL_POISON; +EXPORT_SYMBOL(shrink_dcache_memory_fn); +#endif /* HAVE_SHRINK_DCACHE_MEMORY */ + +#ifndef HAVE_SHRINK_ICACHE_MEMORY +shrink_icache_memory_t shrink_icache_memory_fn = SYMBOL_POISON; +EXPORT_SYMBOL(shrink_icache_memory_fn); +#endif /* HAVE_SHRINK_ICACHE_MEMORY */ + pgcnt_t spl_kmem_availrmem(void) { @@ -2102,6 +2112,24 @@ spl_kmem_init_kallsyms_lookup(void) } #endif /* HAVE_INVALIDATE_INODES */ +#ifndef HAVE_SHRINK_DCACHE_MEMORY + shrink_dcache_memory_fn = (shrink_dcache_memory_t) + spl_kallsyms_lookup_name("shrink_dcache_memory"); + if (!shrink_dcache_memory_fn) { + printk(KERN_ERR "Error: Unknown symbol shrink_dcache_memory\n"); + return -EFAULT; + } +#endif /* HAVE_SHRINK_DCACHE_MEMORY */ + +#ifndef HAVE_SHRINK_ICACHE_MEMORY + shrink_icache_memory_fn = (shrink_icache_memory_t) + spl_kallsyms_lookup_name("shrink_icache_memory"); + if (!shrink_icache_memory_fn) { + printk(KERN_ERR "Error: Unknown symbol shrink_icache_memory\n"); + return -EFAULT; + } +#endif /* HAVE_SHRINK_ICACHE_MEMORY */ + return 0; } diff --git a/spl_config.h.in b/spl_config.h.in index a58784119..284b9b1e9 100644 --- a/spl_config.h.in +++ b/spl_config.h.in @@ -156,6 +156,12 @@ /* set_shrinker() available */ #undef HAVE_SET_SHRINKER +/* shrink_dcache_memory() is available */ +#undef HAVE_SHRINK_DCACHE_MEMORY + +/* shrink_icache_memory() is available */ +#undef HAVE_SHRINK_ICACHE_MEMORY + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H