]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
AArch64: Check kernel version for SVE ifuncs
authorWilco Dijkstra <wilco.dijkstra@arm.com>
Thu, 21 Mar 2024 16:48:33 +0000 (16:48 +0000)
committerWilco Dijkstra <wilco.dijkstra@arm.com>
Tue, 9 Apr 2024 17:02:26 +0000 (18:02 +0100)
Old Linux kernels disable SVE after every system call.  Calling the
SVE-optimized memcpy afterwards will then cause a trap to reenable SVE.
As a result, applications with a high use of syscalls may run slower with
the SVE memcpy.  This is true for kernels between 4.15.0 and before 6.2.0,
except for 5.14.0 which was patched.  Avoid this by checking the kernel
version and selecting the SVE ifunc on modern kernels.

Parse the kernel version reported by uname() 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.

Tested-by: Florian Weimer <fweimer@redhat.com>
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
(cherry picked from commit 2e94e2f5d2bf2de124c8ad7da85463355e54ccb2)

sysdeps/aarch64/multiarch/init-arch.h
sysdeps/aarch64/multiarch/memcpy.c
sysdeps/aarch64/multiarch/memmove.c
sysdeps/unix/sysv/linux/aarch64/cpu-features.c
sysdeps/unix/sysv/linux/aarch64/cpu-features.h

index a32f67b5a87cc952145aed5e49f242ae41e9242a..4baeedae07dec3f9f63afc7bb84928ba5c409caa 100644 (file)
@@ -36,5 +36,7 @@
     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;
index f054aaf19b03c74a56781156f60abfcded2c0b7a..15b0c4e0f540b63def8c42931559e6e8deabc1ad 100644 (file)
@@ -47,7 +47,7 @@ select_memcpy_ifunc (void)
     {
       if (IS_A64FX (midr))
        return __memcpy_a64fx;
-      return __memcpy_sve;
+      return prefer_sve_ifuncs ? __memcpy_sve : __memcpy_generic;
     }
 
   if (IS_THUNDERX (midr))
index ca483394ae6ebac6c8ecd7e76a5bebb45b2bf9a7..2ae265f27fc004aff18a10fa4aeda2f3591cd00a 100644 (file)
@@ -47,7 +47,7 @@ select_memmove_ifunc (void)
     {
       if (IS_A64FX (midr))
        return __memmove_a64fx;
-      return __memmove_sve;
+      return prefer_sve_ifuncs ? __memmove_sve : __memmove_generic;
     }
 
   if (IS_THUNDERX (midr))
index 12146ddf71a73eec0656efe2b217dfeee27e2ff1..5b0a2814d150c6f7d562137af34cbf5cdccc26e6 100644 (file)
@@ -20,6 +20,7 @@
 #include <sys/auxv.h>
 #include <elf/dl-hwcaps.h>
 #include <sys/prctl.h>
+#include <sys/utsname.h>
 
 #define DCZID_DZP_MASK (1 << 4)
 #define DCZID_BS_MASK (0xf)
@@ -59,6 +60,46 @@ get_midr_from_mcpu (const char *mcpu)
 }
 #endif
 
+#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)
 {
@@ -119,6 +160,13 @@ 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;
 }
index d3bfc1b318481c625805c36020baadab2b091467..5f2da91ebbd0adafb0d84ec503b0f902f566da5a 100644 (file)
@@ -1,6 +1,6 @@
 /* Initialize CPU feature data.  AArch64 version.
    This file is part of the GNU C Library.
-   Copyright (C) 2017-2021 Free Software Foundation, Inc.
+   Copyright (C) 2017-2024 Free Software Foundation, Inc.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -71,6 +71,7 @@ struct cpu_features
   /* Currently, the GLIBC memory tagging tunable only defines 8 bits.  */
   uint8_t mte_state;
   bool sve;
+  bool prefer_sve_ifuncs;
   bool mops;
 };