From 36811b4430aaea8c8b91bbe7d812a26799865499 Mon Sep 17 00:00:00 2001 From: Richard Yao Date: Wed, 6 Jun 2012 22:38:12 -0400 Subject: [PATCH] Detect kernels that honor gfp flags passed to vmalloc() zfsonlinux/spl@2092cf68d89a51eb0d6193aeadabb579dfc4b4a0 used PF_MEMALLOC to workaround a bug in the Linux kernel where allocations did not honor the gfp flags passed to vmalloc(). Unfortunately, PF_MEMALLOC has the side effect of permitting allocations to allocate pages outside of ZONE_NORMAL. This has been observed to result in the depletion of ZONE_DMA32. A kernel patch is available in the Gentoo bug tracker for this issue. https://bugs.gentoo.org/show_bug.cgi?id=416685 This negates any benefit PF_MEMALLOC provides, so we introduce an autotools check to disable the use of PF_MEMALLOC on systems with patched kernels. Signed-off-by: Richard Yao Signed-off-by: Brian Behlendorf Closes #126 --- config/spl-build.m4 | 36 ++++++++++ configure | 156 ++++++++++++++++++++++++++++++++++++++++++ module/spl/spl-kmem.c | 4 ++ spl_config.h.in | 3 + 4 files changed, 199 insertions(+) diff --git a/config/spl-build.m4 b/config/spl-build.m4 index 4d02a72e..c28ad0bf 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -88,6 +88,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_2ARGS_ZLIB_DEFLATE_WORKSPACESIZE SPL_AC_SHRINK_CONTROL_STRUCT SPL_AC_RWSEM_SPINLOCK_IS_RAW + SPL_AC_PMD_ALLOC_WITH_MASK ]) AC_DEFUN([SPL_AC_MODULE_SYMVERS], [ @@ -2079,3 +2080,38 @@ AC_DEFUN([SPL_AC_RWSEM_SPINLOCK_IS_RAW], [ ]) EXTRA_KCFLAGS="$tmp_flags" ]) + +dnl # +dnl # Proposed VM Subsystem Bug Fix +dnl # https://bugs.gentoo.org/show_bug.cgi?id=416685 +dnl # +dnl # Make __pte_alloc_kernel() honor gfp flags passed to vmalloc() +dnl # This is detected by checking a macro that is changed to support this. +dnl # +AC_DEFUN([SPL_AC_PMD_ALLOC_WITH_MASK], [ + AC_MSG_CHECKING([whether pmd_alloc_with_mask exists]) + SPL_LINUX_TRY_COMPILE([ + #if !defined(CONFIG_MMU) + #define CONFIG_MMU + #endif + + #if defined(RCH_HAS_4LEVEL_HACK) + #undef RCH_HAS_4LEVEL_HACK + #endif + + #include + ],[ + struct mm_struct init_mm; + pud_t *pud = NULL; + unsigned long addr = 0; + gfp_t gfp_mask = GFP_KERNEL; + + pmd_alloc_with_mask(&init_mm, pud, addr, gfp_mask); + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_PMD_ALLOC_WITH_MASK, 1, + [pmd_alloc_with_mask exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) diff --git a/configure b/configure index 761fdc1c..2a502798 100755 --- a/configure +++ b/configure @@ -16231,6 +16231,84 @@ fi EXTRA_KCFLAGS="$tmp_flags" + + { $as_echo "$as_me:$LINENO: checking whether pmd_alloc_with_mask exists" >&5 +$as_echo_n "checking whether pmd_alloc_with_mask exists... " >&6; } + + +cat confdefs.h - <<_ACEOF >conftest.c +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + + #if !defined(CONFIG_MMU) + #define CONFIG_MMU + #endif + + #if defined(RCH_HAS_4LEVEL_HACK) + #undef RCH_HAS_4LEVEL_HACK + #endif + + #include + +int +main (void) +{ + + struct mm_struct init_mm; + pud_t *pud = NULL; + unsigned long addr = 0; + gfp_t gfp_mask = GFP_KERNEL; + + pmd_alloc_with_mask(&init_mm, pud, addr, gfp_mask); + + ; + return 0; +} + +_ACEOF + + + rm -Rf build && mkdir -p build + echo "obj-m := conftest.o" >build/Makefile + if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PMD_ALLOC_WITH_MASK 1 +_ACEOF + + +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + + +fi + + rm -Rf build + + + ;; user) ;; all) @@ -20730,6 +20808,84 @@ fi EXTRA_KCFLAGS="$tmp_flags" + { $as_echo "$as_me:$LINENO: checking whether pmd_alloc_with_mask exists" >&5 +$as_echo_n "checking whether pmd_alloc_with_mask exists... " >&6; } + + +cat confdefs.h - <<_ACEOF >conftest.c +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + + #if !defined(CONFIG_MMU) + #define CONFIG_MMU + #endif + + #if defined(RCH_HAS_4LEVEL_HACK) + #undef RCH_HAS_4LEVEL_HACK + #endif + + #include + +int +main (void) +{ + + struct mm_struct init_mm; + pud_t *pud = NULL; + unsigned long addr = 0; + gfp_t gfp_mask = GFP_KERNEL; + + pmd_alloc_with_mask(&init_mm, pud, addr, gfp_mask); + + ; + return 0; +} + +_ACEOF + + + rm -Rf build && mkdir -p build + echo "obj-m := conftest.o" >build/Makefile + if { ac_try='cp conftest.c build && make modules -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } >/dev/null && { ac_try='test -s build/conftest.o' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define HAVE_PMD_ALLOC_WITH_MASK 1 +_ACEOF + + +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + + +fi + + rm -Rf build + + + + ;; srpm) ;; *) diff --git a/module/spl/spl-kmem.c b/module/spl/spl-kmem.c index a6d09f9a..80c4ff4b 100644 --- a/module/spl/spl-kmem.c +++ b/module/spl/spl-kmem.c @@ -843,6 +843,9 @@ kv_alloc(spl_kmem_cache_t *skc, int size, int flags) if (skc->skc_flags & KMC_KMEM) { ptr = (void *)__get_free_pages(flags, get_order(size)); } else { +#ifdef HAVE_PMD_ALLOC_WITH_MASK + ptr = __vmalloc(size, flags|__GFP_HIGHMEM, PAGE_KERNEL); +#else /* * As part of vmalloc() an __pte_alloc_kernel() allocation * may occur. This internal allocation does not honor the @@ -866,6 +869,7 @@ kv_alloc(spl_kmem_cache_t *skc, int size, int flags) } else { ptr = __vmalloc(size, flags|__GFP_HIGHMEM, PAGE_KERNEL); } +#endif } /* Resulting allocated memory will be page aligned */ diff --git a/spl_config.h.in b/spl_config.h.in index 92c11df2..1b4a129b 100644 --- a/spl_config.h.in +++ b/spl_config.h.in @@ -165,6 +165,9 @@ /* pgdat_list is available */ #undef HAVE_PGDAT_LIST +/* pmd_alloc_with_mask exists */ +#undef HAVE_PMD_ALLOC_WITH_MASK + /* __put_task_struct() is available */ #undef HAVE_PUT_TASK_STRUCT