MTE_ENABLED (); \
bool __attribute__((unused)) sve = \
GLRO(dl_aarch64_cpu_features).sve; \
+ bool __attribute__((unused)) prefer_sve_ifuncs = \
+ GLRO(dl_aarch64_cpu_features).prefer_sve_ifuncs; \
bool __attribute__((unused)) mops = \
GLRO(dl_aarch64_cpu_features).mops;
#include <sys/auxv.h>
#include <elf/dl-hwcaps.h>
#include <sys/prctl.h>
+#include <sys/utsname.h>
#include <dl-tunables-parse.h>
#define DCZID_DZP_MASK (1 << 4)
return UINT64_MAX;
}
+#if __LINUX_KERNEL_VERSION < 0x060200
+
+/* Return true if we prefer using SVE in string ifuncs. Old kernels disable
+ SVE after every system call which results in unnecessary traps if memcpy
+ uses SVE. This is true for kernels between 4.15.0 and before 6.2.0, except
+ for 5.14.0 which was patched. For these versions return false to avoid using
+ SVE ifuncs.
+ Parse the kernel version into a 24-bit kernel.major.minor value without
+ calling any library functions. If uname() is not supported or if the version
+ format is not recognized, assume the kernel is modern and return true. */
+
+static inline bool
+prefer_sve_ifuncs (void)
+{
+ struct utsname buf;
+ const char *p = &buf.release[0];
+ int kernel = 0;
+ int val;
+
+ if (__uname (&buf) < 0)
+ return true;
+
+ for (int shift = 16; shift >= 0; shift -= 8)
+ {
+ for (val = 0; *p >= '0' && *p <= '9'; p++)
+ val = val * 10 + *p - '0';
+ kernel |= (val & 255) << shift;
+ if (*p++ != '.')
+ break;
+ }
+
+ if (kernel >= 0x060200 || kernel == 0x050e00)
+ return true;
+ if (kernel >= 0x040f00)
+ return false;
+ return true;
+}
+
+#endif
+
static inline void
init_cpu_features (struct cpu_features *cpu_features)
{
/* Check if SVE is supported. */
cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE;
+ cpu_features->prefer_sve_ifuncs = cpu_features->sve;
+
+#if __LINUX_KERNEL_VERSION < 0x060200
+ if (cpu_features->sve)
+ cpu_features->prefer_sve_ifuncs = prefer_sve_ifuncs ();
+#endif
+
/* Check if MOPS is supported. */
cpu_features->mops = GLRO (dl_hwcap2) & HWCAP2_MOPS;
}