[PATCHv2,2/2] Initialize tunable list with the GLIBC_TUNABLES environment variable
Commit Message
Read tunables values from the users using the GLIBC_TUNABLES
environment variable. The value of this variable is a colon-separated
list of name=value pairs. So a typical string would look like this:
GLIBC_TUNABLES=glibc.malloc.mmap_threshold=2048:glibc.malloc.trim_threshold=1024
* tunables/tunables.c: Include sys/mman.h and libc-internals.h.
(GLIBC_TUNABLES): New macro.
(t_strdup): New function.
(__tunables_init): Implement initializer.
---
tunables/tunables.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 101 insertions(+), 1 deletion(-)
Comments
On 01/16/2016 12:56 PM, Siddhesh Poyarekar wrote:
> Read tunables values from the users using the GLIBC_TUNABLES
> environment variable. The value of this variable is a colon-separated
> list of name=value pairs. So a typical string would look like this:
Tested on ppc64le.
While this does work as expected with trivial inputs, it does
have some quirks:
I.e this will set the desired tunable:
GLIBC_TUNABLES="glibc.pthread.elision_enable=1"
GLIBC_TUNABLES="glibc.pthread.elision_enable=1:"
GLIBC_TUNABLES="foo=:glibc.pthread.elision_enable=1"
But the following does not:
GLIBC_TUNABLES=":glibc.pthread.elision_enable=1"
GLIBC_TUNABLES="foo:glibc.pthread.elision_enable=1"
GLIBC_TUNABLES="val=foo:bar:glibc.pthread.elision_enable=1"
@@ -24,12 +24,16 @@
#include <stdbool.h>
#include <unistd.h>
#include <sys/param.h>
+#include <sys/mman.h>
+#include <libc-internal.h>
extern char **__environ;
#define TUNABLES_INTERNAL 1
#include "tunables.h"
+#define GLIBC_TUNABLES "GLIBC_TUNABLES"
+
/* We avoid calling into the C library as much as we can, especially functions
that we know could use tunables in future for some reason or the other. For
example, we call mmap directly instead of malloc because malloc uses
@@ -57,6 +61,31 @@ t_strcmp (const char *a, const char *b)
return *a - *b;
}
+static char *
+t_strdup (const char *in)
+{
+ size_t len = 0;
+
+ while (in[len] != '\0')
+ len++;
+
+ /* Allocate enough number of pages. Given the number of tunables this should
+ not exceed a single page but we err on the conservative side and try to
+ allocate space as needed. */
+ size_t alloclen = ALIGN_UP (len + 1, __getpagesize ());
+
+ char *out = __mmap (NULL, alloclen, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+
+ if (__glibc_unlikely (out == MAP_FAILED))
+ return NULL;
+ else
+ {
+ t_memcpy (out, in, len);
+ return out;
+ }
+}
+
static bool
get_next_env (char ***envp, char **name, size_t *namelen, char **val)
{
@@ -93,7 +122,78 @@ get_next_env (char ***envp, char **name, size_t *namelen, char **val)
void
__tunables_init (char **envp)
{
- /* Empty for now. */
+ static bool initialized = false;
+
+ if (__glibc_likely (initialized))
+ return;
+
+ char **evp = envp;
+ char *p = NULL;
+
+ char *envname;
+ size_t envnamelen;
+ char *envval;
+
+ while (get_next_env (&evp, &envname, &envnamelen, &envval))
+ {
+ char *name = alloca (envnamelen + 1);
+
+ t_memcpy (name, envname, envnamelen);
+ name[envnamelen] = '\0';
+
+ if (!t_strcmp (name, GLIBC_TUNABLES))
+ {
+ p = t_strdup (envval);
+ break;
+ }
+ }
+
+ if (p == NULL || *p == '\0')
+ goto out;
+
+ while (true)
+ {
+ char *name = p;
+ size_t len = 0;
+
+ /* First, find where the name ends. */
+ while (p[len] != '=' && p[len] != '\0')
+ len++;
+
+ /* If we reach the end of the string before getting a valid name-value
+ pair, bail out. */
+ if (p[len] == '\0')
+ goto out;
+
+ p[len] = '\0';
+ p += len + 1;
+
+ char *value = p;
+ len = 0;
+
+ while (p[len] != ':' && p[len] != '\0')
+ len++;
+
+ char end = p[len];
+ p[len] = '\0';
+
+ /* Add the tunable if it exists. */
+ for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
+ {
+ if (t_strcmp (name, tunable_list[i].name) == 0)
+ {
+ tunable_list[i].val = value;
+ break;
+ }
+ }
+
+ if (end == ':')
+ p += len + 1;
+ else
+ goto out;
+ }
+out:
+ initialized = true;
}
libc_hidden_def (__tunables_init)