[16/19] nptl: Make pthread_attr_t dynamically extensible

Message ID 0372d63243979fb93d566609b913cf1ee3c15097.1589884403.git.fweimer@redhat.com
State New
Headers show
Series
  • Signal mask for timer helper thread
Related show

Commit Message

Fangrui Song via Libc-alpha May 19, 2020, 10:44 a.m.
This introduces the function __pthread_attr_extension to allocate the
extension space, which is freed by pthread_attr_destroy.
---
 nptl/Makefile                          |  1 +
 nptl/pthreadP.h                        |  6 +++++
 nptl/pthread_attr_copy.c               | 16 +++++++------
 nptl/pthread_attr_destroy.c            | 12 ++++++----
 nptl/pthread_attr_extension.c          | 32 ++++++++++++++++++++++++++
 nptl/pthread_attr_getaffinity.c        | 14 +++++------
 nptl/pthread_attr_setaffinity.c        | 23 +++++++++++-------
 nptl/pthread_create.c                  |  2 +-
 sysdeps/nptl/internaltypes.h           | 16 ++++++++++---
 sysdeps/unix/sysv/linux/createthread.c |  9 +++++---
 10 files changed, 98 insertions(+), 33 deletions(-)
 create mode 100644 nptl/pthread_attr_extension.c

-- 
2.25.4

Comments

Fangrui Song via Libc-alpha June 2, 2020, 3:47 a.m. | #1
On 5/19/20 6:44 AM, Florian Weimer via Libc-alpha wrote:
> This introduces the function __pthread_attr_extension to allocate the

> extension space, which is freed by pthread_attr_destroy.


OK for master with one typo fixed.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>


> ---

>  nptl/Makefile                          |  1 +

>  nptl/pthreadP.h                        |  6 +++++

>  nptl/pthread_attr_copy.c               | 16 +++++++------

>  nptl/pthread_attr_destroy.c            | 12 ++++++----

>  nptl/pthread_attr_extension.c          | 32 ++++++++++++++++++++++++++

>  nptl/pthread_attr_getaffinity.c        | 14 +++++------

>  nptl/pthread_attr_setaffinity.c        | 23 +++++++++++-------

>  nptl/pthread_create.c                  |  2 +-

>  sysdeps/nptl/internaltypes.h           | 16 ++++++++++---

>  sysdeps/unix/sysv/linux/createthread.c |  9 +++++---

>  10 files changed, 98 insertions(+), 33 deletions(-)

>  create mode 100644 nptl/pthread_attr_extension.c

> 

> diff --git a/nptl/Makefile b/nptl/Makefile

> index e5686b20ac..d6f12d0371 100644

> --- a/nptl/Makefile

> +++ b/nptl/Makefile

> @@ -41,6 +41,7 @@ routines = \

>    pthread_atfork \

>    pthread_attr_copy \

>    pthread_attr_destroy \

> +  pthread_attr_extension \


OK.

>    pthread_attr_getdetachstate \

>    pthread_attr_getinheritsched \

>    pthread_attr_getschedparam \

> diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h

> index ed30b72923..7b3153594e 100644

> --- a/nptl/pthreadP.h

> +++ b/nptl/pthreadP.h

> @@ -578,6 +578,12 @@ extern void __shm_directory_freeres (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__;


OK. Good comment.

> +

>  #ifdef SHARED

>  # define PTHREAD_STATIC_FN_REQUIRE(name)

>  #else

> diff --git a/nptl/pthread_attr_copy.c b/nptl/pthread_attr_copy.c

> index 77a1a43eeb..eb29557505 100644

> --- a/nptl/pthread_attr_copy.c

> +++ b/nptl/pthread_attr_copy.c

> @@ -29,18 +29,20 @@ __pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source)

>    temp.external = *source;

>  

>    /* Force new allocation.  This function has full ownership of temp.  */

> -  temp.internal.cpuset = NULL;

> -  temp.internal.cpusetsize = 0;

> +  temp.internal.extension = NULL;


OK. Init.

>  

>    int ret = 0;

>  

>    struct pthread_attr *isource = (struct pthread_attr *) source;

>  

> -  /* Propagate affinity mask information.  */

> -  if (isource->cpusetsize > 0)

> -    ret = __pthread_attr_setaffinity_np (&temp.external,

> -                                         isource->cpusetsize,

> -                                         isource->cpuset);

> +  if (isource->extension != NULL)

> +    {

> +      /* Propagate affinity mask information.  */

> +      if (isource->extension->cpusetsize > 0)

> +        ret = __pthread_attr_setaffinity_np (&temp.external,

> +                                             isource->extension->cpusetsize,

> +                                             isource->extension->cpuset);

> +    }


OK. Use extension space.

>  

>    if (ret != 0)

>      {

> diff --git a/nptl/pthread_attr_destroy.c b/nptl/pthread_attr_destroy.c

> index 21f8026a2c..b6a3cca657 100644

> --- a/nptl/pthread_attr_destroy.c

> +++ b/nptl/pthread_attr_destroy.c

> @@ -30,12 +30,16 @@ __pthread_attr_destroy (pthread_attr_t *attr)

>    iattr = (struct pthread_attr *) attr;

>  

>  #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_1)

> -  /* In old struct pthread_attr, neither next nor cpuset are

> -     present.  */

> +  /* In old struct pthread_attr, the extension member is missing.  */

>    if (__builtin_expect ((iattr->flags & ATTR_FLAG_OLDATTR), 0) == 0)

>  #endif

> -    /* The affinity CPU set might be allocated dynamically.  */

> -    free (iattr->cpuset);

> +    {

> +      if (iattr->extension != NULL)

> +        {

> +          free (iattr->extension->cpuset);

> +          free (iattr->extension);


OK.

> +        }

> +    }

>  

>    return 0;

>  }

> diff --git a/nptl/pthread_attr_extension.c b/nptl/pthread_attr_extension.c

> new file mode 100644

> index 0000000000..e8521b1556

> --- /dev/null

> +++ b/nptl/pthread_attr_extension.c

> @@ -0,0 +1,32 @@

> +/* Allocate the extension space for strucht pthread_attr.


Typo: s/strucht/struct/g

> +   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;

> +}


OK. Dynamically allocate space for the extension via calloc.

> diff --git a/nptl/pthread_attr_getaffinity.c b/nptl/pthread_attr_getaffinity.c

> index 212c1f7c0a..9483f69ddc 100644

> --- a/nptl/pthread_attr_getaffinity.c

> +++ b/nptl/pthread_attr_getaffinity.c

> @@ -33,22 +33,22 @@ __pthread_attr_getaffinity_new (const pthread_attr_t *attr, size_t cpusetsize,

>  

>    iattr = (const struct pthread_attr *) attr;

>  

> -  if (iattr->cpuset != NULL)

> +  if (iattr->extension != NULL && iattr->extension->cpuset != NULL)


OK.

>      {

>        /* Check whether there are any bits set beyond the limits

>  	 the user requested.  */

> -      for (size_t cnt = cpusetsize; cnt < iattr->cpusetsize; ++cnt)

> -	if (((char *) iattr->cpuset)[cnt] != 0)

> +      for (size_t cnt = cpusetsize; cnt < iattr->extension->cpusetsize; ++cnt)

> +	if (((char *) iattr->extension->cpuset)[cnt] != 0)


OK.

>  	  return EINVAL;

>  

>        /* 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

>  	 overrun.  If the destination is larger, fill the remaining space with

>  	 zeroes.  */

> -      void *p = mempcpy (cpuset, iattr->cpuset,

> -			 MIN (iattr->cpusetsize, cpusetsize));

> -      if (cpusetsize > iattr->cpusetsize)

> -	memset (p, '\0', cpusetsize - iattr->cpusetsize);

> +      void *p = mempcpy (cpuset, iattr->extension->cpuset,

> +			 MIN (iattr->extension->cpusetsize, cpusetsize));

> +      if (cpusetsize > iattr->extension->cpusetsize)

> +	memset (p, '\0', cpusetsize - iattr->extension->cpusetsize);


OK.

>      }

>    else

>      /* We have no information.  */

> diff --git a/nptl/pthread_attr_setaffinity.c b/nptl/pthread_attr_setaffinity.c

> index a42ffd92f4..9f9a70dee0 100644

> --- a/nptl/pthread_attr_setaffinity.c

> +++ b/nptl/pthread_attr_setaffinity.c

> @@ -34,23 +34,30 @@ __pthread_attr_setaffinity_np (pthread_attr_t *attr, size_t cpusetsize,

>  

>    if (cpuset == NULL || cpusetsize == 0)

>      {

> -      free (iattr->cpuset);

> -      iattr->cpuset = NULL;

> -      iattr->cpusetsize = 0;

> +      if (iattr->extension != NULL)

> +	{

> +	  free (iattr->extension->cpuset);

> +	  iattr->extension->cpuset = NULL;

> +	  iattr->extension->cpusetsize = 0;


OK.

> +	}

>      }

>    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)

>  	    return ENOMEM;

>  

> -	  iattr->cpuset = newp;

> -	  iattr->cpusetsize = cpusetsize;

> +	  iattr->extension->cpuset = newp;

> +	  iattr->extension->cpusetsize = cpusetsize;

>  	}

>  

> -      memcpy (iattr->cpuset, cpuset, cpusetsize);

> +      memcpy (iattr->extension->cpuset, cpuset, cpusetsize);


OK.

>      }

>  

>    return 0;

> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c

> index 86fbeb5218..f6418eb5ed 100644

> --- a/nptl/pthread_create.c

> +++ b/nptl/pthread_create.c

> @@ -884,7 +884,7 @@ __pthread_create_2_0 (pthread_t *newthread, const pthread_attr_t *attr,

>        new_attr.guardsize = ps;

>        new_attr.stackaddr = NULL;

>        new_attr.stacksize = 0;

> -      new_attr.cpuset = NULL;

> +      new_attr.extension = NULL;


OK.

>  

>        /* We will pass this value on to the real implementation.  */

>        attr = (pthread_attr_t *) &new_attr;

> diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h

> index 6d06a76baf..ca57c315d4 100644

> --- a/sysdeps/nptl/internaltypes.h

> +++ b/sysdeps/nptl/internaltypes.h

> @@ -36,9 +36,10 @@ struct pthread_attr

>    /* Stack handling.  */

>    void *stackaddr;

>    size_t stacksize;

> -  /* Affinity map.  */

> -  cpu_set_t *cpuset;

> -  size_t cpusetsize;

> +

> +  /* Allocated via a call to __pthread_attr_extension once needed.  */

> +  struct pthread_attr_extension *extension;

> +  void *unused;


OK. Keep the same size, but shuffle the usage around to get an extension
pointer and an unused pointer.

>  };

>  

>  #define ATTR_FLAG_DETACHSTATE		0x0001

> @@ -57,6 +58,15 @@ union pthread_attr_transparent

>    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;


OK. At worst an applicaiton that is copying the attribute around
is going to just get a copied pointer to the same affinity map.
Such an application was not correct anyway.

> +};

> +

>  /* Mutex attribute data structure.  */

>  struct pthread_mutexattr

>  {

> diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c

> index 21f9d24f2d..6588893ba5 100644

> --- a/sysdeps/unix/sysv/linux/createthread.c

> +++ b/sysdeps/unix/sysv/linux/createthread.c

> @@ -52,8 +52,10 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,

>    /* Determine whether the newly created threads has to be started

>       stopped since we have to set the scheduling parameters or set the

>       affinity.  */

> +  bool need_setaffinity = (attr != NULL && attr->extension != NULL

> +			   && attr->extension->cpuset != 0);


OK.

>    if (attr != NULL

> -      && (__glibc_unlikely (attr->cpuset != NULL)

> +      && (__glibc_unlikely (need_setaffinity)

>  	  || __glibc_unlikely ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)))

>      *stopped_start = true;

>  

> @@ -113,12 +115,13 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,

>        int res;

>  

>        /* Set the affinity mask if necessary.  */

> -      if (attr->cpuset != NULL)

> +      if (need_setaffinity)

>  	{

>  	  assert (*stopped_start);

>  

>  	  res = INTERNAL_SYSCALL_CALL (sched_setaffinity, pd->tid,

> -				       attr->cpusetsize, attr->cpuset);

> +				       attr->extension->cpusetsize,

> +				       attr->extension->cpuset);


OK.

>  

>  	  if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res)))

>  	  err_out:

> 



-- 
Cheers,
Carlos.

Patch

diff --git a/nptl/Makefile b/nptl/Makefile
index e5686b20ac..d6f12d0371 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -41,6 +41,7 @@  routines = \
   pthread_atfork \
   pthread_attr_copy \
   pthread_attr_destroy \
+  pthread_attr_extension \
   pthread_attr_getdetachstate \
   pthread_attr_getinheritsched \
   pthread_attr_getschedparam \
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index ed30b72923..7b3153594e 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -578,6 +578,12 @@  extern void __shm_directory_freeres (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
 # define PTHREAD_STATIC_FN_REQUIRE(name)
 #else
diff --git a/nptl/pthread_attr_copy.c b/nptl/pthread_attr_copy.c
index 77a1a43eeb..eb29557505 100644
--- a/nptl/pthread_attr_copy.c
+++ b/nptl/pthread_attr_copy.c
@@ -29,18 +29,20 @@  __pthread_attr_copy (pthread_attr_t *target, const pthread_attr_t *source)
   temp.external = *source;
 
   /* Force new allocation.  This function has full ownership of temp.  */
-  temp.internal.cpuset = NULL;
-  temp.internal.cpusetsize = 0;
+  temp.internal.extension = NULL;
 
   int ret = 0;
 
   struct pthread_attr *isource = (struct pthread_attr *) source;
 
-  /* Propagate affinity mask information.  */
-  if (isource->cpusetsize > 0)
-    ret = __pthread_attr_setaffinity_np (&temp.external,
-                                         isource->cpusetsize,
-                                         isource->cpuset);
+  if (isource->extension != NULL)
+    {
+      /* Propagate affinity mask information.  */
+      if (isource->extension->cpusetsize > 0)
+        ret = __pthread_attr_setaffinity_np (&temp.external,
+                                             isource->extension->cpusetsize,
+                                             isource->extension->cpuset);
+    }
 
   if (ret != 0)
     {
diff --git a/nptl/pthread_attr_destroy.c b/nptl/pthread_attr_destroy.c
index 21f8026a2c..b6a3cca657 100644
--- a/nptl/pthread_attr_destroy.c
+++ b/nptl/pthread_attr_destroy.c
@@ -30,12 +30,16 @@  __pthread_attr_destroy (pthread_attr_t *attr)
   iattr = (struct pthread_attr *) attr;
 
 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_1)
-  /* In old struct pthread_attr, neither next nor cpuset are
-     present.  */
+  /* In old struct pthread_attr, the extension member is missing.  */
   if (__builtin_expect ((iattr->flags & ATTR_FLAG_OLDATTR), 0) == 0)
 #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;
 }
diff --git a/nptl/pthread_attr_extension.c b/nptl/pthread_attr_extension.c
new file mode 100644
index 0000000000..e8521b1556
--- /dev/null
+++ b/nptl/pthread_attr_extension.c
@@ -0,0 +1,32 @@ 
+/* Allocate the extension space for strucht 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;
+}
diff --git a/nptl/pthread_attr_getaffinity.c b/nptl/pthread_attr_getaffinity.c
index 212c1f7c0a..9483f69ddc 100644
--- a/nptl/pthread_attr_getaffinity.c
+++ b/nptl/pthread_attr_getaffinity.c
@@ -33,22 +33,22 @@  __pthread_attr_getaffinity_new (const pthread_attr_t *attr, size_t cpusetsize,
 
   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
 	 the user requested.  */
-      for (size_t cnt = cpusetsize; cnt < iattr->cpusetsize; ++cnt)
-	if (((char *) iattr->cpuset)[cnt] != 0)
+      for (size_t cnt = cpusetsize; cnt < iattr->extension->cpusetsize; ++cnt)
+	if (((char *) iattr->extension->cpuset)[cnt] != 0)
 	  return EINVAL;
 
       /* 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
 	 overrun.  If the destination is larger, fill the remaining space with
 	 zeroes.  */
-      void *p = mempcpy (cpuset, iattr->cpuset,
-			 MIN (iattr->cpusetsize, cpusetsize));
-      if (cpusetsize > iattr->cpusetsize)
-	memset (p, '\0', cpusetsize - iattr->cpusetsize);
+      void *p = mempcpy (cpuset, iattr->extension->cpuset,
+			 MIN (iattr->extension->cpusetsize, cpusetsize));
+      if (cpusetsize > iattr->extension->cpusetsize)
+	memset (p, '\0', cpusetsize - iattr->extension->cpusetsize);
     }
   else
     /* We have no information.  */
diff --git a/nptl/pthread_attr_setaffinity.c b/nptl/pthread_attr_setaffinity.c
index a42ffd92f4..9f9a70dee0 100644
--- a/nptl/pthread_attr_setaffinity.c
+++ b/nptl/pthread_attr_setaffinity.c
@@ -34,23 +34,30 @@  __pthread_attr_setaffinity_np (pthread_attr_t *attr, size_t cpusetsize,
 
   if (cpuset == NULL || cpusetsize == 0)
     {
-      free (iattr->cpuset);
-      iattr->cpuset = NULL;
-      iattr->cpusetsize = 0;
+      if (iattr->extension != NULL)
+	{
+	  free (iattr->extension->cpuset);
+	  iattr->extension->cpuset = NULL;
+	  iattr->extension->cpusetsize = 0;
+	}
     }
   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)
 	    return ENOMEM;
 
-	  iattr->cpuset = newp;
-	  iattr->cpusetsize = cpusetsize;
+	  iattr->extension->cpuset = newp;
+	  iattr->extension->cpusetsize = cpusetsize;
 	}
 
-      memcpy (iattr->cpuset, cpuset, cpusetsize);
+      memcpy (iattr->extension->cpuset, cpuset, cpusetsize);
     }
 
   return 0;
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 86fbeb5218..f6418eb5ed 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -884,7 +884,7 @@  __pthread_create_2_0 (pthread_t *newthread, const pthread_attr_t *attr,
       new_attr.guardsize = ps;
       new_attr.stackaddr = NULL;
       new_attr.stacksize = 0;
-      new_attr.cpuset = NULL;
+      new_attr.extension = NULL;
 
       /* We will pass this value on to the real implementation.  */
       attr = (pthread_attr_t *) &new_attr;
diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h
index 6d06a76baf..ca57c315d4 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -36,9 +36,10 @@  struct pthread_attr
   /* Stack handling.  */
   void *stackaddr;
   size_t stacksize;
-  /* Affinity map.  */
-  cpu_set_t *cpuset;
-  size_t cpusetsize;
+
+  /* Allocated via a call to __pthread_attr_extension once needed.  */
+  struct pthread_attr_extension *extension;
+  void *unused;
 };
 
 #define ATTR_FLAG_DETACHSTATE		0x0001
@@ -57,6 +58,15 @@  union pthread_attr_transparent
   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.  */
 struct pthread_mutexattr
 {
diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
index 21f9d24f2d..6588893ba5 100644
--- a/sysdeps/unix/sysv/linux/createthread.c
+++ b/sysdeps/unix/sysv/linux/createthread.c
@@ -52,8 +52,10 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
   /* Determine whether the newly created threads has to be started
      stopped since we have to set the scheduling parameters or set the
      affinity.  */
+  bool need_setaffinity = (attr != NULL && attr->extension != NULL
+			   && attr->extension->cpuset != 0);
   if (attr != NULL
-      && (__glibc_unlikely (attr->cpuset != NULL)
+      && (__glibc_unlikely (need_setaffinity)
 	  || __glibc_unlikely ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)))
     *stopped_start = true;
 
@@ -113,12 +115,13 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
       int res;
 
       /* Set the affinity mask if necessary.  */
-      if (attr->cpuset != NULL)
+      if (need_setaffinity)
 	{
 	  assert (*stopped_start);
 
 	  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)))
 	  err_out: