From 35d43ba8eadafdefd76306ce59fb72946c2ea845 Mon Sep 17 00:00:00 2001 From: Low-power Date: Sat, 11 Mar 2023 01:35:00 +0800 Subject: [PATCH] Workaround for Linux PowerPC GPL-only cpu_has_feature() Linux since 4.7 makes interface 'cpu_has_feature' to use jump labels on powerpc if CONFIG_JUMP_LABEL_FEATURE_CHECKS is enabled, in this case however the inline function references GPL-only symbol 'cpu_feature_keys'. ZFS currently uses 'cpu_has_feature' either directly or indirectly from several places; while it is unknown how this issue didn't break ZFS on 64-bit little-endian powerpc, it is known to break ZFS with many Linux versions on both 32-bit and 64-bit big-endian powerpc. Until this issue is fixed in Linux, we have to workaround it by overriding affected inline functions without depending on 'cpu_feature_keys'. Reviewed-by: Brian Behlendorf Signed-off-by: WHR Closes #14590 --- config/kernel-cpu_has_feature.m4 | 29 +++++++++++++++++++ config/kernel-flush_dcache_page.m4 | 26 +++++++++++++++++ config/kernel.m4 | 12 ++++++++ include/os/linux/kernel/linux/dcache_compat.h | 15 ++++++++++ include/os/linux/kernel/linux/simd_powerpc.h | 11 +++++++ 5 files changed, 93 insertions(+) create mode 100644 config/kernel-cpu_has_feature.m4 create mode 100644 config/kernel-flush_dcache_page.m4 diff --git a/config/kernel-cpu_has_feature.m4 b/config/kernel-cpu_has_feature.m4 new file mode 100644 index 0000000000..608faf0f89 --- /dev/null +++ b/config/kernel-cpu_has_feature.m4 @@ -0,0 +1,29 @@ +dnl # +dnl # cpu_has_feature() may referencing GPL-only cpu_feature_keys on powerpc +dnl # + +dnl # +dnl # Checking if cpu_has_feature is exported GPL-only +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_CPU_HAS_FEATURE], [ + ZFS_LINUX_TEST_SRC([cpu_has_feature], [ + #include + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + #include + #else + #include + #endif + ], [ + return cpu_has_feature(CPU_FTR_ALTIVEC) ? 0 : 1; + ], [], [ZFS_META_LICENSE]) +]) +AC_DEFUN([ZFS_AC_KERNEL_CPU_HAS_FEATURE], [ + AC_MSG_CHECKING([whether cpu_has_feature() is GPL-only]) + ZFS_LINUX_TEST_RESULT([cpu_has_feature_license], [ + AC_MSG_RESULT(no) + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_CPU_HAS_FEATURE_GPL_ONLY, 1, + [cpu_has_feature() is GPL-only]) + ]) +]) diff --git a/config/kernel-flush_dcache_page.m4 b/config/kernel-flush_dcache_page.m4 new file mode 100644 index 0000000000..2340c386ef --- /dev/null +++ b/config/kernel-flush_dcache_page.m4 @@ -0,0 +1,26 @@ +dnl # +dnl # Starting from Linux 5.13, flush_dcache_page() becomes an inline +dnl # function and may indirectly referencing GPL-only cpu_feature_keys on +dnl # powerpc +dnl # + +dnl # +dnl # Checking if flush_dcache_page is exported GPL-only +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_FLUSH_DCACHE_PAGE], [ + ZFS_LINUX_TEST_SRC([flush_dcache_page], [ + #include + ], [ + flush_dcache_page(0); + ], [], [ZFS_META_LICENSE]) +]) +AC_DEFUN([ZFS_AC_KERNEL_FLUSH_DCACHE_PAGE], [ + AC_MSG_CHECKING([whether flush_dcache_page() is GPL-only]) + ZFS_LINUX_TEST_RESULT([flush_dcache_page_license], [ + AC_MSG_RESULT(no) + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_FLUSH_DCACHE_PAGE_GPL_ONLY, 1, + [flush_dcache_page() is GPL-only]) + ]) +]) diff --git a/config/kernel.m4 b/config/kernel.m4 index 32689cb945..65cfe1edbe 100644 --- a/config/kernel.m4 +++ b/config/kernel.m4 @@ -145,6 +145,12 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [ ZFS_AC_KERNEL_SRC_ZERO_PAGE ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC ZFS_AC_KERNEL_SRC_FILEMAP + case "$host_cpu" in + powerpc*) + ZFS_AC_KERNEL_SRC_CPU_HAS_FEATURE + ZFS_AC_KERNEL_SRC_FLUSH_DCACHE_PAGE + ;; + esac AC_MSG_CHECKING([for available kernel interfaces]) ZFS_LINUX_TEST_COMPILE_ALL([kabi]) @@ -263,6 +269,12 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [ ZFS_AC_KERNEL_ZERO_PAGE ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC ZFS_AC_KERNEL_FILEMAP + case "$host_cpu" in + powerpc*) + ZFS_AC_KERNEL_CPU_HAS_FEATURE + ZFS_AC_KERNEL_FLUSH_DCACHE_PAGE + ;; + esac ]) dnl # diff --git a/include/os/linux/kernel/linux/dcache_compat.h b/include/os/linux/kernel/linux/dcache_compat.h index c90135fd38..f87f1653ab 100644 --- a/include/os/linux/kernel/linux/dcache_compat.h +++ b/include/os/linux/kernel/linux/dcache_compat.h @@ -39,6 +39,21 @@ #define d_alias d_u.d_alias #endif +/* + * Starting from Linux 5.13, flush_dcache_page() becomes an inline function + * and under some configurations, may indirectly referencing GPL-only + * cpu_feature_keys on powerpc. Override this function when it is detected + * being GPL-only. + */ +#if defined __powerpc__ && defined HAVE_FLUSH_DCACHE_PAGE_GPL_ONLY +#include +#define flush_dcache_page(page) do { \ + if (!cpu_has_feature(CPU_FTR_COHERENT_ICACHE) && \ + test_bit(PG_dcache_clean, &(page)->flags)) \ + clear_bit(PG_dcache_clean, &(page)->flags); \ + } while (0) +#endif + /* * 2.6.30 API change, * The const keyword was added to the 'struct dentry_operations' in diff --git a/include/os/linux/kernel/linux/simd_powerpc.h b/include/os/linux/kernel/linux/simd_powerpc.h index 108cef22f5..422b85af3a 100644 --- a/include/os/linux/kernel/linux/simd_powerpc.h +++ b/include/os/linux/kernel/linux/simd_powerpc.h @@ -76,6 +76,17 @@ #define kfpu_init() 0 #define kfpu_fini() ((void) 0) +/* + * Linux 4.7 makes cpu_has_feature to use jump labels on powerpc if + * CONFIG_JUMP_LABEL_FEATURE_CHECKS is enabled, in this case however it + * references GPL-only symbol cpu_feature_keys. Therefore we overrides this + * interface when it is detected being GPL-only. + */ +#if defined(CONFIG_JUMP_LABEL_FEATURE_CHECKS) && \ + defined(HAVE_CPU_HAS_FEATURE_GPL_ONLY) +#define cpu_has_feature(feature) early_cpu_has_feature(feature) +#endif + /* * Check if AltiVec instruction set is available */