tunables, aarch64: New tunable to override cpu

Message ID 1498159168-24635-1-git-send-email-siddhesh@sourceware.org
State New, archived
Headers

Commit Message

Siddhesh Poyarekar June 22, 2017, 7:19 p.m. UTC
  Add a new tunable (glibc.tune.cpu) to override CPU identification on
aarch64.  This is useful in two cases: one where it is desirable to
pretend to be another CPU for purposes of testing or because routines
written for that CPU are beneficial for specific workloads and second
where the underlying kernel does not support emulation of MRS to get
the MIDR of the CPU.

	* elf/dl-tunables.h (tunable_is_name): Move from...
	* elf/dl-tunables.c (is_name): ... here.
	(parse_tunables, __tunables_init): Adjust.
	* manual/tunables.texi: Document glibc.tune.cpu.
	* sysdeps/aarch64/dl-tunables.list: New file.
	* sysdeps/unix/sysv/linux/aarch64/cpu-features.c (struct
	cpu_list): New type.
	(cpu_list): New list of CPU names and their MIDR.
	(get_midr_from_mcpu): New function.
	(init_cpu_features): Override MIDR if necessary.
---
 elf/dl-tunables.c                              | 24 +++----------
 elf/dl-tunables.h                              | 17 +++++++++
 manual/tunables.texi                           |  8 +++++
 sysdeps/aarch64/dl-tunables.list               | 25 ++++++++++++++
 sysdeps/unix/sysv/linux/aarch64/cpu-features.c | 48 ++++++++++++++++++++++----
 5 files changed, 96 insertions(+), 26 deletions(-)
 create mode 100644 sysdeps/aarch64/dl-tunables.list
  

Comments

Szabolcs Nagy June 23, 2017, 2:01 p.m. UTC | #1
On 22/06/17 20:19, Siddhesh Poyarekar wrote:
> Add a new tunable (glibc.tune.cpu) to override CPU identification on
> aarch64.  This is useful in two cases: one where it is desirable to
> pretend to be another CPU for purposes of testing or because routines
> written for that CPU are beneficial for specific workloads and second
> where the underlying kernel does not support emulation of MRS to get
> the MIDR of the CPU.
> 
> 	* elf/dl-tunables.h (tunable_is_name): Move from...
> 	* elf/dl-tunables.c (is_name): ... here.
> 	(parse_tunables, __tunables_init): Adjust.
> 	* manual/tunables.texi: Document glibc.tune.cpu.
> 	* sysdeps/aarch64/dl-tunables.list: New file.
> 	* sysdeps/unix/sysv/linux/aarch64/cpu-features.c (struct
> 	cpu_list): New type.
> 	(cpu_list): New list of CPU names and their MIDR.
> 	(get_midr_from_mcpu): New function.
> 	(init_cpu_features): Override MIDR if necessary.

sounds good.

> +++ b/manual/tunables.texi
> @@ -231,6 +231,14 @@ the ones in @code{sysdeps/x86/cpu-features.h}.
>  This tunable is specific to i386 and x86-64.
>  @end deftp
>  
> +@deftp Tunable glibc.tune.cpu
> +The @code{glibc.tune.cpu=xxx} tunable allows the user to tell @theglibc{} to
> +assume that the CPU is @code{xxx} where xxx may have one of these values:
> +@code{generic}, @code{thunderx}, @code{falkor}.
> +
> +This tunable is specific to aarch64.
> +@end deftp

good, but this way we will have to update this list if glibc
handles some new cpu.

i'm not convinced yet that we need falkor specific memcpy,
in which case adding falkor here is not (yet) necessary.

> +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> @@ -20,18 +20,54 @@
>  #include <sys/auxv.h>
>  #include <elf/dl-hwcaps.h>
>  
> +#if HAVE_TUNABLES
> +struct cpu_list
> +{
> +  const char *name;
> +  uint64_t midr;
> +};
> +
> +static struct cpu_list cpu_list[] = {
> +      {"falkor",	0x510FC000},
> +      {"thunderx",	0x430F0A10},

note that this is not consistent with the gcc aarch64-cores.def
naming where this cpu is named thunderxt88, but it's fine if
this is what we want (i don't know if thunderx ppl will want to
distinguish further variants).

> +      {"generic", 	0x0}
> +};
> +
> +static uint64_t
> +get_midr_from_mcpu (const char *mcpu)
> +{
> +  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
> +    if (tunable_is_name (mcpu, cpu_list[i].name) == 0)
> +      return cpu_list[i].midr;
> +
> +  return UINT64_MAX;
> +}
> +#endif
> +
>  static inline void
>  init_cpu_features (struct cpu_features *cpu_features)
>  {
>    uint64_t hwcap_mask = GET_HWCAP_MASK();
>    uint64_t hwcap = GLRO (dl_hwcap) & hwcap_mask;
>  
> -  if (hwcap & HWCAP_CPUID)
> +  register uint64_t midr = UINT_MAX;

UINT_MAX vs UINT64_MAX above is inconsistent.

> +
> +#if HAVE_TUNABLES
> +  /* Get the tunable override.  */
> +  const char *mcpu = TUNABLE_GET (glibc, tune, mcpu, const char *, NULL);
> +  if (mcpu != NULL)
> +    midr = get_midr_from_mcpu (mcpu);
> +#endif
> +
> +  /* If there was no useful tunable override, query the MIDR if the kernel
> +     allows it.  */
> +  if (midr == UINT_MAX)
>      {
> -      register uint64_t id = 0;
> -      asm volatile ("mrs %0, midr_el1" : "=r"(id));
> -      cpu_features->midr_el1 = id;
> +      if (hwcap & HWCAP_CPUID)
> +	asm volatile ("mrs %0, midr_el1" : "=r"(midr));
> +      else
> +	midr = 0;
>      }
> -  else
> -    cpu_features->midr_el1 = 0;
> +
> +  cpu_features->midr_el1 = midr;
>  }
>
  
Siddhesh Poyarekar June 23, 2017, 2:10 p.m. UTC | #2
On Friday 23 June 2017 07:31 PM, Szabolcs Nagy wrote:
>> +++ b/manual/tunables.texi
>> @@ -231,6 +231,14 @@ the ones in @code{sysdeps/x86/cpu-features.h}.
>>  This tunable is specific to i386 and x86-64.
>>  @end deftp
>>  
>> +@deftp Tunable glibc.tune.cpu
>> +The @code{glibc.tune.cpu=xxx} tunable allows the user to tell @theglibc{} to
>> +assume that the CPU is @code{xxx} where xxx may have one of these values:
>> +@code{generic}, @code{thunderx}, @code{falkor}.
>> +
>> +This tunable is specific to aarch64.
>> +@end deftp
> 
> good, but this way we will have to update this list if glibc
> handles some new cpu.

Yeah, maybe we should auto-generate this later.


> i'm not convinced yet that we need falkor specific memcpy,
> in which case adding falkor here is not (yet) necessary.

Ack, I'll drop falkor from the list for now.

> 
>> +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
>> @@ -20,18 +20,54 @@
>>  #include <sys/auxv.h>
>>  #include <elf/dl-hwcaps.h>
>>  
>> +#if HAVE_TUNABLES
>> +struct cpu_list
>> +{
>> +  const char *name;
>> +  uint64_t midr;
>> +};
>> +
>> +static struct cpu_list cpu_list[] = {
>> +      {"falkor",	0x510FC000},
>> +      {"thunderx",	0x430F0A10},
> 
> note that this is not consistent with the gcc aarch64-cores.def
> naming where this cpu is named thunderxt88, but it's fine if
> this is what we want (i don't know if thunderx ppl will want to
> distinguish further variants).

I intended it to be the same as gcc, so I'll change it to thunderxt88.
And I'll drop falkor from here for now.

>> +      {"generic", 	0x0}
>> +};
>> +
>> +static uint64_t
>> +get_midr_from_mcpu (const char *mcpu)
>> +{
>> +  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
>> +    if (tunable_is_name (mcpu, cpu_list[i].name) == 0)
>> +      return cpu_list[i].midr;
>> +
>> +  return UINT64_MAX;
>> +}
>> +#endif
>> +
>>  static inline void
>>  init_cpu_features (struct cpu_features *cpu_features)
>>  {
>>    uint64_t hwcap_mask = GET_HWCAP_MASK();
>>    uint64_t hwcap = GLRO (dl_hwcap) & hwcap_mask;
>>  
>> -  if (hwcap & HWCAP_CPUID)
>> +  register uint64_t midr = UINT_MAX;
> 
> UINT_MAX vs UINT64_MAX above is inconsistent.

Ugh, yes, I'll fix that.

Siddhesh
  

Patch

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index b3c1392..44c160c 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -33,22 +33,6 @@ 
 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
 #endif
 
-/* Compare environment or tunable names, bounded by the name hardcoded in
-   glibc.  */
-static bool
-is_name (const char *orig, const char *envname)
-{
-  for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
-    if (*orig != *envname)
-      break;
-
-  /* The ENVNAME is immediately followed by a value.  */
-  if (*orig == '\0' && *envname == '=')
-    return true;
-  else
-    return false;
-}
-
 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 static char *
 tunables_strdup (const char *in)
@@ -235,7 +219,7 @@  parse_tunables (char *tunestr, char *valstring)
 	{
 	  tunable_t *cur = &tunable_list[i];
 
-	  if (is_name (cur->name, name))
+	  if (tunable_is_name (cur->name, name))
 	    {
 	      /* If we are in a secure context (AT_SECURE) then ignore the tunable
 		 unless it is explicitly marked as secure.  Tunable values take
@@ -318,7 +302,7 @@  __tunables_init (char **envp)
 			       &prev_envp)) != NULL)
     {
 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
-      if (is_name (GLIBC_TUNABLES, envname))
+      if (tunable_is_name (GLIBC_TUNABLES, envname))
 	{
 	  char *new_env = tunables_strdup (envname);
 	  if (new_env != NULL)
@@ -341,7 +325,7 @@  __tunables_init (char **envp)
 	  const char *name = cur->env_alias;
 
 	  /* We have a match.  Initialize and move on to the next line.  */
-	  if (is_name (name, envname))
+	  if (tunable_is_name (name, envname))
 	    {
 	      /* For AT_SECURE binaries, we need to check the security settings of
 		 the tunable and decide whether we read the value and also whether
@@ -356,7 +340,7 @@  __tunables_init (char **envp)
 
 		      while (*ep != NULL)
 			{
-			  if (is_name (name, *ep))
+			  if (tunable_is_name (name, *ep))
 			    {
 			      char **dp = ep;
 
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 6c49dcb..c92882a 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -111,5 +111,22 @@  rtld_hidden_proto (__tunable_get_val)
 # define TUNABLES_FRONTEND_valstring 1
 /* The default value for TUNABLES_FRONTEND.  */
 # define TUNABLES_FRONTEND_yes TUNABLES_FRONTEND_valstring
+
+/* Compare two name strings, bounded by the name hardcoded in glibc.  */
+static inline bool
+__always_inline
+tunable_is_name (const char *orig, const char *envname)
+{
+  for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
+    if (*orig != *envname)
+      break;
+
+  /* The ENVNAME is immediately followed by a value.  */
+  if (*orig == '\0' && *envname == '=')
+    return true;
+  else
+    return false;
+}
+
 #endif
 #endif
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 689e894..c242194 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -231,6 +231,14 @@  the ones in @code{sysdeps/x86/cpu-features.h}.
 This tunable is specific to i386 and x86-64.
 @end deftp
 
+@deftp Tunable glibc.tune.cpu
+The @code{glibc.tune.cpu=xxx} tunable allows the user to tell @theglibc{} to
+assume that the CPU is @code{xxx} where xxx may have one of these values:
+@code{generic}, @code{thunderx}, @code{falkor}.
+
+This tunable is specific to aarch64.
+@end deftp
+
 @deftp Tunable glibc.tune.x86_data_cache_size
 The @code{glibc.tune.x86_data_cache_size} tunable allows the user to set
 data cache size in bytes for use in memory and string routines.
diff --git a/sysdeps/aarch64/dl-tunables.list b/sysdeps/aarch64/dl-tunables.list
new file mode 100644
index 0000000..b1da2bf
--- /dev/null
+++ b/sysdeps/aarch64/dl-tunables.list
@@ -0,0 +1,25 @@ 
+# aarch64 specific tunables.
+# Copyright (C) 2017 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/>.
+
+glibc {
+  tune {
+    cpu {
+      type: STRING
+    }
+  }
+}
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index ef6eecd..5be6ff6 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -20,18 +20,54 @@ 
 #include <sys/auxv.h>
 #include <elf/dl-hwcaps.h>
 
+#if HAVE_TUNABLES
+struct cpu_list
+{
+  const char *name;
+  uint64_t midr;
+};
+
+static struct cpu_list cpu_list[] = {
+      {"falkor",	0x510FC000},
+      {"thunderx",	0x430F0A10},
+      {"generic", 	0x0}
+};
+
+static uint64_t
+get_midr_from_mcpu (const char *mcpu)
+{
+  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
+    if (tunable_is_name (mcpu, cpu_list[i].name) == 0)
+      return cpu_list[i].midr;
+
+  return UINT64_MAX;
+}
+#endif
+
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
   uint64_t hwcap_mask = GET_HWCAP_MASK();
   uint64_t hwcap = GLRO (dl_hwcap) & hwcap_mask;
 
-  if (hwcap & HWCAP_CPUID)
+  register uint64_t midr = UINT_MAX;
+
+#if HAVE_TUNABLES
+  /* Get the tunable override.  */
+  const char *mcpu = TUNABLE_GET (glibc, tune, mcpu, const char *, NULL);
+  if (mcpu != NULL)
+    midr = get_midr_from_mcpu (mcpu);
+#endif
+
+  /* If there was no useful tunable override, query the MIDR if the kernel
+     allows it.  */
+  if (midr == UINT_MAX)
     {
-      register uint64_t id = 0;
-      asm volatile ("mrs %0, midr_el1" : "=r"(id));
-      cpu_features->midr_el1 = id;
+      if (hwcap & HWCAP_CPUID)
+	asm volatile ("mrs %0, midr_el1" : "=r"(midr));
+      else
+	midr = 0;
     }
-  else
-    cpu_features->midr_el1 = 0;
+
+  cpu_features->midr_el1 = midr;
 }