[PATCH v6 1/2] elf: Do not duplicate the GLIBC_TUNABLES string
Siddhesh Poyarekar
siddhesh@sourceware.org
Tue Dec 12 19:15:36 GMT 2023
On 2023-12-06 08:24, Adhemerval Zanella wrote:
> The tunable parsing duplicates the tunable environment variable so it
> null-terminates each one since it simplifies the later parsing. It has
> the drawback of adding another point of failure (__minimal_malloc
> failing), and the memory copy requires tuning the compiler to avoid mem
> operations calls.
>
> The parsing now tracks the tunable start and its size. The
> dl-tunable-parse.h adds helper functions to help parsing, like a strcmp
> that also checks for size and an iterator for suboptions that are
> comma-separated (used on hwcap parsing by x86, powerpc, and s390x).
>
> Since the environment variable is allocated on the stack by the kernel,
> it is safe to keep the references to the suboptions for later parsing
> of string tunables (as done by set_hwcaps by multiple architectures).
>
> Checked on x86_64-linux-gnu, powerpc64le-linux-gnu, and
> aarch64-linux-gnu.
> ---
LGTM.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
> elf/dl-tunables.c | 80 ++++-----
> elf/dl-tunables.h | 6 +-
> elf/tst-tunables.c | 66 ++++++-
> sysdeps/generic/dl-tunables-parse.h | 134 ++++++++++++++
> sysdeps/s390/cpu-features.c | 165 +++++++-----------
> .../unix/sysv/linux/aarch64/cpu-features.c | 33 ++--
> .../unix/sysv/linux/powerpc/cpu-features.c | 45 ++---
> .../sysv/linux/powerpc/tst-hwcap-tunables.c | 6 +-
> sysdeps/x86/Makefile | 4 +-
> sysdeps/x86/cpu-tunables.c | 118 +++++--------
> sysdeps/x86/tst-hwcap-tunables.c | 148 ++++++++++++++++
> 11 files changed, 524 insertions(+), 281 deletions(-)
> create mode 100644 sysdeps/generic/dl-tunables-parse.h
> create mode 100644 sysdeps/x86/tst-hwcap-tunables.c
>
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index 644d21d1b0..3d41e8e28e 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -36,31 +36,8 @@
> #define TUNABLES_INTERNAL 1
> #include "dl-tunables.h"
>
> -#include <not-errno.h>
> -
> -static char *
> -tunables_strdup (const char *in)
> -{
> - size_t i = 0;
> -
> - while (in[i++] != '\0');
> - char *out = __minimal_malloc (i + 1);
> -
> - /* For most of the tunables code, we ignore user errors. However,
> - this is a system error - and running out of memory at program
> - startup should be reported, so we do. */
> - if (out == NULL)
> - _dl_fatal_printf ("failed to allocate memory to process tunables\n");
> -
> - while (i-- > 0)
> - out[i] = in[i];
> -
> - return out;
> -}
> -
> static char **
> -get_next_env (char **envp, char **name, size_t *namelen, char **val,
> - char ***prev_envp)
> +get_next_env (char **envp, char **name, char **val, char ***prev_envp)
> {
> while (envp != NULL && *envp != NULL)
> {
> @@ -76,7 +53,6 @@ get_next_env (char **envp, char **name, size_t *namelen, char **val,
> continue;
>
> *name = envline;
> - *namelen = len;
> *val = &envline[len + 1];
> *prev_envp = prev;
>
> @@ -134,14 +110,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
> /* Validate range of the input value and initialize the tunable CUR if it looks
> good. */
> static void
> -tunable_initialize (tunable_t *cur, const char *strval)
> +tunable_initialize (tunable_t *cur, const char *strval, size_t len)
> {
> - tunable_val_t val;
> + tunable_val_t val = { 0 };
>
> if (cur->type.type_code != TUNABLE_TYPE_STRING)
> val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
> else
> - val.strval = strval;
> + val.strval = (struct tunable_str_t) { strval, len };
> do_tunable_update_val (cur, &val, NULL, NULL);
> }
>
> @@ -165,29 +141,29 @@ struct tunable_toset_t
> {
> tunable_t *t;
> const char *value;
> + size_t len;
> };
>
> enum { tunables_list_size = array_length (tunable_list) };
>
> /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
> - and their respective strings. VALSTRING is a duplicated values, where
> - delimiters ':' are replaced with '\0', so string tunables are null
> - terminated.
> + and their respective values. The VALSTRING is parsed in place, with the
> + tunable start and size recorded in TUNABLES.
> Return the number of tunables found (including 0 if the string is empty)
> or -1 if for an ill-formatted definition. */
> static int
> -parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
> +parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
> {
> if (valstring == NULL || *valstring == '\0')
> return 0;
>
> - char *p = valstring;
> + const char *p = valstring;
> bool done = false;
> int ntunables = 0;
>
> while (!done)
> {
> - char *name = p;
> + const char *name = p;
>
> /* First, find where the name ends. */
> while (*p != '=' && *p != ':' && *p != '\0')
> @@ -209,7 +185,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
> /* Skip the '='. */
> p++;
>
> - char *value = p;
> + const char *value = p;
>
> while (*p != '=' && *p != ':' && *p != '\0')
> p++;
> @@ -218,8 +194,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
> return -1;
> else if (*p == '\0')
> done = true;
> - else
> - *p++ = '\0';
>
> /* Add the tunable if it exists. */
> for (size_t i = 0; i < tunables_list_size; i++)
> @@ -228,7 +202,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
>
> if (tunable_is_name (cur->name, name))
> {
> - tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
> + tunables[ntunables++] =
> + (struct tunable_toset_t) { cur, value, p - value };
> break;
> }
> }
> @@ -238,7 +213,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
> }
>
> static void
> -parse_tunables (char *valstring)
> +parse_tunables (const char *valstring)
> {
> struct tunable_toset_t tunables[tunables_list_size];
> int ntunables = parse_tunables_string (valstring, tunables);
> @@ -250,7 +225,7 @@ parse_tunables (char *valstring)
> }
>
> for (int i = 0; i < ntunables; i++)
> - tunable_initialize (tunables[i].t, tunables[i].value);
> + tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
> }
>
> /* Initialize the tunables list from the environment. For now we only use the
> @@ -261,19 +236,20 @@ __tunables_init (char **envp)
> {
> char *envname = NULL;
> char *envval = NULL;
> - size_t len = 0;
> char **prev_envp = envp;
>
> /* Ignore tunables for AT_SECURE programs. */
> if (__libc_enable_secure)
> return;
>
> - while ((envp = get_next_env (envp, &envname, &len, &envval,
> - &prev_envp)) != NULL)
> + while ((envp = get_next_env (envp, &envname, &envval, &prev_envp)) != NULL)
> {
> + /* The environment variable is allocated on the stack by the kernel, so
> + it is safe to keep the references to the suboptions for later parsing
> + of string tunables. */
> if (tunable_is_name ("GLIBC_TUNABLES", envname))
> {
> - parse_tunables (tunables_strdup (envval));
> + parse_tunables (envval);
> continue;
> }
>
> @@ -291,7 +267,11 @@ __tunables_init (char **envp)
> /* We have a match. Initialize and move on to the next line. */
> if (tunable_is_name (name, envname))
> {
> - tunable_initialize (cur, envval);
> + size_t envvallen = 0;
> + /* The environment variable is always null-terminated. */
> + for (const char *p = envval; *p != '\0'; p++, envvallen++);
> +
> + tunable_initialize (cur, envval, envvallen);
> break;
> }
> }
> @@ -305,7 +285,7 @@ __tunables_print (void)
> {
> const tunable_t *cur = &tunable_list[i];
> if (cur->type.type_code == TUNABLE_TYPE_STRING
> - && cur->val.strval == NULL)
> + && cur->val.strval.str == NULL)
> _dl_printf ("%s:\n", cur->name);
> else
> {
> @@ -331,7 +311,9 @@ __tunables_print (void)
> (size_t) cur->type.max);
> break;
> case TUNABLE_TYPE_STRING:
> - _dl_printf ("%s\n", cur->val.strval);
> + _dl_printf ("%.*s\n",
> + (int) cur->val.strval.len,
> + cur->val.strval.str);
> break;
> default:
> __builtin_unreachable ();
> @@ -364,7 +346,7 @@ __tunable_get_default (tunable_id_t id, void *valp)
> }
> case TUNABLE_TYPE_STRING:
> {
> - *((const char **)valp) = cur->def.strval;
> + *((const struct tunable_str_t **)valp) = &cur->def.strval;
> break;
> }
> default:
> @@ -399,7 +381,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
> }
> case TUNABLE_TYPE_STRING:
> {
> - *((const char **)valp) = cur->val.strval;
> + *((const struct tunable_str_t **) valp) = &cur->val.strval;
> break;
> }
> default:
> diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
> index 0df4dde24e..5d5ee2c3aa 100644
> --- a/elf/dl-tunables.h
> +++ b/elf/dl-tunables.h
> @@ -30,7 +30,11 @@ typedef intmax_t tunable_num_t;
> typedef union
> {
> tunable_num_t numval;
> - const char *strval;
> + struct tunable_str_t
> + {
> + const char *str;
> + size_t len;
> + } strval;
> } tunable_val_t;
>
> typedef void (*tunable_callback_t) (tunable_val_t *);
> diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
> index e1ad44f27c..188345b070 100644
> --- a/elf/tst-tunables.c
> +++ b/elf/tst-tunables.c
> @@ -31,7 +31,8 @@ static int restart;
>
> static const struct test_t
> {
> - const char *env;
> + const char *name;
> + const char *value;
> int32_t expected_malloc_check;
> size_t expected_mmap_threshold;
> int32_t expected_perturb;
> @@ -39,12 +40,14 @@ static const struct test_t
> {
> /* Expected tunable format. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2",
> 2,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> 2,
> 4096,
> @@ -52,6 +55,7 @@ static const struct test_t
> },
> /* Empty tunable are ignored. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2::glibc.malloc.mmap_threshold=4096",
> 2,
> 4096,
> @@ -59,6 +63,7 @@ static const struct test_t
> },
> /* As well empty values. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=:glibc.malloc.mmap_threshold=4096",
> 0,
> 4096,
> @@ -66,18 +71,21 @@ static const struct test_t
> },
> /* Tunable are processed from left to right, so last one is the one set. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=1:glibc.malloc.check=2",
> 2,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=1:glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> 2,
> 4096,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096:glibc.malloc.check=1",
> 1,
> 4096,
> @@ -85,12 +93,14 @@ static const struct test_t
> },
> /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.perturb=0x800",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.perturb=0x55",
> 0,
> 0,
> @@ -98,6 +108,7 @@ static const struct test_t
> },
> /* Out of range values are just ignored. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.perturb=0x800:glibc.malloc.mmap_threshold=4096",
> 0,
> 4096,
> @@ -105,24 +116,28 @@ static const struct test_t
> },
> /* Invalid keys are ignored. */
> {
> + "GLIBC_TUNABLES",
> ":glibc.malloc.garbage=2:glibc.malloc.check=1",
> 1,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.perturb=0x800:not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> 0,
> 4096,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.not_valid.check=2:glibc.malloc.mmap_threshold=4096",
> 0,
> 4096,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "not_valid.malloc.check=2:glibc.malloc.mmap_threshold=4096",
> 0,
> 4096,
> @@ -130,24 +145,28 @@ static const struct test_t
> },
> /* Invalid subkeys are ignored. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096:glibc.malloc.check=2",
> 2,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=4:glibc.malloc.garbage=2:glibc.maoc.mmap_threshold=4096",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "not_valid.malloc.check=2",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.not_valid.check=2",
> 0,
> 0,
> @@ -156,6 +175,7 @@ static const struct test_t
> /* An ill-formatted tunable in the for key=key=value will considere the
> value as 'key=value' (which can not be parsed as an integer). */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096",
> 0,
> 0,
> @@ -163,41 +183,77 @@ static const struct test_t
> },
> /* Ill-formatted tunables string is not parsed. */
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.mmap_threshold=glibc.malloc.mmap_threshold=4096:glibc.malloc.check=2",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2=2",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2=2:glibc.malloc.mmap_threshold=4096",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2=2:glibc.malloc.check=2",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
> 0,
> 0,
> 0,
> },
> {
> + "GLIBC_TUNABLES",
> "glibc.malloc.check=2:glibc.malloc.mmap_threshold=4096=4096",
> 0,
> 0,
> 0,
> },
> + /* Also check some tunable aliases. */
> + {
> + "MALLOC_CHECK_",
> + "2",
> + 2,
> + 0,
> + 0,
> + },
> + {
> + "MALLOC_MMAP_THRESHOLD_",
> + "4096",
> + 0,
> + 4096,
> + 0,
> + },
> + {
> + "MALLOC_PERTURB_",
> + "0x55",
> + 0,
> + 0,
> + 0x55,
> + },
> + /* 0x800 is larger than tunable maxval (0xff), so the tunable is unchanged. */
> + {
> + "MALLOC_PERTURB_",
> + "0x800",
> + 0,
> + 0,
> + 0,
> + },
> };
>
> static int
> @@ -245,13 +301,17 @@ do_test (int argc, char *argv[])
> {
> snprintf (nteststr, sizeof nteststr, "%d", i);
>
> - printf ("[%d] Spawned test for %s\n", i, tests[i].env);
> - setenv ("GLIBC_TUNABLES", tests[i].env, 1);
> + printf ("[%d] Spawned test for %s=%s\n",
> + i,
> + tests[i].name,
> + tests[i].value);
> + setenv (tests[i].name, tests[i].value, 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);
> + unsetenv (tests[i].name);
> }
>
> return 0;
> 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 917c26f116..a64e5f002a 100644
> --- a/sysdeps/x86/Makefile
> +++ b/sysdeps/x86/Makefile
> @@ -12,7 +12,8 @@ CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
>
> tests += tst-get-cpu-features tst-get-cpu-features-static \
> tst-cpu-features-cpuinfo tst-cpu-features-cpuinfo-static \
> - tst-cpu-features-supports tst-cpu-features-supports-static
> + tst-cpu-features-supports tst-cpu-features-supports-static \
> + tst-hwcap-tunables
> tests-static += tst-get-cpu-features-static \
> tst-cpu-features-cpuinfo-static \
> tst-cpu-features-supports-static
> @@ -65,6 +66,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>
More information about the Libc-alpha
mailing list