From e22bfd814960295029ca41c8e116e8d516d3e730 Mon Sep 17 00:00:00 2001
From: Tony Hutter <hutter2@llnl.gov>
Date: Fri, 11 Jan 2019 18:01:28 -0800
Subject: [PATCH] Linux 5.0 compat: Disable vector instructions on 5.0+ kernels

The 5.0 kernel no longer exports the functions we need to do vector
(SSE/SSE2/SSE3/AVX...) instructions.  Disable vector-based checksum
algorithms when building against those kernels.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tony Hutter <hutter2@llnl.gov>
Closes #8259
---
 config/kernel-fpu.m4     |  41 ++++++++++---
 include/linux/simd_x86.h | 127 +++++++++++++++++++++++++++++++--------
 2 files changed, 134 insertions(+), 34 deletions(-)

diff --git a/config/kernel-fpu.m4 b/config/kernel-fpu.m4
index 1c5690969d4..671fe7ea54e 100644
--- a/config/kernel-fpu.m4
+++ b/config/kernel-fpu.m4
@@ -1,18 +1,41 @@
+dnl # 
+dnl # Handle differences in kernel FPU code.
 dnl #
-dnl # 4.2 API change
-dnl # asm/i387.h is replaced by asm/fpu/api.h
+dnl # Kernel
+dnl # 5.0:	All kernel fpu functions are GPL only, so we can't use them.
+dnl #		(nothing defined)
+dnl #
+dnl # 4.2:	Use __kernel_fpu_{begin,end}()
+dnl #		HAVE_UNDERSCORE_KERNEL_FPU & KERNEL_EXPORTS_X86_FPU
+dnl #
+dnl # Pre-4.2:	Use kernel_fpu_{begin,end}()
+dnl #		HAVE_KERNEL_FPU & KERNEL_EXPORTS_X86_FPU
 dnl #
 AC_DEFUN([ZFS_AC_KERNEL_FPU], [
-	AC_MSG_CHECKING([whether asm/fpu/api.h exists])
+	AC_MSG_CHECKING([which kernel_fpu function to use])
 	ZFS_LINUX_TRY_COMPILE([
-		#include <linux/kernel.h>
-		#include <asm/fpu/api.h>
+		#include <asm/i387.h>
+		#include <asm/xcr.h>
 	],[
-		__kernel_fpu_begin();
+		kernel_fpu_begin();
+		kernel_fpu_end();
 	],[
-		AC_MSG_RESULT(yes)
-		AC_DEFINE(HAVE_FPU_API_H, 1, [kernel has <asm/fpu/api.h> interface])
+		AC_MSG_RESULT(kernel_fpu_*)
+		AC_DEFINE(HAVE_KERNEL_FPU, 1, [kernel has kernel_fpu_* functions])
+		AC_DEFINE(KERNEL_EXPORTS_X86_FPU, 1, [kernel exports FPU functions])
 	],[
-		AC_MSG_RESULT(no)
+		ZFS_LINUX_TRY_COMPILE([
+			#include <linux/kernel.h>
+			#include <asm/fpu/api.h>
+		],[
+			__kernel_fpu_begin();
+			__kernel_fpu_end();
+		],[
+			AC_MSG_RESULT(__kernel_fpu_*)
+			AC_DEFINE(HAVE_UNDERSCORE_KERNEL_FPU, 1, [kernel has __kernel_fpu_* functions])
+			AC_DEFINE(KERNEL_EXPORTS_X86_FPU, 1, [kernel exports FPU functions])
+		],[
+			AC_MSG_RESULT(not exported)
+		])
 	])
 ])
diff --git a/include/linux/simd_x86.h b/include/linux/simd_x86.h
index c9e3970c0cf..c55cb177893 100644
--- a/include/linux/simd_x86.h
+++ b/include/linux/simd_x86.h
@@ -81,7 +81,7 @@
 #endif
 
 #if defined(_KERNEL)
-#if defined(HAVE_FPU_API_H)
+#if defined(HAVE_UNDERSCORE_KERNEL_FPU)
 #include <asm/fpu/api.h>
 #include <asm/fpu/internal.h>
 #define	kfpu_begin()		\
@@ -94,12 +94,18 @@
 	__kernel_fpu_end();		\
 	preempt_enable();		\
 }
-#else
+#elif defined(HAVE_KERNEL_FPU)
 #include <asm/i387.h>
 #include <asm/xcr.h>
 #define	kfpu_begin()	kernel_fpu_begin()
 #define	kfpu_end()		kernel_fpu_end()
-#endif /* defined(HAVE_FPU_API_H) */
+#else
+/* Kernel doesn't export any kernel_fpu_* functions */
+#include <asm/fpu/internal.h>	/* For kernel xgetbv() */
+#define	kfpu_begin() 	panic("This code should never run")
+#define	kfpu_end() 	panic("This code should never run")
+#endif /* defined(HAVE_KERNEL_FPU) */
+
 #else
 /*
  * fpu dummy methods for userspace
@@ -278,11 +284,13 @@ __simd_state_enabled(const uint64_t state)
 	boolean_t has_osxsave;
 	uint64_t xcr0;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_OSXSAVE)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_OSXSAVE) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_osxsave = !!boot_cpu_has(X86_FEATURE_OSXSAVE);
-#elif defined(_KERNEL) && !defined(X86_FEATURE_OSXSAVE)
-	has_osxsave = B_FALSE;
 #else
+	has_osxsave = B_FALSE;
+#endif
+#elif !defined(_KERNEL)
 	has_osxsave = __cpuid_has_osxsave();
 #endif
 
@@ -307,8 +315,12 @@ static inline boolean_t
 zfs_sse_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_XMM));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_sse());
 #endif
 }
@@ -320,8 +332,12 @@ static inline boolean_t
 zfs_sse2_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_XMM2));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_sse2());
 #endif
 }
@@ -333,8 +349,12 @@ static inline boolean_t
 zfs_sse3_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_XMM3));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_sse3());
 #endif
 }
@@ -346,8 +366,12 @@ static inline boolean_t
 zfs_ssse3_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_SSSE3));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_ssse3());
 #endif
 }
@@ -359,8 +383,12 @@ static inline boolean_t
 zfs_sse4_1_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_XMM4_1));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_sse4_1());
 #endif
 }
@@ -372,8 +400,12 @@ static inline boolean_t
 zfs_sse4_2_available(void)
 {
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_XMM4_2));
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_sse4_2());
 #endif
 }
@@ -386,8 +418,12 @@ zfs_avx_available(void)
 {
 	boolean_t has_avx;
 #if defined(_KERNEL)
+#if defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx = !!boot_cpu_has(X86_FEATURE_AVX);
 #else
+	has_avx = B_FALSE;
+#endif
+#elif !defined(_KERNEL)
 	has_avx = __cpuid_has_avx();
 #endif
 
@@ -401,11 +437,13 @@ static inline boolean_t
 zfs_avx2_available(void)
 {
 	boolean_t has_avx2;
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX2)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX2) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx2 = !!boot_cpu_has(X86_FEATURE_AVX2);
-#elif defined(_KERNEL) && !defined(X86_FEATURE_AVX2)
-	has_avx2 = B_FALSE;
 #else
+	has_avx2 = B_FALSE;
+#endif
+#elif !defined(_KERNEL)
 	has_avx2 = __cpuid_has_avx2();
 #endif
 
@@ -418,11 +456,13 @@ zfs_avx2_available(void)
 static inline boolean_t
 zfs_bmi1_available(void)
 {
-#if defined(_KERNEL) && defined(X86_FEATURE_BMI1)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_BMI1) && defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_BMI1));
-#elif defined(_KERNEL) && !defined(X86_FEATURE_BMI1)
-	return (B_FALSE);
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_bmi1());
 #endif
 }
@@ -433,16 +473,17 @@ zfs_bmi1_available(void)
 static inline boolean_t
 zfs_bmi2_available(void)
 {
-#if defined(_KERNEL) && defined(X86_FEATURE_BMI2)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_BMI2) && defined(KERNEL_EXPORTS_X86_FPU)
 	return (!!boot_cpu_has(X86_FEATURE_BMI2));
-#elif defined(_KERNEL) && !defined(X86_FEATURE_BMI2)
-	return (B_FALSE);
 #else
+	return (B_FALSE);
+#endif
+#elif !defined(_KERNEL)
 	return (__cpuid_has_bmi2());
 #endif
 }
 
-
 /*
  * AVX-512 family of instruction sets:
  *
@@ -466,8 +507,12 @@ zfs_avx512f_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512F)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512F) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = !!boot_cpu_has(X86_FEATURE_AVX512F);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512f();
 #endif
@@ -481,9 +526,13 @@ zfs_avx512cd_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512CD)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512CD) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512CD);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512cd();
 #endif
@@ -497,9 +546,13 @@ zfs_avx512er_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512ER)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512ER) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512ER);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512er();
 #endif
@@ -513,9 +566,13 @@ zfs_avx512pf_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512PF)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512PF) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512PF);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512pf();
 #endif
@@ -529,9 +586,13 @@ zfs_avx512bw_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512BW)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512BW) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512BW);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512bw();
 #endif
@@ -545,9 +606,13 @@ zfs_avx512dq_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512DQ)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512DQ) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512DQ);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512dq();
 #endif
@@ -561,9 +626,13 @@ zfs_avx512vl_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512VL)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512VL) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512VL);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512vl();
 #endif
@@ -577,9 +646,13 @@ zfs_avx512ifma_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512IFMA)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512IFMA) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512IFMA);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512ifma();
 #endif
@@ -593,9 +666,13 @@ zfs_avx512vbmi_available(void)
 {
 	boolean_t has_avx512 = B_FALSE;
 
-#if defined(_KERNEL) && defined(X86_FEATURE_AVX512VBMI)
+#if defined(_KERNEL)
+#if defined(X86_FEATURE_AVX512VBMI) && defined(KERNEL_EXPORTS_X86_FPU)
 	has_avx512 = boot_cpu_has(X86_FEATURE_AVX512F) &&
 	    boot_cpu_has(X86_FEATURE_AVX512VBMI);
+#else
+	has_avx512 = B_FALSE;
+#endif
 #elif !defined(_KERNEL)
 	has_avx512 = __cpuid_has_avx512f() &&
 	    __cpuid_has_avx512vbmi();
