[v7] PowerPC: Influence cpu/arch hwcap features via GLIBC_TUNABLES.

Message ID 20230713141513.349437-1-bmahi496@linux.ibm.com
State Committed
Headers
Series [v7] PowerPC: Influence cpu/arch hwcap features via GLIBC_TUNABLES. |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed

Commit Message

develop--- via Libc-alpha July 13, 2023, 2:15 p.m. UTC
  From: Mahesh Bodapati <mahesh.bodapati@ibm.com>

This patch enables the option to influence hwcaps used by PowerPC.
The environment variable, GLIBC_TUNABLES=glibc.cpu.hwcaps=-xxx,yyy,-zzz....,
can be used to enable CPU/ARCH feature yyy, disable CPU/ARCH feature xxx
and zzz, where the feature name is case-sensitive and has to match the ones
mentioned in the file{sysdeps/powerpc/dl-procinfo.c}.

Note that the hwcap tunables only used in the IFUNC selection.
---
 manual/tunables.texi                          |   5 +-
 sysdeps/powerpc/cpu-features.c                |  32 -----
 sysdeps/powerpc/cpu-features.h                |  28 ----
 sysdeps/powerpc/dl-tunables.list              |   3 +
 sysdeps/powerpc/hwcapinfo.c                   |   4 +
 .../power4/multiarch/ifunc-impl-list.c        |   4 +-
 .../powerpc32/power4/multiarch/init-arch.h    |  10 +-
 sysdeps/powerpc/powerpc64/dl-machine.h        |   2 -
 .../powerpc64/multiarch/ifunc-impl-list.c     |   7 +-
 sysdeps/unix/sysv/linux/powerpc/Makefile      |  11 +-
 .../unix/sysv/linux/powerpc/cpu-features.c    | 124 +++++++++++++++++
 .../unix/sysv/linux/powerpc/cpu-features.h    | 130 ++++++++++++++++++
 .../sysv/linux/powerpc/tst-hwcap-tunables.c   | 126 +++++++++++++++++
 13 files changed, 412 insertions(+), 74 deletions(-)
 delete mode 100644 sysdeps/powerpc/cpu-features.c
 delete mode 100644 sysdeps/powerpc/cpu-features.h
 create mode 100644 sysdeps/unix/sysv/linux/powerpc/cpu-features.c
 create mode 100644 sysdeps/unix/sysv/linux/powerpc/cpu-features.h
 create mode 100644 sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
  

Comments

Adhemerval Zanella July 14, 2023, 12:54 p.m. UTC | #1
On 13/07/23 11:15, bmahi496@linux.ibm.com wrote:
> From: Mahesh Bodapati <mahesh.bodapati@ibm.com>
> 
> This patch enables the option to influence hwcaps used by PowerPC.
> The environment variable, GLIBC_TUNABLES=glibc.cpu.hwcaps=-xxx,yyy,-zzz....,
> can be used to enable CPU/ARCH feature yyy, disable CPU/ARCH feature xxx
> and zzz, where the feature name is case-sensitive and has to match the ones
> mentioned in the file{sysdeps/powerpc/dl-procinfo.c}.
> 
> Note that the hwcap tunables only used in the IFUNC selection.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  manual/tunables.texi                          |   5 +-
>  sysdeps/powerpc/cpu-features.c                |  32 -----
>  sysdeps/powerpc/cpu-features.h                |  28 ----
>  sysdeps/powerpc/dl-tunables.list              |   3 +
>  sysdeps/powerpc/hwcapinfo.c                   |   4 +
>  .../power4/multiarch/ifunc-impl-list.c        |   4 +-
>  .../powerpc32/power4/multiarch/init-arch.h    |  10 +-
>  sysdeps/powerpc/powerpc64/dl-machine.h        |   2 -
>  .../powerpc64/multiarch/ifunc-impl-list.c     |   7 +-
>  sysdeps/unix/sysv/linux/powerpc/Makefile      |  11 +-
>  .../unix/sysv/linux/powerpc/cpu-features.c    | 124 +++++++++++++++++
>  .../unix/sysv/linux/powerpc/cpu-features.h    | 130 ++++++++++++++++++
>  .../sysv/linux/powerpc/tst-hwcap-tunables.c   | 126 +++++++++++++++++
>  13 files changed, 412 insertions(+), 74 deletions(-)
>  delete mode 100644 sysdeps/powerpc/cpu-features.c
>  delete mode 100644 sysdeps/powerpc/cpu-features.h
>  create mode 100644 sysdeps/unix/sysv/linux/powerpc/cpu-features.c
>  create mode 100644 sysdeps/unix/sysv/linux/powerpc/cpu-features.h
>  create mode 100644 sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> 
> diff --git a/manual/tunables.texi b/manual/tunables.texi
> index 4ca0e42a11..776fd93fd9 100644
> --- a/manual/tunables.texi
> +++ b/manual/tunables.texi
> @@ -513,7 +513,10 @@ On s390x, the supported HWCAP and STFLE features can be found in
>  @code{sysdeps/s390/cpu-features.c}.  In addition the user can also set
>  a CPU arch-level like @code{z13} instead of single HWCAP and STFLE features.
>  
> -This tunable is specific to i386, x86-64 and s390x.
> +On powerpc, the supported HWCAP and HWCAP2 features can be found in
> +@code{sysdeps/powerpc/dl-procinfo.c}.
> +
> +This tunable is specific to i386, x86-64, s390x and powerpc.
>  @end deftp
>  
>  @deftp Tunable glibc.cpu.cached_memopt
> diff --git a/sysdeps/powerpc/cpu-features.c b/sysdeps/powerpc/cpu-features.c
> deleted file mode 100644
> index 0ef3cf89d2..0000000000
> --- a/sysdeps/powerpc/cpu-features.c
> +++ /dev/null
> @@ -1,32 +0,0 @@
> -/* Initialize cpu feature data.  PowerPC version.
> -   Copyright (C) 2017-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/>.  */
> -
> -#include <stdint.h>
> -#include <cpu-features.h>
> -#include <elf/dl-tunables.h>
> -
> -static inline void
> -init_cpu_features (struct cpu_features *cpu_features)
> -{
> -  /* Default is to use aligned memory access on optimized function unless
> -     tunables is enable, since for this case user can explicit disable
> -     unaligned optimizations.  */
> -  int32_t cached_memfunc = TUNABLE_GET (glibc, cpu, cached_memopt, int32_t,
> -					NULL);
> -  cpu_features->use_cached_memopt = (cached_memfunc > 0);
> -}
> diff --git a/sysdeps/powerpc/cpu-features.h b/sysdeps/powerpc/cpu-features.h
> deleted file mode 100644
> index d316dc3d64..0000000000
> --- a/sysdeps/powerpc/cpu-features.h
> +++ /dev/null
> @@ -1,28 +0,0 @@
> -/* Initialize cpu feature data.  PowerPC version.
> -   Copyright (C) 2017-2023 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
> -   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 __CPU_FEATURES_POWERPC_H
> -# define __CPU_FEATURES_POWERPC_H
> -
> -#include <stdbool.h>
> -
> -struct cpu_features
> -{
> -  bool use_cached_memopt;
> -};
> -
> -#endif /* __CPU_FEATURES_H  */
> diff --git a/sysdeps/powerpc/dl-tunables.list b/sysdeps/powerpc/dl-tunables.list
> index 87d6235c75..807b7f8013 100644
> --- a/sysdeps/powerpc/dl-tunables.list
> +++ b/sysdeps/powerpc/dl-tunables.list
> @@ -24,5 +24,8 @@ glibc {
>        maxval: 1
>        default: 0
>      }
> +    hwcaps {
> +      type: STRING
> +    }
>    }
>  }
> diff --git a/sysdeps/powerpc/hwcapinfo.c b/sysdeps/powerpc/hwcapinfo.c
> index e26e64d99e..f2c473c556 100644
> --- a/sysdeps/powerpc/hwcapinfo.c
> +++ b/sysdeps/powerpc/hwcapinfo.c
> @@ -19,6 +19,7 @@
>  #include <unistd.h>
>  #include <shlib-compat.h>
>  #include <dl-procinfo.h>
> +#include <cpu-features.c>
>  
>  tcbhead_t __tcb __attribute__ ((visibility ("hidden")));
>  
> @@ -63,6 +64,9 @@ __tcb_parse_hwcap_and_convert_at_platform (void)
>    else if (h1 & PPC_FEATURE_POWER5)
>      h1 |= PPC_FEATURE_POWER4;
>  
> +  uint64_t array_hwcaps[] = { h1, h2 };
> +  init_cpu_features (&GLRO(dl_powerpc_cpu_features), array_hwcaps);
> +
>    /* Consolidate both HWCAP and HWCAP2 into a single doubleword so that
>       we can read both in a single load later.  */
>    __tcb.hwcap = (h1 << 32) | (h2 & 0xffffffff);
> diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
> index b4f80539e7..986c37d71e 100644
> --- a/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
> +++ b/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
> @@ -21,6 +21,7 @@
>  #include <wchar.h>
>  #include <ldsodefs.h>
>  #include <ifunc-impl-list.h>
> +#include <cpu-features.h>
>  
>  size_t
>  __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
> @@ -28,7 +29,8 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
>  {
>    size_t i = max;
>  
> -  unsigned long int hwcap = GLRO(dl_hwcap);
> +  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);
> +  unsigned long int hwcap = features->hwcap;
>    /* hwcap contains only the latest supported ISA, the code checks which is
>       and fills the previous supported ones.  */
>    if (hwcap & PPC_FEATURE_ARCH_2_06)
> diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> index 3dd00e02ee..4f075d37fd 100644
> --- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> +++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
> @@ -16,6 +16,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <ldsodefs.h>
> +#include <cpu-features.h>
>  
>  /* The code checks if _rtld_global_ro was realocated before trying to access
>     the dl_hwcap field. The assembly is to make the compiler not optimize the
> @@ -32,11 +33,12 @@
>  # define __GLRO(value)  GLRO(value)
>  #endif
>  
> -/* dl_hwcap contains only the latest supported ISA, the macro checks which is
> -   and fills the previous ones.  */
> +/* Get the hardware information post the tunables set, the macro checks
> +   it and fills the previous ones.  */
>  #define INIT_ARCH() \
> -  unsigned long int hwcap = __GLRO(dl_hwcap); 			\
> -  unsigned long int __attribute__((unused)) hwcap2 = __GLRO(dl_hwcap2); \
> +  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
> +  unsigned long int hwcap = features->hwcap;				\
> +  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
>    bool __attribute__((unused)) use_cached_memopt =		\
>      __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
>    if (hwcap & PPC_FEATURE_ARCH_2_06)				\
> diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
> index 9b8943bc91..449208e86f 100644
> --- a/sysdeps/powerpc/powerpc64/dl-machine.h
> +++ b/sysdeps/powerpc/powerpc64/dl-machine.h
> @@ -27,7 +27,6 @@
>  #include <dl-tls.h>
>  #include <sysdep.h>
>  #include <hwcapinfo.h>
> -#include <cpu-features.c>
>  #include <dl-static-tls.h>
>  #include <dl-funcdesc.h>
>  #include <dl-machine-rel.h>
> @@ -297,7 +296,6 @@ static inline void __attribute__ ((unused))
>  dl_platform_init (void)
>  {
>    __tcb_parse_hwcap_and_convert_at_platform ();
> -  init_cpu_features (&GLRO(dl_powerpc_cpu_features));
>  }
>  #endif
>  
> diff --git a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
> index ebe9434052..fc26dd0e17 100644
> --- a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
> +++ b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
> @@ -17,6 +17,7 @@
>     <https://www.gnu.org/licenses/>.  */
>  
>  #include <assert.h>
> +#include <cpu-features.h>
>  #include <string.h>
>  #include <wchar.h>
>  #include <ldsodefs.h>
> @@ -27,9 +28,9 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
>  			size_t max)
>  {
>    size_t i = max;
> -
> -  unsigned long int hwcap = GLRO(dl_hwcap);
> -  unsigned long int hwcap2 = GLRO(dl_hwcap2);
> +  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);
> +  unsigned long int hwcap = features->hwcap;
> +  unsigned long int hwcap2 = features->hwcap2;
>  #ifdef SHARED
>    int cacheline_size = GLRO(dl_cache_line_size);
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/powerpc/Makefile b/sysdeps/unix/sysv/linux/powerpc/Makefile
> index 93783cae00..24827efe79 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/Makefile
> +++ b/sysdeps/unix/sysv/linux/powerpc/Makefile
> @@ -23,9 +23,14 @@ ifeq ($(subdir),misc)
>  sysdep_headers += bits/ppc.h
>  sysdep_routines += get_timebase_freq
>  tests-static += test-gettimebasefreq-static
> -tests += $(tests-static)
> -tests += test-gettimebasefreq
> -tests += test-powerpc-linux-sysconf
> +tests += \
> +  $(tests-static) \
> +  test-gettimebasefreq \
> +  test-powerpc-linux-sysconf \
> +  tst-hwcap-tunables \
> +  # tests
> +
> +tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd)
>  endif
>  
>  ifeq ($(subdir),csu)
> diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
> new file mode 100644
> index 0000000000..7c6e20e702
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
> @@ -0,0 +1,124 @@
> +/* Initialize cpu feature data.  PowerPC version.
> +   Copyright (C) 2017-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/>.  */
> +
> +#include <array_length.h>
> +#include <stdint.h>
> +#include <cpu-features.h>
> +#include <elf/dl-tunables.h>
> +#include <unistd.h>
> +#include <string.h>
> +
> +static void
> +TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
> +{
> +  /* The current IFUNC selection is always using the most recent
> +     features which are available via AT_HWCAP or AT_HWCAP2.  But in
> +     some scenarios it is useful to adjust this selection.
> +
> +     The environment variable:
> +
> +     GLIBC_TUNABLES=glibc.cpu.hwcaps=-xxx,yyy,....
> +
> +     Can be used to enable HWCAP/HWCAP2 feature yyy, disable HWCAP/HWCAP2
> +     feature xxx, where the feature name is case-sensitive and has to match
> +     the ones mentioned in the file{sysdeps/powerpc/dl-procinfo.c}. */
> +
> +  /* Copy the features from dl_powerpc_cpu_features, which contains the
> +     features provided by AT_HWCAP and AT_HWCAP2.  */
> +  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
> +    {
> +      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;
> +
> +      /* 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)
> +	{
> +	  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)
> +	    {
> +	      /* Update the hwcap and hwcap2 bits.  */
> +	      if (disable)
> +		{
> +		  /* Id is 1 for hwcap2 tunable.  */
> +		  if (hwcap_tunables[i].id)
> +		    cpu_features->hwcap2 &= ~(hwcap_tunables[i].mask);
> +		  else
> +		    cpu_features->hwcap &= ~(hwcap_tunables[i].mask);
> +		}
> +	      else
> +		{
> +		  /* Enable the features and also check that no unsupported
> +		     features were enabled by user.  */
> +		  if (hwcap_tunables[i].id)
> +		    cpu_features->hwcap2 |= (tcbv_hwcap2 & hwcap_tunables[i].mask);
> +		  else
> +		    cpu_features->hwcap |= (tcbv_hwcap & hwcap_tunables[i].mask);
> +		}
> +	      break;
> +	    }
> +	  offset += hwcap_name_len + 1;
> +	}
> +	token += token_len;
> +	/* ... and skip token separator for next round.  */
> +	if (*token == ',')
> +	  token++;
> +    }
> +  while (*token != '\0');
> +}
> +
> +static inline void
> +init_cpu_features (struct cpu_features *cpu_features, uint64_t hwcaps[])
> +{
> +  /* Fill the cpu_features with the supported hwcaps
> +     which are set by __tcb_parse_hwcap_and_convert_at_platform.  */
> +  cpu_features->hwcap = hwcaps[0];
> +  cpu_features->hwcap2 = hwcaps[1];
> +  /* Default is to use aligned memory access on optimized function unless
> +     tunables is enable, since for this case user can explicit disable
> +     unaligned optimizations.  */
> +  int32_t cached_memfunc = TUNABLE_GET (glibc, cpu, cached_memopt, int32_t,
> +					NULL);
> +  cpu_features->use_cached_memopt = (cached_memfunc > 0);
> +  TUNABLE_GET (glibc, cpu, hwcaps, tunable_val_t *,
> +	       TUNABLE_CALLBACK (set_hwcaps));
> +}
> diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.h b/sysdeps/unix/sysv/linux/powerpc/cpu-features.h
> new file mode 100644
> index 0000000000..e5fce88e5e
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.h
> @@ -0,0 +1,130 @@
> +/* Initialize cpu feature data.  PowerPC version.
> +   Copyright (C) 2017-2023 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
> +   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 __CPU_FEATURES_POWERPC_H
> +# define __CPU_FEATURES_POWERPC_H
> +
> +#include <stdbool.h>
> +#include <sys/auxv.h>
> +
> +struct cpu_features
> +{
> +  bool use_cached_memopt;
> +  unsigned long int hwcap;
> +  unsigned long int hwcap2;
> +};
> +
> +static const char hwcap_names[] = {
> +  "4xxmac\0"
> +  "altivec\0"
> +  "arch_2_05\0"
> +  "arch_2_06\0"
> +  "archpmu\0"
> +  "booke\0"
> +  "cellbe\0"
> +  "dfp\0"
> +  "efpdouble\0"
> +  "efpsingle\0"
> +  "fpu\0"
> +  "ic_snoop\0"
> +  "mmu\0"
> +  "notb\0"
> +  "pa6t\0"
> +  "power4\0"
> +  "power5\0"
> +  "power5+\0"
> +  "power6x\0"
> +  "ppc32\0"
> +  "ppc601\0"
> +  "ppc64\0"
> +  "ppcle\0"
> +  "smt\0"
> +  "spe\0"
> +  "true_le\0"
> +  "ucache\0"
> +  "vsx\0"
> +  "arch_2_07\0"
> +  "dscr\0"
> +  "ebb\0"
> +  "htm\0"
> +  "htm-nosc\0"
> +  "htm-no-suspend\0"
> +  "isel\0"
> +  "tar\0"
> +  "vcrypto\0"
> +  "arch_3_00\0"
> +  "ieee128\0"
> +  "darn\0"
> +  "scv\0"
> +  "arch_3_1\0"
> +  "mma\0"
> +};
> +
> +static const struct
> +{
> +  unsigned int mask;
> +  bool id;
> +} hwcap_tunables[] = {
> +   /* AT_HWCAP tunable masks.  */
> +   { PPC_FEATURE_HAS_4xxMAC,                 0 },
> +   { PPC_FEATURE_HAS_ALTIVEC,                0 },
> +   { PPC_FEATURE_ARCH_2_05,                  0 },
> +   { PPC_FEATURE_ARCH_2_06,                  0 },
> +   { PPC_FEATURE_PSERIES_PERFMON_COMPAT,     0 },
> +   { PPC_FEATURE_BOOKE,                      0 },
> +   { PPC_FEATURE_CELL_BE,                    0 },
> +   { PPC_FEATURE_HAS_DFP,                    0 },
> +   { PPC_FEATURE_HAS_EFP_DOUBLE,             0 },
> +   { PPC_FEATURE_HAS_EFP_SINGLE,             0 },
> +   { PPC_FEATURE_HAS_FPU,                    0 },
> +   { PPC_FEATURE_ICACHE_SNOOP,               0 },
> +   { PPC_FEATURE_HAS_MMU,                    0 },
> +   { PPC_FEATURE_NO_TB,                      0 },
> +   { PPC_FEATURE_PA6T,                       0 },
> +   { PPC_FEATURE_POWER4,                     0 },
> +   { PPC_FEATURE_POWER5,                     0 },
> +   { PPC_FEATURE_POWER5_PLUS,                0 },
> +   { PPC_FEATURE_POWER6_EXT,                 0 },
> +   { PPC_FEATURE_32,                         0 },
> +   { PPC_FEATURE_601_INSTR,                  0 },
> +   { PPC_FEATURE_64,                         0 },
> +   { PPC_FEATURE_PPC_LE,                     0 },
> +   { PPC_FEATURE_SMT,                        0 },
> +   { PPC_FEATURE_HAS_SPE,                    0 },
> +   { PPC_FEATURE_TRUE_LE,                    0 },
> +   { PPC_FEATURE_UNIFIED_CACHE,              0 },
> +   { PPC_FEATURE_HAS_VSX,                    0 },
> +
> +   /* AT_HWCAP2 tunable masks.  */
> +   { PPC_FEATURE2_ARCH_2_07,                 1 },
> +   { PPC_FEATURE2_HAS_DSCR,                  1 },
> +   { PPC_FEATURE2_HAS_EBB,                   1 },
> +   { PPC_FEATURE2_HAS_HTM,                   1 },
> +   { PPC_FEATURE2_HTM_NOSC,                  1 },
> +   { PPC_FEATURE2_HTM_NO_SUSPEND,            1 },
> +   { PPC_FEATURE2_HAS_ISEL,                  1 },
> +   { PPC_FEATURE2_HAS_TAR,                   1 },
> +   { PPC_FEATURE2_HAS_VEC_CRYPTO,            1 },
> +   { PPC_FEATURE2_ARCH_3_00,                 1 },
> +   { PPC_FEATURE2_HAS_IEEE128,               1 },
> +   { PPC_FEATURE2_DARN,                      1 },
> +   { PPC_FEATURE2_SCV,                       1 },
> +   { PPC_FEATURE2_ARCH_3_1,                  1 },
> +   { PPC_FEATURE2_MMA,                       1 },
> +};
> +
> +#endif /* __CPU_FEATURES_H  */
> diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> new file mode 100644
> index 0000000000..4492722c5d
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> @@ -0,0 +1,126 @@
> +/* Tests for powerpc 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 <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include <sys/auxv.h>
> +#include <sys/wait.h>
> +
> +/* Nonzero if the program gets called via `exec'.  */
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },
> +static int restart;
> +
> +/* Hold the four initial argument used to respawn the process, plus the extra
> +   '--direct', '--restart', and the function to check  */
> +static char *spargs[8];
> +static int fc;
> +
> +/* Called on process re-execution.  */
> +_Noreturn static void
> +handle_restart (int argc, char *argv[])
> +{
> +  TEST_VERIFY_EXIT (argc == 1);
> +  const char *funcname = argv[0];
> +
> +  struct libc_ifunc_impl impls[32];
> +  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
> +  TEST_VERIFY_EXIT (cnt >= 1);
> +  for (int i = 0; i < cnt; i++) {
> +    if (strcmp (impls[i].name, funcname) == 0)
> +      {
> +	TEST_COMPARE (impls[i].usable, false);
> +	break;
> +      }
> +  }
> +
> +  _exit (EXIT_SUCCESS);
> +}
> +
> +static void
> +run_test (const char *filter, const char *funcname)
> +{
> +  printf ("info: checking filter %s (expect %s ifunc selection to be removed)\n",
> +	  filter, funcname);
> +  char *tunable = xasprintf ("GLIBC_TUNABLES=glibc.cpu.hwcaps=%s", filter);
> +  char *const newenvs[] = { (char*) tunable, NULL };
> +  spargs[fc] = (char *) funcname;
> +
> +  pid_t pid;
> +  TEST_COMPARE (posix_spawn (&pid, spargs[0], NULL, NULL, spargs, newenvs), 0);
> +  int status;
> +  TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
> +  TEST_VERIFY (WIFEXITED (status));
> +  TEST_VERIFY (!WIFSIGNALED (status));
> +  TEST_COMPARE (WEXITSTATUS (status), 0);
> +
> +  free (tunable);
> +}
> +
> +static int
> +do_test (int argc, char *argv[])
> +{
> +  if (restart)
> +    handle_restart (argc - 1, &argv[1]);
> +
> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
> +
> +  int i;
> +  for (i = 0; i < argc - 1; i++)
> +    spargs[i] = argv[i + 1];
> +  spargs[i++] = (char *) "--direct";
> +  spargs[i++] = (char *) "--restart";
> +  fc = i++;
> +  spargs[i] = NULL;
> +
> +  unsigned long int hwcap = getauxval (AT_HWCAP);
> +  unsigned long int hwcap2 = getauxval (AT_HWCAP2);
> +  if (__WORDSIZE == 64)
> +    {
> +      if (hwcap2 & PPC_FEATURE2_ARCH_3_1)
> +	run_test ("-arch_3_1", "__memcpy_power10");
> +      if (hwcap2 & PPC_FEATURE2_ARCH_2_07)
> +	run_test ("-arch_2_07", "__memcpy_power8_cached");
> +      if (hwcap & PPC_FEATURE_ARCH_2_06)
> +	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");
> +    }
> +  else
> +    {
> +      if (hwcap & PPC_FEATURE_HAS_VSX)
> +	run_test ("-vsx", "__memcpy_power7");
> +      if (hwcap & PPC_FEATURE_ARCH_2_06)
> +	run_test ("-arch_2_06", "__memcpy_a2");
> +      if (hwcap & PPC_FEATURE_ARCH_2_05)
> +	run_test ("-arch_2_05", "__memcpy_power6");
> +    }
> +  return 0;
> +}
> +
> +#define TEST_FUNCTION_ARGV do_test
> +#include <support/test-driver.c>
  
Adhemerval Zanella July 14, 2023, 2:49 p.m. UTC | #2
On 13/07/23 11:15, bmahi496@linux.ibm.com wrote:
> From: Mahesh Bodapati <mahesh.bodapati@ibm.com>

> diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> new file mode 100644
> index 0000000000..4492722c5d
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
> @@ -0,0 +1,126 @@
> +/* Tests for powerpc 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 <support/check.h>
> +#include <support/support.h>
> +#include <support/xunistd.h>
> +#include <sys/auxv.h>
> +#include <sys/wait.h>
> +
> +/* Nonzero if the program gets called via `exec'.  */
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },
> +static int restart;
> +
> +/* Hold the four initial argument used to respawn the process, plus the extra
> +   '--direct', '--restart', and the function to check  */
> +static char *spargs[8];
> +static int fc;
> +
> +/* Called on process re-execution.  */
> +_Noreturn static void
> +handle_restart (int argc, char *argv[])
> +{
> +  TEST_VERIFY_EXIT (argc == 1);
> +  const char *funcname = argv[0];
> +
> +  struct libc_ifunc_impl impls[32];
> +  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
> +  TEST_VERIFY_EXIT (cnt >= 1);

In fact it needs to just return if cnt == 0, since not all powerpc 32 bit
configuration adds ifunc variants (it is only enabled for power4 or
higher).
  
MAHESH BODAPATI July 17, 2023, 12:41 p.m. UTC | #3
On 14/07/23 8:19 pm, Adhemerval Zanella Netto wrote:
>
> On 13/07/23 11:15, bmahi496@linux.ibm.com wrote:
>> From: Mahesh Bodapati <mahesh.bodapati@ibm.com>
>> diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
>> new file mode 100644
>> index 0000000000..4492722c5d
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
>> @@ -0,0 +1,126 @@
>> +/* Tests for powerpc 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 <support/check.h>
>> +#include <support/support.h>
>> +#include <support/xunistd.h>
>> +#include <sys/auxv.h>
>> +#include <sys/wait.h>
>> +
>> +/* Nonzero if the program gets called via `exec'.  */
>> +#define CMDLINE_OPTIONS \
>> +  { "restart", no_argument, &restart, 1 },
>> +static int restart;
>> +
>> +/* Hold the four initial argument used to respawn the process, plus the extra
>> +   '--direct', '--restart', and the function to check  */
>> +static char *spargs[8];
>> +static int fc;
>> +
>> +/* Called on process re-execution.  */
>> +_Noreturn static void
>> +handle_restart (int argc, char *argv[])
>> +{
>> +  TEST_VERIFY_EXIT (argc == 1);
>> +  const char *funcname = argv[0];
>> +
>> +  struct libc_ifunc_impl impls[32];
>> +  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
>> +  TEST_VERIFY_EXIT (cnt >= 1);
> In fact it needs to just return if cnt == 0, since not all powerpc 32 bit
> configuration adds ifunc variants (it is only enabled for power4 or
> higher).

Thanks Adhemerval.
I am marking this test as pass if ifunc_impl_list is 0

+  if (cnt == 0)
+    _exit (EXIT_SUCCESS);
    TEST_VERIFY_EXIT (cnt >= 1);
  

Patch

diff --git a/manual/tunables.texi b/manual/tunables.texi
index 4ca0e42a11..776fd93fd9 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -513,7 +513,10 @@  On s390x, the supported HWCAP and STFLE features can be found in
 @code{sysdeps/s390/cpu-features.c}.  In addition the user can also set
 a CPU arch-level like @code{z13} instead of single HWCAP and STFLE features.
 
-This tunable is specific to i386, x86-64 and s390x.
+On powerpc, the supported HWCAP and HWCAP2 features can be found in
+@code{sysdeps/powerpc/dl-procinfo.c}.
+
+This tunable is specific to i386, x86-64, s390x and powerpc.
 @end deftp
 
 @deftp Tunable glibc.cpu.cached_memopt
diff --git a/sysdeps/powerpc/cpu-features.c b/sysdeps/powerpc/cpu-features.c
deleted file mode 100644
index 0ef3cf89d2..0000000000
--- a/sysdeps/powerpc/cpu-features.c
+++ /dev/null
@@ -1,32 +0,0 @@ 
-/* Initialize cpu feature data.  PowerPC version.
-   Copyright (C) 2017-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/>.  */
-
-#include <stdint.h>
-#include <cpu-features.h>
-#include <elf/dl-tunables.h>
-
-static inline void
-init_cpu_features (struct cpu_features *cpu_features)
-{
-  /* Default is to use aligned memory access on optimized function unless
-     tunables is enable, since for this case user can explicit disable
-     unaligned optimizations.  */
-  int32_t cached_memfunc = TUNABLE_GET (glibc, cpu, cached_memopt, int32_t,
-					NULL);
-  cpu_features->use_cached_memopt = (cached_memfunc > 0);
-}
diff --git a/sysdeps/powerpc/cpu-features.h b/sysdeps/powerpc/cpu-features.h
deleted file mode 100644
index d316dc3d64..0000000000
--- a/sysdeps/powerpc/cpu-features.h
+++ /dev/null
@@ -1,28 +0,0 @@ 
-/* Initialize cpu feature data.  PowerPC version.
-   Copyright (C) 2017-2023 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
-   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 __CPU_FEATURES_POWERPC_H
-# define __CPU_FEATURES_POWERPC_H
-
-#include <stdbool.h>
-
-struct cpu_features
-{
-  bool use_cached_memopt;
-};
-
-#endif /* __CPU_FEATURES_H  */
diff --git a/sysdeps/powerpc/dl-tunables.list b/sysdeps/powerpc/dl-tunables.list
index 87d6235c75..807b7f8013 100644
--- a/sysdeps/powerpc/dl-tunables.list
+++ b/sysdeps/powerpc/dl-tunables.list
@@ -24,5 +24,8 @@  glibc {
       maxval: 1
       default: 0
     }
+    hwcaps {
+      type: STRING
+    }
   }
 }
diff --git a/sysdeps/powerpc/hwcapinfo.c b/sysdeps/powerpc/hwcapinfo.c
index e26e64d99e..f2c473c556 100644
--- a/sysdeps/powerpc/hwcapinfo.c
+++ b/sysdeps/powerpc/hwcapinfo.c
@@ -19,6 +19,7 @@ 
 #include <unistd.h>
 #include <shlib-compat.h>
 #include <dl-procinfo.h>
+#include <cpu-features.c>
 
 tcbhead_t __tcb __attribute__ ((visibility ("hidden")));
 
@@ -63,6 +64,9 @@  __tcb_parse_hwcap_and_convert_at_platform (void)
   else if (h1 & PPC_FEATURE_POWER5)
     h1 |= PPC_FEATURE_POWER4;
 
+  uint64_t array_hwcaps[] = { h1, h2 };
+  init_cpu_features (&GLRO(dl_powerpc_cpu_features), array_hwcaps);
+
   /* Consolidate both HWCAP and HWCAP2 into a single doubleword so that
      we can read both in a single load later.  */
   __tcb.hwcap = (h1 << 32) | (h2 & 0xffffffff);
diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
index b4f80539e7..986c37d71e 100644
--- a/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
+++ b/sysdeps/powerpc/powerpc32/power4/multiarch/ifunc-impl-list.c
@@ -21,6 +21,7 @@ 
 #include <wchar.h>
 #include <ldsodefs.h>
 #include <ifunc-impl-list.h>
+#include <cpu-features.h>
 
 size_t
 __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
@@ -28,7 +29,8 @@  __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
 {
   size_t i = max;
 
-  unsigned long int hwcap = GLRO(dl_hwcap);
+  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);
+  unsigned long int hwcap = features->hwcap;
   /* hwcap contains only the latest supported ISA, the code checks which is
      and fills the previous supported ones.  */
   if (hwcap & PPC_FEATURE_ARCH_2_06)
diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
index 3dd00e02ee..4f075d37fd 100644
--- a/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
+++ b/sysdeps/powerpc/powerpc32/power4/multiarch/init-arch.h
@@ -16,6 +16,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <ldsodefs.h>
+#include <cpu-features.h>
 
 /* The code checks if _rtld_global_ro was realocated before trying to access
    the dl_hwcap field. The assembly is to make the compiler not optimize the
@@ -32,11 +33,12 @@ 
 # define __GLRO(value)  GLRO(value)
 #endif
 
-/* dl_hwcap contains only the latest supported ISA, the macro checks which is
-   and fills the previous ones.  */
+/* Get the hardware information post the tunables set, the macro checks
+   it and fills the previous ones.  */
 #define INIT_ARCH() \
-  unsigned long int hwcap = __GLRO(dl_hwcap); 			\
-  unsigned long int __attribute__((unused)) hwcap2 = __GLRO(dl_hwcap2); \
+  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);	\
+  unsigned long int hwcap = features->hwcap;				\
+  unsigned long int __attribute__((unused)) hwcap2 = features->hwcap2; \
   bool __attribute__((unused)) use_cached_memopt =		\
     __GLRO(dl_powerpc_cpu_features.use_cached_memopt);		\
   if (hwcap & PPC_FEATURE_ARCH_2_06)				\
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index 9b8943bc91..449208e86f 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -27,7 +27,6 @@ 
 #include <dl-tls.h>
 #include <sysdep.h>
 #include <hwcapinfo.h>
-#include <cpu-features.c>
 #include <dl-static-tls.h>
 #include <dl-funcdesc.h>
 #include <dl-machine-rel.h>
@@ -297,7 +296,6 @@  static inline void __attribute__ ((unused))
 dl_platform_init (void)
 {
   __tcb_parse_hwcap_and_convert_at_platform ();
-  init_cpu_features (&GLRO(dl_powerpc_cpu_features));
 }
 #endif
 
diff --git a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
index ebe9434052..fc26dd0e17 100644
--- a/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
+++ b/sysdeps/powerpc/powerpc64/multiarch/ifunc-impl-list.c
@@ -17,6 +17,7 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
+#include <cpu-features.h>
 #include <string.h>
 #include <wchar.h>
 #include <ldsodefs.h>
@@ -27,9 +28,9 @@  __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
 			size_t max)
 {
   size_t i = max;
-
-  unsigned long int hwcap = GLRO(dl_hwcap);
-  unsigned long int hwcap2 = GLRO(dl_hwcap2);
+  const struct cpu_features *features = &GLRO(dl_powerpc_cpu_features);
+  unsigned long int hwcap = features->hwcap;
+  unsigned long int hwcap2 = features->hwcap2;
 #ifdef SHARED
   int cacheline_size = GLRO(dl_cache_line_size);
 #endif
diff --git a/sysdeps/unix/sysv/linux/powerpc/Makefile b/sysdeps/unix/sysv/linux/powerpc/Makefile
index 93783cae00..24827efe79 100644
--- a/sysdeps/unix/sysv/linux/powerpc/Makefile
+++ b/sysdeps/unix/sysv/linux/powerpc/Makefile
@@ -23,9 +23,14 @@  ifeq ($(subdir),misc)
 sysdep_headers += bits/ppc.h
 sysdep_routines += get_timebase_freq
 tests-static += test-gettimebasefreq-static
-tests += $(tests-static)
-tests += test-gettimebasefreq
-tests += test-powerpc-linux-sysconf
+tests += \
+  $(tests-static) \
+  test-gettimebasefreq \
+  test-powerpc-linux-sysconf \
+  tst-hwcap-tunables \
+  # tests
+
+tst-hwcap-tunables-ARGS = -- $(host-test-program-cmd)
 endif
 
 ifeq ($(subdir),csu)
diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.c b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
new file mode 100644
index 0000000000..7c6e20e702
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.c
@@ -0,0 +1,124 @@ 
+/* Initialize cpu feature data.  PowerPC version.
+   Copyright (C) 2017-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/>.  */
+
+#include <array_length.h>
+#include <stdint.h>
+#include <cpu-features.h>
+#include <elf/dl-tunables.h>
+#include <unistd.h>
+#include <string.h>
+
+static void
+TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
+{
+  /* The current IFUNC selection is always using the most recent
+     features which are available via AT_HWCAP or AT_HWCAP2.  But in
+     some scenarios it is useful to adjust this selection.
+
+     The environment variable:
+
+     GLIBC_TUNABLES=glibc.cpu.hwcaps=-xxx,yyy,....
+
+     Can be used to enable HWCAP/HWCAP2 feature yyy, disable HWCAP/HWCAP2
+     feature xxx, where the feature name is case-sensitive and has to match
+     the ones mentioned in the file{sysdeps/powerpc/dl-procinfo.c}. */
+
+  /* Copy the features from dl_powerpc_cpu_features, which contains the
+     features provided by AT_HWCAP and AT_HWCAP2.  */
+  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
+    {
+      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;
+
+      /* 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)
+	{
+	  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)
+	    {
+	      /* Update the hwcap and hwcap2 bits.  */
+	      if (disable)
+		{
+		  /* Id is 1 for hwcap2 tunable.  */
+		  if (hwcap_tunables[i].id)
+		    cpu_features->hwcap2 &= ~(hwcap_tunables[i].mask);
+		  else
+		    cpu_features->hwcap &= ~(hwcap_tunables[i].mask);
+		}
+	      else
+		{
+		  /* Enable the features and also check that no unsupported
+		     features were enabled by user.  */
+		  if (hwcap_tunables[i].id)
+		    cpu_features->hwcap2 |= (tcbv_hwcap2 & hwcap_tunables[i].mask);
+		  else
+		    cpu_features->hwcap |= (tcbv_hwcap & hwcap_tunables[i].mask);
+		}
+	      break;
+	    }
+	  offset += hwcap_name_len + 1;
+	}
+	token += token_len;
+	/* ... and skip token separator for next round.  */
+	if (*token == ',')
+	  token++;
+    }
+  while (*token != '\0');
+}
+
+static inline void
+init_cpu_features (struct cpu_features *cpu_features, uint64_t hwcaps[])
+{
+  /* Fill the cpu_features with the supported hwcaps
+     which are set by __tcb_parse_hwcap_and_convert_at_platform.  */
+  cpu_features->hwcap = hwcaps[0];
+  cpu_features->hwcap2 = hwcaps[1];
+  /* Default is to use aligned memory access on optimized function unless
+     tunables is enable, since for this case user can explicit disable
+     unaligned optimizations.  */
+  int32_t cached_memfunc = TUNABLE_GET (glibc, cpu, cached_memopt, int32_t,
+					NULL);
+  cpu_features->use_cached_memopt = (cached_memfunc > 0);
+  TUNABLE_GET (glibc, cpu, hwcaps, tunable_val_t *,
+	       TUNABLE_CALLBACK (set_hwcaps));
+}
diff --git a/sysdeps/unix/sysv/linux/powerpc/cpu-features.h b/sysdeps/unix/sysv/linux/powerpc/cpu-features.h
new file mode 100644
index 0000000000..e5fce88e5e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/powerpc/cpu-features.h
@@ -0,0 +1,130 @@ 
+/* Initialize cpu feature data.  PowerPC version.
+   Copyright (C) 2017-2023 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
+   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 __CPU_FEATURES_POWERPC_H
+# define __CPU_FEATURES_POWERPC_H
+
+#include <stdbool.h>
+#include <sys/auxv.h>
+
+struct cpu_features
+{
+  bool use_cached_memopt;
+  unsigned long int hwcap;
+  unsigned long int hwcap2;
+};
+
+static const char hwcap_names[] = {
+  "4xxmac\0"
+  "altivec\0"
+  "arch_2_05\0"
+  "arch_2_06\0"
+  "archpmu\0"
+  "booke\0"
+  "cellbe\0"
+  "dfp\0"
+  "efpdouble\0"
+  "efpsingle\0"
+  "fpu\0"
+  "ic_snoop\0"
+  "mmu\0"
+  "notb\0"
+  "pa6t\0"
+  "power4\0"
+  "power5\0"
+  "power5+\0"
+  "power6x\0"
+  "ppc32\0"
+  "ppc601\0"
+  "ppc64\0"
+  "ppcle\0"
+  "smt\0"
+  "spe\0"
+  "true_le\0"
+  "ucache\0"
+  "vsx\0"
+  "arch_2_07\0"
+  "dscr\0"
+  "ebb\0"
+  "htm\0"
+  "htm-nosc\0"
+  "htm-no-suspend\0"
+  "isel\0"
+  "tar\0"
+  "vcrypto\0"
+  "arch_3_00\0"
+  "ieee128\0"
+  "darn\0"
+  "scv\0"
+  "arch_3_1\0"
+  "mma\0"
+};
+
+static const struct
+{
+  unsigned int mask;
+  bool id;
+} hwcap_tunables[] = {
+   /* AT_HWCAP tunable masks.  */
+   { PPC_FEATURE_HAS_4xxMAC,                 0 },
+   { PPC_FEATURE_HAS_ALTIVEC,                0 },
+   { PPC_FEATURE_ARCH_2_05,                  0 },
+   { PPC_FEATURE_ARCH_2_06,                  0 },
+   { PPC_FEATURE_PSERIES_PERFMON_COMPAT,     0 },
+   { PPC_FEATURE_BOOKE,                      0 },
+   { PPC_FEATURE_CELL_BE,                    0 },
+   { PPC_FEATURE_HAS_DFP,                    0 },
+   { PPC_FEATURE_HAS_EFP_DOUBLE,             0 },
+   { PPC_FEATURE_HAS_EFP_SINGLE,             0 },
+   { PPC_FEATURE_HAS_FPU,                    0 },
+   { PPC_FEATURE_ICACHE_SNOOP,               0 },
+   { PPC_FEATURE_HAS_MMU,                    0 },
+   { PPC_FEATURE_NO_TB,                      0 },
+   { PPC_FEATURE_PA6T,                       0 },
+   { PPC_FEATURE_POWER4,                     0 },
+   { PPC_FEATURE_POWER5,                     0 },
+   { PPC_FEATURE_POWER5_PLUS,                0 },
+   { PPC_FEATURE_POWER6_EXT,                 0 },
+   { PPC_FEATURE_32,                         0 },
+   { PPC_FEATURE_601_INSTR,                  0 },
+   { PPC_FEATURE_64,                         0 },
+   { PPC_FEATURE_PPC_LE,                     0 },
+   { PPC_FEATURE_SMT,                        0 },
+   { PPC_FEATURE_HAS_SPE,                    0 },
+   { PPC_FEATURE_TRUE_LE,                    0 },
+   { PPC_FEATURE_UNIFIED_CACHE,              0 },
+   { PPC_FEATURE_HAS_VSX,                    0 },
+
+   /* AT_HWCAP2 tunable masks.  */
+   { PPC_FEATURE2_ARCH_2_07,                 1 },
+   { PPC_FEATURE2_HAS_DSCR,                  1 },
+   { PPC_FEATURE2_HAS_EBB,                   1 },
+   { PPC_FEATURE2_HAS_HTM,                   1 },
+   { PPC_FEATURE2_HTM_NOSC,                  1 },
+   { PPC_FEATURE2_HTM_NO_SUSPEND,            1 },
+   { PPC_FEATURE2_HAS_ISEL,                  1 },
+   { PPC_FEATURE2_HAS_TAR,                   1 },
+   { PPC_FEATURE2_HAS_VEC_CRYPTO,            1 },
+   { PPC_FEATURE2_ARCH_3_00,                 1 },
+   { PPC_FEATURE2_HAS_IEEE128,               1 },
+   { PPC_FEATURE2_DARN,                      1 },
+   { PPC_FEATURE2_SCV,                       1 },
+   { PPC_FEATURE2_ARCH_3_1,                  1 },
+   { PPC_FEATURE2_MMA,                       1 },
+};
+
+#endif /* __CPU_FEATURES_H  */
diff --git a/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
new file mode 100644
index 0000000000..4492722c5d
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/powerpc/tst-hwcap-tunables.c
@@ -0,0 +1,126 @@ 
+/* Tests for powerpc 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 <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <sys/auxv.h>
+#include <sys/wait.h>
+
+/* Nonzero if the program gets called via `exec'.  */
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+static int restart;
+
+/* Hold the four initial argument used to respawn the process, plus the extra
+   '--direct', '--restart', and the function to check  */
+static char *spargs[8];
+static int fc;
+
+/* Called on process re-execution.  */
+_Noreturn static void
+handle_restart (int argc, char *argv[])
+{
+  TEST_VERIFY_EXIT (argc == 1);
+  const char *funcname = argv[0];
+
+  struct libc_ifunc_impl impls[32];
+  int cnt = __libc_ifunc_impl_list ("memcpy", impls, array_length (impls));
+  TEST_VERIFY_EXIT (cnt >= 1);
+  for (int i = 0; i < cnt; i++) {
+    if (strcmp (impls[i].name, funcname) == 0)
+      {
+	TEST_COMPARE (impls[i].usable, false);
+	break;
+      }
+  }
+
+  _exit (EXIT_SUCCESS);
+}
+
+static void
+run_test (const char *filter, const char *funcname)
+{
+  printf ("info: checking filter %s (expect %s ifunc selection to be removed)\n",
+	  filter, funcname);
+  char *tunable = xasprintf ("GLIBC_TUNABLES=glibc.cpu.hwcaps=%s", filter);
+  char *const newenvs[] = { (char*) tunable, NULL };
+  spargs[fc] = (char *) funcname;
+
+  pid_t pid;
+  TEST_COMPARE (posix_spawn (&pid, spargs[0], NULL, NULL, spargs, newenvs), 0);
+  int status;
+  TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
+  TEST_VERIFY (WIFEXITED (status));
+  TEST_VERIFY (!WIFSIGNALED (status));
+  TEST_COMPARE (WEXITSTATUS (status), 0);
+
+  free (tunable);
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  if (restart)
+    handle_restart (argc - 1, &argv[1]);
+
+  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
+
+  int i;
+  for (i = 0; i < argc - 1; i++)
+    spargs[i] = argv[i + 1];
+  spargs[i++] = (char *) "--direct";
+  spargs[i++] = (char *) "--restart";
+  fc = i++;
+  spargs[i] = NULL;
+
+  unsigned long int hwcap = getauxval (AT_HWCAP);
+  unsigned long int hwcap2 = getauxval (AT_HWCAP2);
+  if (__WORDSIZE == 64)
+    {
+      if (hwcap2 & PPC_FEATURE2_ARCH_3_1)
+	run_test ("-arch_3_1", "__memcpy_power10");
+      if (hwcap2 & PPC_FEATURE2_ARCH_2_07)
+	run_test ("-arch_2_07", "__memcpy_power8_cached");
+      if (hwcap & PPC_FEATURE_ARCH_2_06)
+	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");
+    }
+  else
+    {
+      if (hwcap & PPC_FEATURE_HAS_VSX)
+	run_test ("-vsx", "__memcpy_power7");
+      if (hwcap & PPC_FEATURE_ARCH_2_06)
+	run_test ("-arch_2_06", "__memcpy_a2");
+      if (hwcap & PPC_FEATURE_ARCH_2_05)
+	run_test ("-arch_2_05", "__memcpy_power6");
+    }
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>