The attached 2 patches, add a new cpu-supports module,
and updates the crc-x86_64 module to use it.
This ensures that any users of the crc-x86_64 module
will honor the GLIBC_TUNABLES environment variable.
Specifically it will allow disabling of the hardware acceleration
used by crc-x86_64 using:

  export GLIBC_TUNABLES=glibc.cpu.hwcaps=-AVX

Note this is a general mechanism provided by glibc
to tune hardware acceleration within glibc and also
determines what libs are loaded with its dynamic dispatch mechanism:
On my "x86-64-v3" system for example:

$ ld.so --help | grep -B1 x86-64-v
Subdirectories of glibc-hwcaps directories, in priority order:
  x86-64-v4
  x86-64-v3 (supported, searched)
  x86-64-v2 (supported, searched)
$ ld.so --help | GLIBC_TUNABLES=glibc.cpu.hwcaps=-AVX \
 grep -B1 x86-64-v
Subdirectories of glibc-hwcaps directories, in priority order:
  x86-64-v4
  x86-64-v3 (supported, searched)
  x86-64-v2 (supported, searched)

This module will also be used by coreutils to ensure
its various uses of __builtin_cpu_supports() are similarly gated.

cheers,
Padraig
From d17ad4582cd2d918f368c175eb1060383646cb66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Fri, 12 Sep 2025 18:01:08 +0100
Subject: [PATCH 2/2] crc-x86_64: honor GLIBC_TUNABLES to disable acceleration

* modules/crc-x86_64: Depend on cpu-supports
* lib/crc.c: Call the cpu_supports() wrapper that honors glibc hwcaps.
---
 lib/crc.c          | 9 +++++----
 modules/crc-x86_64 | 1 +
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/lib/crc.c b/lib/crc.c
index 319a23715b..6847764d50 100644
--- a/lib/crc.c
+++ b/lib/crc.c
@@ -22,6 +22,7 @@
 #include "endian.h"
 
 #ifdef GL_CRC_X86_64_PCLMUL
+#include "cpu-supports.h"
 #include "crc-x86_64.h"
 static bool pclmul_enabled = false;
 static bool pclmul_checked = false;
@@ -118,8 +119,8 @@ crc32_update_no_xor (uint32_t crc, const char *buf, size_t len)
 #ifdef GL_CRC_X86_64_PCLMUL
   if (!pclmul_checked)
     {
-      pclmul_enabled = (0 < __builtin_cpu_supports ("pclmul")
-                        && 0 < __builtin_cpu_supports ("avx"));
+      pclmul_enabled = (cpu_supports ("pclmul")
+                        && cpu_supports ("avx"));
       pclmul_checked = true;
     }
 
@@ -200,8 +201,8 @@ crc32_update_no_xor (uint32_t crc, const char *buf, size_t len)
 #ifdef GL_CRC_X86_64_PCLMUL
   if (!pclmul_checked)
     {
-      pclmul_enabled = (0 < __builtin_cpu_supports ("pclmul")
-                        && 0 < __builtin_cpu_supports ("avx"));
+      pclmul_enabled = (cpu_supports ("pclmul")
+                        && cpu_supports ("avx"));
       pclmul_checked = true;
     }
 
diff --git a/modules/crc-x86_64 b/modules/crc-x86_64
index 4ce70a99c6..2de91341f0 100644
--- a/modules/crc-x86_64
+++ b/modules/crc-x86_64
@@ -7,6 +7,7 @@ lib/crc-x86_64-pclmul.c
 m4/crc-x86_64.m4
 
 Depends-on:
+cpu-supports
 stdint-h
 crc
 
-- 
2.50.1

From f06f9fd7789e430c75a0f0fc0c573f9ff82f6b00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Fri, 12 Sep 2025 16:55:45 +0100
Subject: [PATCH 1/2] cpu-supports: a module to honor
 GLIBC_TUNABLES=glibc.cpu.hwcaps

This functionality is useful to allow better test coverage at least,
and may be useful for users to tune their environment,
avoiding CPU throttling for example.

* lib/cpu-supports.h (cpu_supports): A new wrapper that
checks that the GLIBC_TUNABLES environment variable allows
the hardware feature, before checking with __builtin_cpu_supports().
(gcc_feature_to_glibc_hwcap): A helper that will resolve
at compile time with standard optimizations enabled.
* lib/cpu-supports.c (hwcap_allowed): Query the GLIBC_TUNABLES
environment variable (read once per process), to see if the
passed GLIBC_HWCAP is allowed.
* modules/cpu-supports: New module definition.
---
 lib/cpu-supports.c   | 50 +++++++++++++++++++++++++++++++++++
 lib/cpu-supports.h   | 63 ++++++++++++++++++++++++++++++++++++++++++++
 modules/cpu-supports | 27 +++++++++++++++++++
 3 files changed, 140 insertions(+)
 create mode 100644 lib/cpu-supports.c
 create mode 100644 lib/cpu-supports.h
 create mode 100644 modules/cpu-supports

diff --git a/lib/cpu-supports.c b/lib/cpu-supports.c
new file mode 100644
index 0000000000..47f7031c9c
--- /dev/null
+++ b/lib/cpu-supports.c
@@ -0,0 +1,50 @@
+#include <config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu-supports.h"
+
+extern bool
+hwcap_allowed (char const* glibc_hwcap)
+{
+  if (! glibc_hwcap)
+    return true;
+
+  /* Match how GLIBC parses tunables as indicated with:
+     GLIBC_TUNABLES=glibc.cpu.hwcaps=... ld.so --list-tunables | grep hwcaps  */
+  static char const *GLIBC_TUNABLES;
+  if (! GLIBC_TUNABLES)
+    { /* Cache glibc.cpu.hwcaps once per process.  */
+      if ((GLIBC_TUNABLES = getenv ("GLIBC_TUNABLES")))
+        {
+          char const *tunables_start = GLIBC_TUNABLES;
+          char const *last_hwcaps;
+          while ((last_hwcaps = strstr (GLIBC_TUNABLES, "glibc.cpu.hwcaps=")))
+            GLIBC_TUNABLES = last_hwcaps + sizeof "glibc.cpu.hwcaps=" - 1;
+          if (GLIBC_TUNABLES == tunables_start)  /* No match.  */
+            GLIBC_TUNABLES = "";
+        }
+      else
+        GLIBC_TUNABLES = "";
+    }
+
+  assert (GLIBC_TUNABLES);
+
+  if (! *GLIBC_TUNABLES)
+    return true;
+
+  char const *sentinel = strchr (GLIBC_TUNABLES, ':');
+  if (! sentinel)
+    sentinel = GLIBC_TUNABLES + strlen (GLIBC_TUNABLES);
+  char const *cap = GLIBC_TUNABLES;
+  while ((cap = strstr (cap, glibc_hwcap)) && cap < sentinel)
+    { /* Check it's not a partial match.  */
+      cap += strlen (glibc_hwcap);
+      if (*cap == ',' || *cap == ':' || *cap == '\0')
+        return false;  /* Feature disabled.  */
+      /* glibc hwcaps can't have '-' in name so ok to search from here. */
+    }
+
+  return true;
+}
diff --git a/lib/cpu-supports.h b/lib/cpu-supports.h
new file mode 100644
index 0000000000..2743f273bf
--- /dev/null
+++ b/lib/cpu-supports.h
@@ -0,0 +1,63 @@
+/* Support routines to query GLIBC_TUNABLES=glibc.cpu.hwcaps
+
+   Copyright 2025 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include "attribute.h"
+
+#ifndef _CPU_SUPPORTS_H
+# define _CPU_SUPPORTS_H
+
+# define cpu_supports(feature) \
+  (hwcap_allowed (gcc_feature_to_glibc_hwcap (feature)) \
+   && __builtin_cpu_supports (feature))
+
+# ifndef STREQ
+#  define STREQ(a, b) (strcmp (a, b) == 0)
+# endif
+
+/* Return the glibc.cpu.hwcaps setting (prepended with "-"),
+   corresponding to the passed gcc _builtin_cpu_supports(FEATURE).
+   Supported hwcaps can be identified from the bit_cpu_* defines
+   in GLIBC's sysdeps/x86/include/cpu-features.h
+   Note this mapping should resolve at compile time.  */
+ATTRIBUTE_PURE
+static inline char const*
+gcc_feature_to_glibc_hwcap (char const* feature)
+{
+  char const* hwcap = nullptr;
+
+  if (0);
+# if defined __x86_64__
+  else if (STREQ (feature, "avx"))          hwcap = "-AVX";
+  else if (STREQ (feature, "avx2"))         hwcap = "-AVX2";
+  else if (STREQ (feature, "avx512bw"))     hwcap = "-AVX512BW";
+  else if (STREQ (feature, "avx512f"))      hwcap = "-AVX512F";
+  else if (STREQ (feature, "pclmul"))       hwcap = "-PCLMULQDQ";
+  else if (STREQ (feature, "vpclmulqdq"))   hwcap = "-VPCLMULQDQ";
+# elif defined __aarch64__
+  else if (STREQ (feature, "pmull"))        hwcap "-PMULL";
+# endif
+
+  return hwcap;
+}
+
+/* Support GLIBC's interface to disable features using:
+    export GLIBC_TUNABLES=glibc.cpu.hwcaps=-AVX512F,-AVX2,-AVX,-PMULL
+   Return true if the HWCAP is allowed.  */
+extern bool hwcap_allowed (char const* glibc_hwcap);
+
+#endif /* _CPU_SUPPORTS_H */
diff --git a/modules/cpu-supports b/modules/cpu-supports
new file mode 100644
index 0000000000..62b2600bf6
--- /dev/null
+++ b/modules/cpu-supports
@@ -0,0 +1,27 @@
+Description:
+A wrapper for __builtin_cpu_supports to also check GLIBC_TUNABLES
+
+Files:
+lib/cpu-supports.h
+lib/cpu-supports.c
+
+Depends-on:
+assert
+attribute
+bool
+c99
+inline
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += cpu-supports.c
+
+Include:
+"cpu-supports.h"
+
+License:
+GPL
+
+Maintainer:
+all
-- 
2.50.1

Reply via email to