diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/generic/dl-tunables-parse.h | 134 | ||||
-rw-r--r-- | sysdeps/s390/cpu-features.c | 165 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/aarch64/cpu-features.c | 33 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/powerpc/cpu-features.c | 45 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c | 6 | ||||
-rw-r--r-- | sysdeps/x86/Makefile | 2 | ||||
-rw-r--r-- | sysdeps/x86/cpu-tunables.c | 118 | ||||
-rw-r--r-- | sysdeps/x86/tst-hwcap-tunables.c | 148 |
8 files changed, 424 insertions, 227 deletions
diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h new file mode 100644 index 0000000000..b37be0443b --- /dev/null +++ b/sysdeps/generic/dl-tunables-parse.h @@ -0,0 +1,134 @@ +/* Helper functions to handle tunable strings. + Copyright (C) 2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _DL_TUNABLES_PARSE_H +#define _DL_TUNABLES_PARSE_H 1 + +#include <assert.h> +#include <string.h> + +/* Compare the contents of STRVAL with STR of size LEN. The STR might not + be null-terminated. */ +static __always_inline bool +tunable_strcmp (const struct tunable_str_t *strval, const char *str, + size_t len) +{ + return strval->len == len && memcmp (strval->str, str, len) == 0; +} +#define tunable_strcmp_cte(__tunable, __str) \ + tunable_strcmp (&__tunable->strval, __str, sizeof (__str) - 1) + +/* + Helper functions to iterate over a tunable string composed by multiple + suboptions separated by commaxi; this is a common pattern for CPU. Each + suboptions is return in the form of { address, size } (no null terminated). + For instance: + + struct tunable_str_comma_t ts; + tunable_str_comma_init (&ts, valp); + + struct tunable_str_t t; + while (tunable_str_comma_next (&ts, &t)) + { + _dl_printf ("[%s] %.*s (%d)\n", + __func__, + (int) tstr.len, + tstr.str, + (int) tstr.len); + + if (tunable_str_comma_strcmp (&t, opt, opt1_len)) + { + [...] + } + else if (tunable_str_comma_strcmp_cte (&t, "opt2")) + { + [...] + } + } + + NB: These function are expected to be called from tunable callback + functions along with tunable_val_t with string types. +*/ + +struct tunable_str_comma_state_t +{ + const char *p; + size_t plen; + size_t maxplen; +}; + +struct tunable_str_comma_t +{ + const char *str; + size_t len; + bool disable; +}; + +static inline void +tunable_str_comma_init (struct tunable_str_comma_state_t *state, + tunable_val_t *valp) +{ + assert (valp->strval.str != NULL); + state->p = valp->strval.str; + state->plen = 0; + state->maxplen = valp->strval.len; +} + +static inline bool +tunable_str_comma_next (struct tunable_str_comma_state_t *state, + struct tunable_str_comma_t *str) +{ + if (*state->p == '\0' || state->plen >= state->maxplen) + return false; + + const char *c; + for (c = state->p; *c != ','; c++, state->plen++) + if (*c == '\0' || state->plen == state->maxplen) + break; + + str->str = state->p; + str->len = c - state->p; + + if (str->len > 0) + { + str->disable = *str->str == '-'; + if (str->disable) + { + str->str = str->str + 1; + str->len = str->len - 1; + } + } + + state->p = c + 1; + state->plen++; + + return true; +} + +/* Compare the contents of T with STR of size LEN. The STR might not be + null-terminated. */ +static __always_inline bool +tunable_str_comma_strcmp (const struct tunable_str_comma_t *t, const char *str, + size_t len) +{ + return t->len == len && memcmp (t->str, str, len) == 0; +} +#define tunable_str_comma_strcmp_cte(__t, __str) \ + tunable_str_comma_strcmp (__t, __str, sizeof (__str) - 1) + +#endif diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c index 55449ba07f..06c1cab0fd 100644 --- a/sysdeps/s390/cpu-features.c +++ b/sysdeps/s390/cpu-features.c @@ -22,6 +22,7 @@ #include <ifunc-memcmp.h> #include <string.h> #include <dl-symbol-redir-ifunc.h> +#include <dl-tunables-parse.h> #define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR) \ (DEST_PTR)->hwcap = (SRC_PTR)->hwcap; \ @@ -51,33 +52,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) struct cpu_features cpu_features_curr; S390_COPY_CPU_FEATURES (cpu_features, &cpu_features_curr); - const char *token = valp->strval; - do + struct tunable_str_comma_state_t ts; + tunable_str_comma_init (&ts, valp); + + struct tunable_str_comma_t t; + while (tunable_str_comma_next (&ts, &t)) { - const char *token_end, *feature; - bool disable; - size_t token_len; - size_t feature_len; - - /* Find token separator or end of string. */ - for (token_end = token; *token_end != ','; token_end++) - if (*token_end == '\0') - break; - - /* Determine feature. */ - token_len = token_end - token; - if (*token == '-') - { - disable = true; - feature = token + 1; - feature_len = token_len - 1; - } - else - { - disable = false; - feature = token; - feature_len = token_len; - } + if (t.len == 0) + continue; /* Handle only the features here which are really used in the IFUNC-resolvers. All others are ignored as the values are only used @@ -85,86 +67,64 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) bool reset_features = false; unsigned long int hwcap_mask = 0UL; unsigned long long stfle_bits0_mask = 0ULL; + bool disable = t.disable; - if ((*feature == 'z' || *feature == 'a')) + if (tunable_str_comma_strcmp_cte (&t, "zEC12") + || tunable_str_comma_strcmp_cte (&t, "arch10")) + { + reset_features = true; + disable = true; + hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT + | HWCAP_S390_VXRS_EXT2; + stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; + } + else if (tunable_str_comma_strcmp_cte (&t, "z13") + || tunable_str_comma_strcmp_cte (&t, "arch11")) + { + reset_features = true; + disable = true; + hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2; + stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; + } + else if (tunable_str_comma_strcmp_cte (&t, "z14") + || tunable_str_comma_strcmp_cte (&t, "arch12")) + { + reset_features = true; + disable = true; + hwcap_mask = HWCAP_S390_VXRS_EXT2; + stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; + } + else if (tunable_str_comma_strcmp_cte (&t, "z15") + || tunable_str_comma_strcmp_cte (&t, "z16") + || tunable_str_comma_strcmp_cte (&t, "arch13") + || tunable_str_comma_strcmp_cte (&t, "arch14")) { - if ((feature_len == 5 && *feature == 'z' - && memcmp (feature, "zEC12", 5) == 0) - || (feature_len == 6 && *feature == 'a' - && memcmp (feature, "arch10", 6) == 0)) - { - reset_features = true; - disable = true; - hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT - | HWCAP_S390_VXRS_EXT2; - stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; - } - else if ((feature_len == 3 && *feature == 'z' - && memcmp (feature, "z13", 3) == 0) - || (feature_len == 6 && *feature == 'a' - && memcmp (feature, "arch11", 6) == 0)) - { - reset_features = true; - disable = true; - hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2; - stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; - } - else if ((feature_len == 3 && *feature == 'z' - && memcmp (feature, "z14", 3) == 0) - || (feature_len == 6 && *feature == 'a' - && memcmp (feature, "arch12", 6) == 0)) - { - reset_features = true; - disable = true; - hwcap_mask = HWCAP_S390_VXRS_EXT2; - stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; - } - else if ((feature_len == 3 && *feature == 'z' - && (memcmp (feature, "z15", 3) == 0 - || memcmp (feature, "z16", 3) == 0)) - || (feature_len == 6 - && (memcmp (feature, "arch13", 6) == 0 - || memcmp (feature, "arch14", 6) == 0))) - { - /* For z15 or newer we don't have to disable something, - but we have to reset to the original values. */ - reset_features = true; - } + /* For z15 or newer we don't have to disable something, but we have + to reset to the original values. */ + reset_features = true; } - else if (*feature == 'H') + else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS")) { - if (feature_len == 15 - && memcmp (feature, "HWCAP_S390_VXRS", 15) == 0) - { - hwcap_mask = HWCAP_S390_VXRS; - if (disable) - hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2; - } - else if (feature_len == 19 - && memcmp (feature, "HWCAP_S390_VXRS_EXT", 19) == 0) - { - hwcap_mask = HWCAP_S390_VXRS_EXT; - if (disable) - hwcap_mask |= HWCAP_S390_VXRS_EXT2; - else - hwcap_mask |= HWCAP_S390_VXRS; - } - else if (feature_len == 20 - && memcmp (feature, "HWCAP_S390_VXRS_EXT2", 20) == 0) - { - hwcap_mask = HWCAP_S390_VXRS_EXT2; - if (!disable) - hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT; - } + hwcap_mask = HWCAP_S390_VXRS; + if (t.disable) + hwcap_mask |= HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2; } - else if (*feature == 'S') + else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT")) { - if (feature_len == 10 - && memcmp (feature, "STFLE_MIE3", 10) == 0) - { - stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; - } + hwcap_mask = HWCAP_S390_VXRS_EXT; + if (t.disable) + hwcap_mask |= HWCAP_S390_VXRS_EXT2; + else + hwcap_mask |= HWCAP_S390_VXRS; + } + else if (tunable_str_comma_strcmp_cte (&t, "HWCAP_S390_VXRS_EXT2")) + { + hwcap_mask = HWCAP_S390_VXRS_EXT2; + if (!t.disable) + hwcap_mask |= HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT; } + else if (tunable_str_comma_strcmp_cte (&t, "STFLE_MIE3")) + stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3; /* Perform the actions determined above. */ if (reset_features) @@ -187,14 +147,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) else cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask; } - - /* Jump over current token ... */ - token += token_len; - - /* ... and skip token separator for next round. */ - if (*token == ',') token++; } - while (*token != '\0'); /* Copy back the features after checking that no unsupported features were enabled by user. */ diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c index a11a86efab..c57f154b48 100644 --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c @@ -16,10 +16,12 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ +#include <array_length.h> #include <cpu-features.h> #include <sys/auxv.h> #include <elf/dl-hwcaps.h> #include <sys/prctl.h> +#include <dl-tunables-parse.h> #define DCZID_DZP_MASK (1 << 4) #define DCZID_BS_MASK (0xf) @@ -33,25 +35,28 @@ struct cpu_list { const char *name; + size_t len; uint64_t midr; }; -static struct cpu_list cpu_list[] = { - {"thunderxt88", 0x430F0A10}, - {"thunderx2t99", 0x431F0AF0}, - {"thunderx2t99p1", 0x420F5160}, - {"ares", 0x411FD0C0}, - {"emag", 0x503F0001}, - {"kunpeng920", 0x481FD010}, - {"a64fx", 0x460F0010}, - {"generic", 0x0} +static const struct cpu_list cpu_list[] = +{ +#define CPU_LIST_ENTRY(__str, __num) { __str, sizeof (__str) - 1, __num } + CPU_LIST_ENTRY ("thunderxt88", 0x430F0A10), + CPU_LIST_ENTRY ("thunderx2t99", 0x431F0AF0), + CPU_LIST_ENTRY ("thunderx2t99p1", 0x420F5160), + CPU_LIST_ENTRY ("ares", 0x411FD0C0), + CPU_LIST_ENTRY ("emag", 0x503F0001), + CPU_LIST_ENTRY ("kunpeng920", 0x481FD010), + CPU_LIST_ENTRY ("a64fx", 0x460F0010), + CPU_LIST_ENTRY ("generic", 0x0), }; static uint64_t -get_midr_from_mcpu (const char *mcpu) +get_midr_from_mcpu (const struct tunable_str_t *mcpu) { - for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++) - if (strcmp (mcpu, cpu_list[i].name) == 0) + for (int i = 0; i < array_length (cpu_list); i++) + if (tunable_strcmp (mcpu, cpu_list[i].name, cpu_list[i].len)) return cpu_list[i].midr; return UINT64_MAX; @@ -63,7 +68,9 @@ init_cpu_features (struct cpu_features *cpu_features) register uint64_t midr = UINT64_MAX; /* Get the tunable override. */ - const char *mcpu = TUNABLE_GET (glibc, cpu, name, const char *, NULL); + const struct tunable_str_t *mcpu = TUNABLE_GET (glibc, cpu, name, + struct tunable_str_t *, + NULL); if (mcpu != NULL) midr = get_midr_from_mcpu (mcpu); diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c index 7c6e20e702..390b3fd11a 100644 --- a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c +++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c @@ -20,6 +20,7 @@ #include <stdint.h> #include <cpu-features.h> #include <elf/dl-tunables.h> +#include <dl-tunables-parse.h> #include <unistd.h> #include <string.h> @@ -43,41 +44,26 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) struct cpu_features *cpu_features = &GLRO(dl_powerpc_cpu_features); unsigned long int tcbv_hwcap = cpu_features->hwcap; unsigned long int tcbv_hwcap2 = cpu_features->hwcap2; - const char *token = valp->strval; - do + + struct tunable_str_comma_state_t ts; + tunable_str_comma_init (&ts, valp); + + struct tunable_str_comma_t t; + while (tunable_str_comma_next (&ts, &t)) { - const char *token_end, *feature; - bool disable; - size_t token_len, i, feature_len, offset = 0; - /* Find token separator or end of string. */ - for (token_end = token; *token_end != ','; token_end++) - if (*token_end == '\0') - break; + if (t.len == 0) + continue; - /* Determine feature. */ - token_len = token_end - token; - if (*token == '-') - { - disable = true; - feature = token + 1; - feature_len = token_len - 1; - } - else - { - disable = false; - feature = token; - feature_len = token_len; - } - for (i = 0; i < array_length (hwcap_tunables); ++i) + size_t offset = 0; + for (int i = 0; i < array_length (hwcap_tunables); ++i) { const char *hwcap_name = hwcap_names + offset; size_t hwcap_name_len = strlen (hwcap_name); /* Check the tunable name on the supported list. */ - if (hwcap_name_len == feature_len - && memcmp (feature, hwcap_name, feature_len) == 0) + if (tunable_str_comma_strcmp (&t, hwcap_name, hwcap_name_len)) { /* Update the hwcap and hwcap2 bits. */ - if (disable) + if (t.disable) { /* Id is 1 for hwcap2 tunable. */ if (hwcap_tunables[i].id) @@ -98,12 +84,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } offset += hwcap_name_len + 1; } - token += token_len; - /* ... and skip token separator for next round. */ - if (*token == ',') - token++; } - while (*token != '\0'); } static inline void diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c index 2631016a3a..049164f841 100644 --- a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c @@ -110,7 +110,11 @@ do_test (int argc, char *argv[]) run_test ("-arch_2_06", "__memcpy_power7"); if (hwcap & PPC_FEATURE_ARCH_2_05) run_test ("-arch_2_06,-arch_2_05","__memcpy_power6"); - run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4", "__memcpy_power4"); + run_test ("-arch_2_06,-arch_2_05,-power5+,-power5,-power4", + "__memcpy_power4"); + /* Also run with valid, but empty settings. */ + run_test (",-,-arch_2_06,-arch_2_05,-power5+,-power5,,-power4,-", + "__memcpy_power4"); } else { diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile index 6911a07a87..0aafd2afeb 100644 --- a/sysdeps/x86/Makefile +++ b/sysdeps/x86/Makefile @@ -17,6 +17,7 @@ tests += \ tst-cpu-features-cpuinfo-static \ tst-cpu-features-supports \ tst-cpu-features-supports-static \ + tst-hwcap-tunables \ # tests tests-static += \ tst-get-cpu-features-static \ @@ -80,6 +81,7 @@ $(objpfx)tst-isa-level-1.out: $(objpfx)tst-isa-level-mod-1-baseline.so \ endif tst-ifunc-isa-2-ENV = GLIBC_TUNABLES=glibc.cpu.hwcaps=-SSE4_2,-AVX,-AVX2,-AVX512F tst-ifunc-isa-2-static-ENV = $(tst-ifunc-isa-2-ENV) +tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd) endif ifeq ($(subdir),math) diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c index 5697885226..ef96148d30 100644 --- a/sysdeps/x86/cpu-tunables.c +++ b/sysdeps/x86/cpu-tunables.c @@ -24,11 +24,12 @@ #include <string.h> #include <cpu-features.h> #include <ldsodefs.h> +#include <dl-tunables-parse.h> #include <dl-symbol-redir-ifunc.h> #define CHECK_GLIBC_IFUNC_CPU_OFF(f, cpu_features, name, len) \ _Static_assert (sizeof (#name) - 1 == len, #name " != " #len); \ - if (memcmp (f, #name, len) == 0) \ + if (tunable_str_comma_strcmp_cte (&f, #name)) \ { \ CPU_FEATURE_UNSET (cpu_features, name) \ break; \ @@ -38,7 +39,7 @@ which isn't available. */ #define CHECK_GLIBC_IFUNC_PREFERRED_OFF(f, cpu_features, name, len) \ _Static_assert (sizeof (#name) - 1 == len, #name " != " #len); \ - if (memcmp (f, #name, len) == 0) \ + if (tunable_str_comma_strcmp_cte (&f, #name) == 0) \ { \ cpu_features->preferred[index_arch_##name] \ &= ~bit_arch_##name; \ @@ -46,12 +47,11 @@ } /* Enable/disable a preferred feature NAME. */ -#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name, \ - disable, len) \ +#define CHECK_GLIBC_IFUNC_PREFERRED_BOTH(f, cpu_features, name, len) \ _Static_assert (sizeof (#name) - 1 == len, #name " != " #len); \ - if (memcmp (f, #name, len) == 0) \ + if (tunable_str_comma_strcmp_cte (&f, #name)) \ { \ - if (disable) \ + if (f.disable) \ cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name; \ else \ cpu_features->preferred[index_arch_##name] |= bit_arch_##name; \ @@ -61,11 +61,11 @@ /* Enable/disable a preferred feature NAME. Enable a preferred feature only if the feature NEED is usable. */ #define CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH(f, cpu_features, name, \ - need, disable, len) \ + need, len) \ _Static_assert (sizeof (#name) - 1 == len, #name " != " #len); \ - if (memcmp (f, #name, len) == 0) \ + if (tunable_str_comma_strcmp_cte (&f, #name)) \ { \ - if (disable) \ + if (f.disable) \ cpu_features->preferred[index_arch_##name] &= ~bit_arch_##name; \ else if (CPU_FEATURE_USABLE_P (cpu_features, need)) \ cpu_features->preferred[index_arch_##name] |= bit_arch_##name; \ @@ -93,38 +93,20 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) NOTE: the IFUNC selection may change over time. Please check all multiarch implementations when experimenting. */ - const char *p = valp->strval, *c; struct cpu_features *cpu_features = &GLRO(dl_x86_cpu_features); - size_t len; - do - { - const char *n; - bool disable; - size_t nl; - - for (c = p; *c != ','; c++) - if (*c == '\0') - break; + struct tunable_str_comma_state_t ts; + tunable_str_comma_init (&ts, valp); - len = c - p; - disable = *p == '-'; - if (disable) - { - n = p + 1; - nl = len - 1; - } - else - { - n = p; - nl = len; - } - switch (nl) + struct tunable_str_comma_t n; + while (tunable_str_comma_next (&ts, &n)) + { + switch (n.len) { default: break; case 3: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX, 3); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, CX8, 3); @@ -135,7 +117,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } break; case 4: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX2, 4); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, BMI1, 4); @@ -149,7 +131,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } break; case 5: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, LZCNT, 5); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, MOVBE, 5); @@ -159,12 +141,12 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } break; case 6: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, POPCNT, 6); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_1, 6); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, SSE4_2, 6); - if (memcmp (n, "XSAVEC", 6) == 0) + if (memcmp (n.str, "XSAVEC", 6) == 0) { /* Update xsave_state_size to XSAVE state size. */ cpu_features->xsave_state_size @@ -174,14 +156,14 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } break; case 7: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512F, 7); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, OSXSAVE, 7); } break; case 8: - if (disable) + if (n.disable) { CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512CD, 8); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512BW, 8); @@ -190,86 +172,72 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512PF, 8); CHECK_GLIBC_IFUNC_CPU_OFF (n, cpu_features, AVX512VL, 8); } - CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF, - disable, 8); + CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Slow_BSF, 8); break; case 11: { - CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Prefer_ERMS, - disable, 11); - CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Prefer_FSRM, - disable, 11); + CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_ERMS, + 11); + CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, Prefer_FSRM, + 11); CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH (n, cpu_features, Slow_SSE4_2, SSE4_2, - disable, 11); + 11); } break; case 15: { CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Fast_Rep_String, - disable, 15); + Fast_Rep_String, 15); } break; case 16: { CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH - (n, cpu_features, Prefer_No_AVX512, AVX512F, - disable, 16); + (n, cpu_features, Prefer_No_AVX512, AVX512F, 16); } break; case 18: { CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Fast_Copy_Backward, - disable, 18); + Fast_Copy_Backward, 18); } break; case 19: { CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Fast_Unaligned_Load, - disable, 19); + Fast_Unaligned_Load, 19); CHECK_GLIBC_IFUNC_PREFERRED_BOTH (n, cpu_features, - Fast_Unaligned_Copy, - disable, 19); + Fast_Unaligned_Copy, 19); } break; case 20: { CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH - (n, cpu_features, Prefer_No_VZEROUPPER, AVX, disable, - 20); + (n, cpu_features, Prefer_No_VZEROUPPER, AVX, 20); } break; case 23: { CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH - (n, cpu_features, AVX_Fast_Unaligned_Load, AVX, - disable, 23); + (n, cpu_features, AVX_Fast_Unaligned_Load, AVX, 23); } break; case 24: { CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH - (n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F, - disable, 24); + (n, cpu_features, MathVec_Prefer_No_AVX512, AVX512F, 24); } break; case 26: { CHECK_GLIBC_IFUNC_PREFERRED_NEED_BOTH - (n, cpu_features, Prefer_PMINUB_for_stringop, SSE2, - disable, 26); + (n, cpu_features, Prefer_PMINUB_for_stringop, SSE2, 26); } break; } - p += len + 1; } - while (*c != '\0'); } #if CET_ENABLED @@ -277,11 +245,11 @@ attribute_hidden void TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp) { - if (memcmp (valp->strval, "on", sizeof ("on")) == 0) + if (tunable_strcmp_cte (valp, "on")) GL(dl_x86_feature_control).ibt = cet_always_on; - else if (memcmp (valp->strval, "off", sizeof ("off")) == 0) + else if (tunable_strcmp_cte (valp, "off")) GL(dl_x86_feature_control).ibt = cet_always_off; - else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0) + else if (tunable_strcmp_cte (valp, "permissive")) GL(dl_x86_feature_control).ibt = cet_permissive; } @@ -289,11 +257,11 @@ attribute_hidden void TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp) { - if (memcmp (valp->strval, "on", sizeof ("on")) == 0) + if (tunable_strcmp_cte (valp, "on")) GL(dl_x86_feature_control).shstk = cet_always_on; - else if (memcmp (valp->strval, "off", sizeof ("off")) == 0) + else if (tunable_strcmp_cte (valp, "off")) GL(dl_x86_feature_control).shstk = cet_always_off; - else if (memcmp (valp->strval, "permissive", sizeof ("permissive")) == 0) + else if (tunable_strcmp_cte (valp, "permissive")) GL(dl_x86_feature_control).shstk = cet_permissive; } #endif diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c new file mode 100644 index 0000000000..01a9377f7e --- /dev/null +++ b/sysdeps/x86/tst-hwcap-tunables.c @@ -0,0 +1,148 @@ +/* Tests for x86 GLIBC_TUNABLES=glibc.cpu.hwcaps filter. + Copyright (C) 2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <array_length.h> +#include <getopt.h> +#include <ifunc-impl-list.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <intprops.h> +#include <support/check.h> +#include <support/support.h> +#include <support/xunistd.h> +#include <support/capture_subprocess.h> + +/* Nonzero if the program gets called via `exec'. */ +#define CMDLINE_OPTIONS \ + { "restart", no_argument, &restart, 1 }, +static int restart; + +/* Disable everything. */ +static const char *test_1[] = +{ + "__memcpy_avx512_no_vzeroupper", + "__memcpy_avx512_unaligned", + "__memcpy_avx512_unaligned_erms", + "__memcpy_evex_unaligned", + "__memcpy_evex_unaligned_erms", + "__memcpy_avx_unaligned", + "__memcpy_avx_unaligned_erms", + "__memcpy_avx_unaligned_rtm", + "__memcpy_avx_unaligned_erms_rtm", + "__memcpy_ssse3", +}; + +static const struct test_t +{ + const char *env; + const char *const *funcs; + size_t nfuncs; +} tests[] = +{ + { + /* Disable everything. */ + "-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable," + "-AVX512F_Usable,-SSE4_1,-SSE4_2,-SSSE3,-Fast_Unaligned_Load,-ERMS," + "-AVX_Fast_Unaligned_Load", + test_1, + array_length (test_1) + }, + { + /* Same as before, but with some empty suboptions. */ + ",-,-Prefer_ERMS,-Prefer_FSRM,-AVX,-AVX2,-AVX_Usable,-AVX2_Usable," + "-AVX512F_Usable,-SSE4_1,-SSE4_2,,-SSSE3,-Fast_Unaligned_Load,,-,-ERMS," + "-AVX_Fast_Unaligned_Load,-,", + test_1, + array_length (test_1) + } +}; + +/* Called on process re-execution. */ +_Noreturn static void +handle_restart (int ntest) +{ + struct libc_ifunc_impl impls[32]; + int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls)); + if (cnt == 0) + _exit (EXIT_SUCCESS); + TEST_VERIFY_EXIT (cnt >= 1); + for (int i = 0; i < cnt; i++) + { + for (int f = 0; f < tests[ntest].nfuncs; f++) + { + if (strcmp (impls[i].name, tests[ntest].funcs[f]) == 0) + TEST_COMPARE (impls[i].usable, false); + } + } + + _exit (EXIT_SUCCESS); +} + +static int +do_test (int argc, char *argv[]) +{ + /* We must have either: + - One our fource parameters left if called initially: + + path to ld.so optional + + "--library-path" optional + + the library path optional + + the application name + + the test to check */ + + TEST_VERIFY_EXIT (argc == 2 || argc == 5); + + if (restart) + handle_restart (atoi (argv[1])); + + char nteststr[INT_BUFSIZE_BOUND (int)]; + + char *spargv[10]; + { + int i = 0; + for (; i < argc - 1; i++) + spargv[i] = argv[i + 1]; + spargv[i++] = (char *) "--direct"; + spargv[i++] = (char *) "--restart"; + spargv[i++] = nteststr; + spargv[i] = NULL; + } + + for (int i = 0; i < array_length (tests); i++) + { + snprintf (nteststr, sizeof nteststr, "%d", i); + + printf ("[%d] Spawned test for %s\n", i, tests[i].env); + char *tunable = xasprintf ("glibc.cpu.hwcaps=%s", tests[i].env); + setenv ("GLIBC_TUNABLES", tunable, 1); + + struct support_capture_subprocess result + = support_capture_subprogram (spargv[0], spargv); + support_capture_subprocess_check (&result, "tst-tunables", 0, + sc_allow_stderr); + support_capture_subprocess_free (&result); + + free (tunable); + } + + return 0; +} + +#define TEST_FUNCTION_ARGV do_test +#include <support/test-driver.c> |