nptl: Make pthread_attr_t dynamically extensible

This introduces the function __pthread_attr_extension to allocate the
extension space, which is freed by pthread_attr_destroy.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
Florian Weimer 2020-06-02 10:33:30 +02:00
parent 6993670b52
commit 7538d46113
10 changed files with 98 additions and 33 deletions

View File

@ -41,6 +41,7 @@ routines = \
pthread_atfork \ pthread_atfork \
pthread_attr_copy \ pthread_attr_copy \
pthread_attr_destroy \ pthread_attr_destroy \
pthread_attr_extension \
pthread_attr_getdetachstate \ pthread_attr_getdetachstate \
pthread_attr_getinheritsched \ pthread_attr_getinheritsched \
pthread_attr_getschedparam \ pthread_attr_getschedparam \

View File

@ -578,6 +578,12 @@ extern void __shm_directory_freeres (void) attribute_hidden;
extern void __wait_lookup_done (void) attribute_hidden; extern void __wait_lookup_done (void) attribute_hidden;
/* Allocates the extension space for ATTR. Returns an error code on
memory allocation failure, zero on success. If ATTR already has an
extension space, this function does nothing. */
int __pthread_attr_extension (struct pthread_attr *attr) attribute_hidden
__attribute_warn_unused_result__;
#ifdef SHARED #ifdef SHARED
# define PTHREAD_STATIC_FN_REQUIRE(name) # define PTHREAD_STATIC_FN_REQUIRE(name)
#else #else

View File

@ -29,18 +29,20 @@ __pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source)
temp.external = *source; temp.external = *source;
/* Force new allocation. This function has full ownership of temp. */ /* Force new allocation. This function has full ownership of temp. */
temp.internal.cpuset = NULL; temp.internal.extension = NULL;
temp.internal.cpusetsize = 0;
int ret = 0; int ret = 0;
struct pthread_attr *isource = (struct pthread_attr *) source; struct pthread_attr *isource = (struct pthread_attr *) source;
/* Propagate affinity mask information. */ if (isource->extension != NULL)
if (isource->cpusetsize > 0) {
ret = __pthread_attr_setaffinity_np (&temp.external, /* Propagate affinity mask information. */
isource->cpusetsize, if (isource->extension->cpusetsize > 0)
isource->cpuset); ret = __pthread_attr_setaffinity_np (&temp.external,
isource->extension->cpusetsize,
isource->extension->cpuset);
}
if (ret != 0) if (ret != 0)
{ {

View File

@ -30,12 +30,16 @@ __pthread_attr_destroy (pthread_attr_t *attr)
iattr = (struct pthread_attr *) attr; iattr = (struct pthread_attr *) attr;
#if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_1) #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_1)
/* In old struct pthread_attr, neither next nor cpuset are /* In old struct pthread_attr, the extension member is missing. */
present. */
if (__builtin_expect ((iattr->flags & ATTR_FLAG_OLDATTR), 0) == 0) if (__builtin_expect ((iattr->flags & ATTR_FLAG_OLDATTR), 0) == 0)
#endif #endif
/* The affinity CPU set might be allocated dynamically. */ {
free (iattr->cpuset); if (iattr->extension != NULL)
{
free (iattr->extension->cpuset);
free (iattr->extension);
}
}
return 0; return 0;
} }

View File

@ -0,0 +1,32 @@
/* Allocate the extension space for struct pthread_attr.
Copyright (C) 2020 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 <errno.h>
#include <pthreadP.h>
#include <stdlib.h>
int
__pthread_attr_extension (struct pthread_attr *attr)
{
if (attr->extension != NULL)
return 0;
attr->extension = calloc (sizeof (*attr->extension), 1);
if (attr->extension == NULL)
return errno;
return 0;
}

View File

@ -33,22 +33,22 @@ __pthread_attr_getaffinity_new (const pthread_attr_t *attr, size_t cpusetsize,
iattr = (const struct pthread_attr *) attr; iattr = (const struct pthread_attr *) attr;
if (iattr->cpuset != NULL) if (iattr->extension != NULL && iattr->extension->cpuset != NULL)
{ {
/* Check whether there are any bits set beyond the limits /* Check whether there are any bits set beyond the limits
the user requested. */ the user requested. */
for (size_t cnt = cpusetsize; cnt < iattr->cpusetsize; ++cnt) for (size_t cnt = cpusetsize; cnt < iattr->extension->cpusetsize; ++cnt)
if (((char *) iattr->cpuset)[cnt] != 0) if (((char *) iattr->extension->cpuset)[cnt] != 0)
return EINVAL; return EINVAL;
/* Copy over the cpuset from the thread attribute object. Limit the copy /* Copy over the cpuset from the thread attribute object. Limit the copy
to the minimum of the source and destination sizes to prevent a buffer to the minimum of the source and destination sizes to prevent a buffer
overrun. If the destination is larger, fill the remaining space with overrun. If the destination is larger, fill the remaining space with
zeroes. */ zeroes. */
void *p = mempcpy (cpuset, iattr->cpuset, void *p = mempcpy (cpuset, iattr->extension->cpuset,
MIN (iattr->cpusetsize, cpusetsize)); MIN (iattr->extension->cpusetsize, cpusetsize));
if (cpusetsize > iattr->cpusetsize) if (cpusetsize > iattr->extension->cpusetsize)
memset (p, '\0', cpusetsize - iattr->cpusetsize); memset (p, '\0', cpusetsize - iattr->extension->cpusetsize);
} }
else else
/* We have no information. */ /* We have no information. */

View File

@ -34,23 +34,30 @@ __pthread_attr_setaffinity_np (pthread_attr_t *attr, size_t cpusetsize,
if (cpuset == NULL || cpusetsize == 0) if (cpuset == NULL || cpusetsize == 0)
{ {
free (iattr->cpuset); if (iattr->extension != NULL)
iattr->cpuset = NULL; {
iattr->cpusetsize = 0; free (iattr->extension->cpuset);
iattr->extension->cpuset = NULL;
iattr->extension->cpusetsize = 0;
}
} }
else else
{ {
if (iattr->cpusetsize != cpusetsize) int ret = __pthread_attr_extension (iattr);
if (ret != 0)
return ret;
if (iattr->extension->cpusetsize != cpusetsize)
{ {
void *newp = (cpu_set_t *) realloc (iattr->cpuset, cpusetsize); void *newp = realloc (iattr->extension->cpuset, cpusetsize);
if (newp == NULL) if (newp == NULL)
return ENOMEM; return ENOMEM;
iattr->cpuset = newp; iattr->extension->cpuset = newp;
iattr->cpusetsize = cpusetsize; iattr->extension->cpusetsize = cpusetsize;
} }
memcpy (iattr->cpuset, cpuset, cpusetsize); memcpy (iattr->extension->cpuset, cpuset, cpusetsize);
} }
return 0; return 0;

View File

@ -884,7 +884,7 @@ __pthread_create_2_0 (pthread_t *newthread, const pthread_attr_t *attr,
new_attr.guardsize = ps; new_attr.guardsize = ps;
new_attr.stackaddr = NULL; new_attr.stackaddr = NULL;
new_attr.stacksize = 0; new_attr.stacksize = 0;
new_attr.cpuset = NULL; new_attr.extension = NULL;
/* We will pass this value on to the real implementation. */ /* We will pass this value on to the real implementation. */
attr = (pthread_attr_t *) &new_attr; attr = (pthread_attr_t *) &new_attr;

View File

@ -36,9 +36,10 @@ struct pthread_attr
/* Stack handling. */ /* Stack handling. */
void *stackaddr; void *stackaddr;
size_t stacksize; size_t stacksize;
/* Affinity map. */
cpu_set_t *cpuset; /* Allocated via a call to __pthread_attr_extension once needed. */
size_t cpusetsize; struct pthread_attr_extension *extension;
void *unused;
}; };
#define ATTR_FLAG_DETACHSTATE 0x0001 #define ATTR_FLAG_DETACHSTATE 0x0001
@ -57,6 +58,15 @@ union pthread_attr_transparent
struct pthread_attr internal; struct pthread_attr internal;
}; };
/* Extension space for pthread attributes. Referenced by the
extension member of struct pthread_attr. */
struct pthread_attr_extension
{
/* Affinity map. */
cpu_set_t *cpuset;
size_t cpusetsize;
};
/* Mutex attribute data structure. */ /* Mutex attribute data structure. */
struct pthread_mutexattr struct pthread_mutexattr
{ {

View File

@ -52,8 +52,10 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,
/* Determine whether the newly created threads has to be started /* Determine whether the newly created threads has to be started
stopped since we have to set the scheduling parameters or set the stopped since we have to set the scheduling parameters or set the
affinity. */ affinity. */
bool need_setaffinity = (attr != NULL && attr->extension != NULL
&& attr->extension->cpuset != 0);
if (attr != NULL if (attr != NULL
&& (__glibc_unlikely (attr->cpuset != NULL) && (__glibc_unlikely (need_setaffinity)
|| __glibc_unlikely ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))) || __glibc_unlikely ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)))
*stopped_start = true; *stopped_start = true;
@ -113,12 +115,13 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,
int res; int res;
/* Set the affinity mask if necessary. */ /* Set the affinity mask if necessary. */
if (attr->cpuset != NULL) if (need_setaffinity)
{ {
assert (*stopped_start); assert (*stopped_start);
res = INTERNAL_SYSCALL_CALL (sched_setaffinity, pd->tid, res = INTERNAL_SYSCALL_CALL (sched_setaffinity, pd->tid,
attr->cpusetsize, attr->cpuset); attr->extension->cpusetsize,
attr->extension->cpuset);
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res))) if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res)))
err_out: err_out: