Patchwork Linux: Implement per-thread file system attributes

login
register
mail settings
Submitter Florian Weimer
Date April 15, 2019, 12:19 p.m.
Message ID <87wojv31hn.fsf@oldenburg2.str.redhat.com>
Download mbox | patch
Permalink /patch/32290/
State New
Headers show

Comments

Florian Weimer - April 15, 2019, 12:19 p.m.
* Carlos O'Donell:

> On 4/10/19 1:03 PM, Florian Weimer wrote:
>> * Carlos O'Donell:
>>
>>> Any child created with CLONE_FS will have per-thread properties, that's
>>> clear and easy to understand.
>>>
>>> However, in the case of !CLONE_FS you will have a set of descendants
>>> which all share the same properties (not those of the process itself).
>>>
>>> (a) Is there any desire to want to create a new thread that has the same
>>>      shared properties as that of the process (not that of the parent
>>>      task)?
>>
>> I doubt we'll ever need it.  We could implement it with a separate
>> helper thread with a tiny stack, specifically serving such requests (and
>> which is created the first time per-thread attributes are created).  I
>> don't think we need to implement it right now.
>
> I agree we do not need to implement this right now, and you're right this
> could be done with a helper thread, or a signaling/msg system like setxid
> (which I actually like).

It's something what applications can do without glibc help (if we ignore
dlopen).  The new patch mentions this in the manual.

>> With my TID patches and kcmp with KCMP_FS, it will be possible to test
>> if two running threads share the same file system attributes.
>
> Perfect, and that's already useful because you it solves the use case
> I was thinking about which is mostly "Do my changes affect the
> process?"

Okay, I will update the manual entry once we have the kcmp system call
wrapper.

>>> If (a) was possible, and I don't think it is given the current linux
>>> interfaces, then when you have "PTHREAD_PER_PROCESS_NP" it would really
>>> do what the name says, and attach the thread's fs-attributes to the
>>> process (not the most recent set of descendant children from a parent
>>> task that set this value).
>>
>> I think that would be super-confusing because the net effect will be
>> that pthread_create changes these attributes (in the sense that the new
>> thread starts out with different values).
>
> The solution we have today is also confusing, in that the semantics of
> PTHREAD_PER_PROCESS_NP are changed by the state of the caller.

I thought this some more and came up with the following behavior, which
matches what we will implement for user/group IDs in the follow-up
patch.

>>> A library, with an outside view, can't tell, by accessing the
>>> thread attributes via pthread_getattr_np(), if it shares with
>>> the process or not because PTHREAD_PER_PROCESS_NP will bet set.
>>
>> True, but one of the threads may have called unshare, so you need kernel
>> support anyway.
>
> That's very different from calling pthread_create() which is a known
> API that might not be expected to change the behaviour of future
> pthread_create() invocations.

But it does!  Future pthread_create calls from such a thread will share
resources with that thread only, and not the main thread.

>> We could also make PTHREAD_PER_THREAD_NP sticky in the sense that *all*
>> descendent threads have it set automatically, nom atter what the
>> attribute requests.  This is what we need to implement for user and
>> group IDs, as discussed before.  We could also add a third flag for the
>> non-sticky behavior.
>
> Yes, I like this idea. I think PTHREAD_PER_THREAD_NP should be sticky
> and setting PTHREAD_PER_PROCESS_NP should result in a error,

The error part will make it impossible to use std::thread and the like
from such threads, which I think is undesirable.

The patch below makes the behavior sticky.

Thanks,
Florian

Linux: Implement per-thread file system attributes

This commit adds the functions pthread_attr_setperthreadfs_np and
pthread_attr_getperthreadfs_np.

The implementation is based on suppressing the CLONE_FS clone flag when
creating the new thread.  The new flag is sticky and is applied to
all threads created from a thread with the PTHREAD_PER_THREAD_NP
attribute.

2019-04-15  Florian Weimer  <fweimer@redhat.com>

	Linux: Implement per-thread file system attributes.
	* manual/threads.texi (Enabling Per-Thread Properties): New
	section.
	(Non-POSIX Extensions): Reference it.
	* nptl/Makefile (routines): Add pthread_attr_setperthreadfs_np,
	pthread_attr_getperthreadfs_np.
	(tests): Add tst-pthread-perthreadfs,
	tst-pthread-perthreadfs-chroot.
	* nptl/Versions (GLIBC_2.30): Export
	pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np.
	* nptl/pthread_attr_setperthreadfs_np.c: New file
	* nptl/pthread_attr_getperthreadfs_np.c: Likewise.
	* nptl/pthread_create.c (__pthread_create_2_1): Use
	ATTR_FLAGS_IGNORED_ATTR and ATTR_FLAGS_INHERITED to compute the
	flags for the new thread.
	* nptl/tst-pthread-perthreadfs.c: Likewise.
	* nptl/tst-pthread-perthreadfs-chroot.c: Likewise.
	* sysdeps/nptl/internaltypes.h (ATTR_FLAG_PERTHREADFS)
	(ATTR_FLAGS_IGNORED_ATTR, ATTR_FLAGS_INHERITED): Define.
	* sysdeps/unix/sysv/linux/createthread.c (create_thread): Use it.
	* sysdeps/nptl/pthread.h (pthread_attr_setperthreadfs_np)
	(pthread_attr_getperthreadfs_np): Declare.
	* support/Makefile (libsupport-routines): Add xchdir, xfchdir.
	* support/xunistd.h (xchdir, xfchdir): Declare.
	* support/xchdir.c: New file.
	* support/xfchdir.c: Likewise.
	* sysdeps/unix/sysv/linux/aarch64/libc.abilist (GLIBC_2.30): Add
	pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/csky/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/hppa/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/ia64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/nios2/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
	(GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/libc.abilist (GLIBC_2.30): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist (GLIBC_2.30):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist (GLIBC_2.30):
	Likewise.
Andreas Schwab - April 15, 2019, 12:35 p.m.
On Apr 15 2019, Florian Weimer <fweimer@redhat.com> wrote:

> diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
> index 579bd94743..3516077ded 100644
> --- a/sysdeps/unix/sysv/linux/createthread.c
> +++ b/sysdeps/unix/sysv/linux/createthread.c
> @@ -66,7 +66,9 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,
>  
>       CLONE_VM, CLONE_FS, CLONE_FILES
>  	These flags select semantics with shared address space and
> -	file descriptors according to what POSIX requires.
> +	file descriptors according to what POSIX requires.  CLONE_FS
> +	is optional; it can be using the function
> +	pthread_attr_setperthreadfs_np.

That sentence lacks a verb.

Andreas.
Florian Weimer - April 15, 2019, 12:40 p.m.
* Andreas Schwab:

> On Apr 15 2019, Florian Weimer <fweimer@redhat.com> wrote:
>
>> diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
>> index 579bd94743..3516077ded 100644
>> --- a/sysdeps/unix/sysv/linux/createthread.c
>> +++ b/sysdeps/unix/sysv/linux/createthread.c
>> @@ -66,7 +66,9 @@ create_thread (struct pthread *pd, const struct pthread_attr *attr,
>>  
>>       CLONE_VM, CLONE_FS, CLONE_FILES
>>  	These flags select semantics with shared address space and
>> -	file descriptors according to what POSIX requires.
>> +	file descriptors according to what POSIX requires.  CLONE_FS
>> +	is optional; it can be using the function
>> +	pthread_attr_setperthreadfs_np.
>
> That sentence lacks a verb.

Thanks, what about this?

      CLONE_VM, CLONE_FS, CLONE_FILES
 	These flags select semantics with shared address space and
 	file descriptors according to what POSIX requires.  CLONE_FS
-	is optional; it can be using the function
+	is optional; it can be controlled using the function
 	pthread_attr_setperthreadfs_np.

Do you have an opinion whether we should go with the sticky or
non-sticky approach?

Thanks,
Florian

Patch

diff --git a/NEWS b/NEWS
index b58e2469d4..a8145e8423 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,11 @@  Major new features:
 
 * The entry for the new Japanese era has been added for ja_JP locale.
 
+* On Linux, it is now possible to create threads with per-thread current
+  directory, umask, and file system root.  The functions
+  pthread_attr_setperthreadfs_np and pthread_attr_getperthreadfs_np have
+  been added in support of that.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The functions clock_gettime, clock_getres, clock_settime,
diff --git a/manual/threads.texi b/manual/threads.texi
index 87fda7d8e7..60c15a8d15 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -625,6 +625,7 @@  the standard.
 @menu
 * Default Thread Attributes::             Setting default attributes for
 					  threads in a process.
+* Enabling Per-Thread Properties::        Additional per-thread properties.
 @end menu
 
 @node Default Thread Attributes
@@ -669,6 +670,100 @@  The system does not have sufficient memory.
 @end table
 @end deftypefun
 
+@node Enabling Per-Thread Properties
+@subsubsection Enabling Additional Per-Thread Properties
+
+POSIX mandates that the current directory, file system root, umask
+value, and the current user and group IDs are process-global
+properties.  For example, if a thread calls the @code{chdir} function
+to change to a different directory, all threads are eventually
+affected by this change.  @Theglibc{} implements an extension which
+allows threads to be created which do not share these properties with
+the rest of the process.
+
+The desired behavior is specified at the time the thread is created,
+using the thread attribute.  The following constants are used to
+update the attribute:
+
+@vtable @code
+@item PTHREAD_PER_PROCESS_NP
+@standards{GNU, pthread.h}
+This property in question is globally shared across the entire process.
+This is the default.
+
+@item PTHREAD_PER_THREAD_NP
+@standards{GNU, pthread.h}
+This property in question is thread-specific.
+@end vtable
+
+The @code{PTHREAD_PER_THREAD_NP} flag is sticky, in the sense that all
+threads created by a thread created with this flag have per-thread
+properties, even if they are created with the matching thread
+attribute set to the @code{PTHREAD_PER_PROCESS_NP} flag.  If an
+application wants to create new threads sharing properties with the
+main thread, it should create a service thread early (perhaps from an
+ELF constructor) and create these threads using this service thread.
+
+Per-thread properties can be set and examined for an attribute using
+the functions below.
+
+@deftypefun int pthread_attr_setperthreadfs_np (pthread_attr_t *@var{attr}, int @var{scope})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Change whether the following properties related to file system access
+are made thread-specific when a new thread is created using the
+attribute @var{attr}:
+
+@itemize @bullet
+@item
+@cindex current directiry as a per-thread property
+@cindex per-thread current directory
+@cindex thread-specific current directory
+current directory (as changed by @code{chdir} and related functions)
+
+@item
+@cindex @code{chroot} as a per-thread property
+@cindex per-thread @code{chroot}
+@cindex thread-specific @code{chroot}
+file system root (as changed by @code{chroot})
+
+@item
+@cindex umask as a per-thread property
+@cindex per-thread @code{umask}
+@cindex thread-specific @code{umask}
+umask value (as changed by the function of the same name)
+@end itemize
+
+This function returns zero on success.  @var{scope} must be one of the
+constants @code{PTHREAD_PER_PROCESS_NP} or
+@code{PTHREAD_PER_THREAD_NP}, otherwise the function returns
+@code{EINVAL}.
+
+If @var{scope} is @code{PTHREAD_PER_THREAD_NP}, the attribute will
+cause the properties listed above to be specific to the thread.  The
+initial values of these properties are copied from the creating
+thread, at thread creation time.
+
+If a thread that has been created with the
+@code{PTHREAD_PER_THREAD_NP} flag creates further threads, these
+threads are implicitly created with the @code{PTHREAD_PER_THREAD_NP}
+flag, ignoring the value of this thread creation attribute.
+(If this behavior is not desirable, it is possible to call
+@samp{unshare (CLONE_FS)} from the new thread instead of creating it
+with the @code{PTHREAD_PER_THREAD_NP} flag.)
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
+@deftypefun int pthread_attr_getperthreadfs_np (pthread_attr_t *restrict @var{attr}, int *restrict @var{scope})
+@standards{GNU, pthread.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+Obtain the per-thread status of the file system properties in
+@var{attr} and store it in the location @var{scope}.
+
+This function is a GNU extension and specific to Linux.
+@end deftypefun
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy
diff --git a/nptl/Makefile b/nptl/Makefile
index f9bc5cc887..d9f2dd42c0 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -30,7 +30,8 @@  extra-libs-others := $(extra-libs)
 routines = alloca_cutoff forward libc-lowlevellock libc-cancellation \
 	   libc-cleanup libc_pthread_init libc_multiple_threads \
 	   register-atfork pthread_atfork pthread_self thrd_current \
-	   thrd_equal thrd_sleep thrd_yield
+	   thrd_equal thrd_sleep thrd_yield \
+	   pthread_attr_setperthreadfs_np pthread_attr_getperthreadfs_np
 shared-only-routines = forward
 static-only-routines = pthread_atfork
 
@@ -321,7 +322,8 @@  tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \
 	tst-rwlock-pwn \
 	tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
-	tst-unwind-thread
+	tst-unwind-thread \
+	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot
 
 tests-internal := tst-rwlock19 tst-rwlock20 \
 		  tst-sem11 tst-sem12 tst-sem13 \
diff --git a/nptl/Versions b/nptl/Versions
index e7f691da7a..817fec04f3 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -32,6 +32,10 @@  libc {
   GLIBC_2.28 {
     thrd_current; thrd_equal; thrd_sleep; thrd_yield;
   }
+  GLIBC_2.30 {
+    pthread_attr_setperthreadfs_np;
+    pthread_attr_getperthreadfs_np;
+  }
   GLIBC_PRIVATE {
     __libc_alloca_cutoff;
     # Internal libc interface to libpthread
diff --git a/nptl/pthread_attr_getperthreadfs_np.c b/nptl/pthread_attr_getperthreadfs_np.c
new file mode 100644
index 0000000000..6670939101
--- /dev/null
+++ b/nptl/pthread_attr_getperthreadfs_np.c
@@ -0,0 +1,29 @@ 
+/* Read the private file system flag in thread attributes.
+   Copyright (C) 2019 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 <pthread.h>
+#include <internaltypes.h>
+
+int
+pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict attr,
+                                int *__restrict scope)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  *scope = (iattr->flags & ATTR_FLAG_PERTHREADFS) != 0;
+  return 0;
+}
diff --git a/nptl/pthread_attr_setperthreadfs_np.c b/nptl/pthread_attr_setperthreadfs_np.c
new file mode 100644
index 0000000000..2d274cb144
--- /dev/null
+++ b/nptl/pthread_attr_setperthreadfs_np.c
@@ -0,0 +1,39 @@ 
+/* Change the private file system flag in thread attributes.
+   Copyright (C) 2019 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 <pthread.h>
+#include <internaltypes.h>
+#include <errno.h>
+
+int
+pthread_attr_setperthreadfs_np (pthread_attr_t *attr, int scope)
+{
+  struct pthread_attr *iattr = (struct pthread_attr *) attr;
+  switch (scope)
+    {
+    case PTHREAD_PER_PROCESS_NP:
+      iattr->flags &= ~ATTR_FLAG_PERTHREADFS;
+      return 0;
+      break;
+    case PTHREAD_PER_THREAD_NP:
+      iattr->flags |= ATTR_FLAG_PERTHREADFS;
+      return 0;
+    default:
+      return EINVAL;
+    }
+}
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 18b7bbe765..0002fa7299 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -693,8 +693,8 @@  __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
 
   /* Copy the thread attribute flags.  */
   struct pthread *self = THREAD_SELF;
-  pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
-	       | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
+  pd->flags = ((iattr->flags & ~ATTR_FLAGS_IGNORED_ATTR)
+	       | (self->flags & ATTR_FLAGS_INHERITED));
 
   /* Initialize the field for the ID of the thread which is waiting
      for us.  This is a self-reference in case the thread is created
diff --git a/nptl/tst-pthread-perthreadfs-chroot.c b/nptl/tst-pthread-perthreadfs-chroot.c
new file mode 100644
index 0000000000..094c291baa
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs-chroot.c
@@ -0,0 +1,255 @@ 
+/* Test per-thread file system attributes, chroot version.
+   Copyright (C) 2019 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/>.  */
+
+/* This thread is separate from tst-pthread-perthreadfs because it
+   requires chroot support.  */
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Paths for chroot operations.  */
+static char *chroot_1;
+static char *chroot_2;
+static char *chroot_3;
+
+/* These paths are used for recognizing chroots.  */
+static const char *const chroot_1_marker = "/chroot-1-marker";
+static const char *const chroot_2_marker = "/chroot-2-marker";
+
+/* Directory available in the chroot for a second chroot call.  */
+static const char *const next_chroot = "/next_chroot";
+
+/* Return 0 for no chroot, 1 for first chroot, 2 for second chroot, or
+   -1 for error.  */
+static int
+which_chroot (void)
+{
+  /* If the full (out-of-chroot) path is present, we are not in a
+     chroot.  */
+  if (access (chroot_1, F_OK) == 0)
+    return 0;
+  if (access (chroot_1_marker, F_OK) == 0)
+    return 1;
+  if (access (chroot_2_marker, F_OK) == 0)
+    return 2;
+  return -1;
+}
+
+/* Thread attribute requesting per-thread file system attributes.  */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads.  This is needed
+   so that the chroot changes happen in the expected order.  */
+static pthread_barrier_t barrier;
+
+static void *
+thread_which_chroot_helper (void *closure)
+{
+  int *result = closure;
+  *result = which_chroot ();
+  return NULL;
+}
+
+static void *
+thread_which_chroot_helper_perthread (void *closure)
+{
+  int *result = closure;
+  *result = which_chroot ();
+  if (*result == 0)
+    /* No /next-chroot available without a first chroot.  */
+    xchroot (chroot_3);
+  else
+    xchroot (next_chroot);
+  return NULL;
+}
+
+/* Determine the current chroot on another thread.  */
+static int
+thread_which_chroot (void)
+{
+  int result1;
+  pthread_t thr = xpthread_create (NULL, thread_which_chroot_helper, &result1);
+  xpthread_join (thr);
+  /* Same using per-thread attributes.  They are supposed to be equal.
+     Also change the chroot to check isolation.  */
+  int result2;
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_which_chroot_helper_perthread, &result2);
+  xpthread_join (thr);
+  TEST_COMPARE (result1, result2);
+  return result1;
+}
+
+/* Verify that the file system attributes for the current thread are
+   as expected.  */
+static void
+check_attributes (const char *where, int expected_chroot)
+{
+  printf ("info: reading file attributes in %s\n", where);
+  TEST_COMPARE (which_chroot (), expected_chroot);
+  TEST_COMPARE (thread_which_chroot (), expected_chroot);
+}
+
+/* Thread function launched with per-thread file system
+   attributes.  */
+static void *
+thread_perthreadfs (void *closure)
+{
+  puts ("info: changing file attributes in thread_perthreadfs");
+  xchroot (chroot_1);
+  check_attributes ("thread_perthreadfs", 1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes changed in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_perthreadfs", 1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+/* Thread function launched with shared file system attributes.  */
+static void *
+thread_sharedfs (void *closure)
+{
+  /* Wait for thread_perthreadfs to update chroot.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_sharedfs", 0);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+
+  puts ("info: changing file attributes in thread_sharedfs");
+  xchroot (chroot_2);
+  check_attributes ("thread_sharedfs", 2);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  support_become_root ();
+  if (!support_can_chroot ())
+    FAIL_UNSUPPORTED ("chroot not supported");
+
+  /* Used to revert the effect of chroot.  */
+  int original_chroot_fd = xopen ("/", O_DIRECTORY | O_RDONLY, 0);
+
+  TEST_COMPARE (access (chroot_1_marker, F_OK), -1);
+  TEST_COMPARE (access (chroot_2_marker, F_OK), -1);
+  TEST_COMPARE (access (next_chroot, F_OK), -1);
+
+  chroot_1 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-1-");
+  chroot_2 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-2-");
+  chroot_3 = support_create_temp_directory
+    ("tst-pthread-perthreadfs-chroot-3-");
+  {
+    char *path = xasprintf ("%s%s", chroot_1, chroot_1_marker);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_1, next_chroot);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_2, chroot_2_marker);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+
+    path = xasprintf ("%s%s", chroot_2, next_chroot);
+    xmkdir (path, 0777);
+    add_temp_file (path);
+    free (path);
+  }
+  TEST_COMPARE (which_chroot (), 0);
+
+  xpthread_barrier_init (&barrier, NULL, 3);
+  xpthread_attr_init (&attr_perthreadfs);
+  TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+                                                PTHREAD_PER_THREAD_NP), 0);
+
+  pthread_t thr1 = xpthread_create (&attr_perthreadfs,
+                                    thread_perthreadfs, NULL);
+  pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+
+  /* Wait for thread_perthreadfs to update chroot.  */
+  xpthread_barrier_wait (&barrier);
+  /* File attributes read thread_sharedfs here.  */
+  xpthread_barrier_wait (&barrier);
+
+  check_attributes ("main thread", 0);
+
+  xpthread_barrier_wait (&barrier);
+  /* File attributes changed thread_sharedfs here.  */
+  xpthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  xpthread_barrier_wait (&barrier);
+
+  check_attributes ("main thread", 2);
+
+  xpthread_barrier_wait (&barrier);
+
+  xpthread_join (thr2);
+  xpthread_join (thr1);
+
+  xpthread_attr_destroy (&attr_perthreadfs);
+  xpthread_barrier_destroy (&barrier);
+
+  free (chroot_3);
+  free (chroot_2);
+  free (chroot_1);
+
+  xfchdir (original_chroot_fd);
+  xclose (original_chroot_fd);
+  xchroot (".");
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nptl/tst-pthread-perthreadfs.c b/nptl/tst-pthread-perthreadfs.c
new file mode 100644
index 0000000000..81cf315c97
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs.c
@@ -0,0 +1,280 @@ 
+/* Test per-thread file system attributes.
+   Copyright (C) 2019 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 <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/* Original values in the parent process.  */
+static char *original_cwd;
+static mode_t original_umask;
+
+/* New values for the attributes, distinct from the old.  */
+static char *new_cwd_1;
+static char *new_cwd_2;
+static mode_t new_umask_1;
+static mode_t new_umask_2;
+
+/* Thread attribute requesting per-thread file system attributes.  */
+pthread_attr_t attr_perthreadfs;
+
+/* Used to synchronize operations among the threads.  This is needed
+   so that the attribute changes happen in the expected order, and
+   that get_umask_unlocked can be used safely.  */
+static pthread_barrier_t barrier;
+
+/* Linux does not have getumask, so use synchronization and the umask
+   system call to read the value.  */
+static mode_t
+get_umask_unlocked (void)
+{
+  mode_t mask = umask (0);
+  TEST_COMPARE (umask (mask), 0);
+  return mask;
+}
+
+static void *
+thread_get_pwd_helper (void *closure)
+{
+  char *result = get_current_dir_name ();
+  if (closure != NULL)
+    xchdir (closure);
+  return result;
+}
+
+/* Obtain the current directory on another thread.  */
+static char *
+thread_get_pwd (void)
+{
+  pthread_t thr = xpthread_create (NULL, thread_get_pwd_helper, NULL);
+  char *path1 = xpthread_join (thr);
+  /* Same using per-thread attributes.  They are supposed to be equal.
+     Also change the current directory to check isolation.  */
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_get_pwd_helper, (char *) "/");
+  char *path2 = xpthread_join (thr);
+  TEST_COMPARE_STRING (path1, path2);
+  free (path2);
+  return path1;
+}
+
+static void *
+thread_get_umask_helper (void *closure)
+{
+  mode_t *pmask = closure;
+  *pmask = get_umask_unlocked ();
+  return NULL;
+}
+
+static void *
+thread_get_umask_helper_perthread (void *closure)
+{
+  mode_t *pmask = closure;
+  *pmask = get_umask_unlocked ();
+  /* Check isolation.  This bit pattern is not used anywhere else.  */
+  TEST_COMPARE (umask (*pmask ^ 0101), *pmask);
+  return NULL;
+}
+
+/* Obtain the current umask on another thread.  */
+static mode_t
+thread_get_umask_unlocked (void)
+{
+  mode_t mask1;
+  pthread_t thr = xpthread_create (NULL, thread_get_umask_helper, &mask1);
+  xpthread_join (thr);
+  mode_t mask2;
+  thr = xpthread_create (&attr_perthreadfs,
+                         thread_get_umask_helper_perthread, &mask2);
+  xpthread_join (thr);
+  TEST_COMPARE (mask1, mask2);
+  return mask1;
+}
+
+/* Verify that the file system attributes for the current thread are
+   as expected.  */
+static void
+check_attributes (const char *where, mode_t expected_umask,
+                  const char *expected_cwd)
+{
+  printf ("info: reading file attributes in %s\n", where);
+  TEST_COMPARE (get_umask_unlocked (), expected_umask);
+  TEST_COMPARE (thread_get_umask_unlocked (), expected_umask);
+  char *cwd = get_current_dir_name ();
+  TEST_COMPARE_STRING (cwd, expected_cwd);
+  free (cwd);
+  cwd = thread_get_pwd ();
+  TEST_COMPARE_STRING (cwd, expected_cwd);
+  free (cwd);
+}
+
+/* Thread function launched with per-thread file system
+   attributes.  */
+static void *
+thread_perthreadfs (void *closure)
+{
+  puts ("info: changing file attributes in thread_perthreadfs");
+  xchdir (new_cwd_1);
+  TEST_COMPARE (umask (new_umask_1), original_umask);
+  check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes changed in thread_sharedfs here.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_perthreadfs", new_umask_1, new_cwd_1);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+/* Launched with te PTHREAD_PER_THREAD_NP, but runs the actual test on
+   a PTHREAD_PER_PROCESS_NP thread.  */
+static void *
+thread_perthreadfs_indirect (void *closure)
+{
+  return xpthread_join (xpthread_create (NULL, thread_perthreadfs, closure));
+}
+
+/* Thread function launched with shared file system attributes.  */
+static void *
+thread_sharedfs (void *closure)
+{
+  /* Wait for thread_perthreadfs to update current directory and
+     umask.  */
+  pthread_barrier_wait (&barrier);
+
+  check_attributes ("thread_sharedfs", original_umask, original_cwd);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+
+  puts ("info: changing file attributes in thread_sharedfs");
+  TEST_COMPARE (umask (new_umask_2), original_umask);
+  xchdir (new_cwd_2);
+  check_attributes ("thread_sharedfs", new_umask_2, new_cwd_2);
+
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in thread_perthreadfs here.  */
+  pthread_barrier_wait (&barrier);
+  /* File attributes read in main thread here.  */
+  pthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  original_cwd = get_current_dir_name ();
+  TEST_VERIFY_EXIT (original_cwd != NULL);
+  original_umask = get_umask_unlocked ();
+
+  new_cwd_1 = support_create_temp_directory ("tst-pthread-perthreadfs-1-");
+  new_cwd_2 = support_create_temp_directory ("tst-pthread-perthreadfs-2-");
+  /* Arbitrary bit pattern change to obtain distinct values, so that
+     it is possible to check for actual changes.  */
+  new_umask_1 = original_umask ^ 0111;
+  new_umask_2 = original_umask ^ 0222;
+
+  xpthread_barrier_init (&barrier, NULL, 3);
+  xpthread_attr_init (&attr_perthreadfs);
+  {
+    int scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_PROCESS_NP);
+    TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs,
+                                                  PTHREAD_PER_THREAD_NP), 0);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+
+    TEST_COMPARE (pthread_attr_setperthreadfs_np (&attr_perthreadfs, 2),
+                  EINVAL);
+    scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_THREAD_NP);
+  }
+
+  /* Two test runs, once with perthreadsfs set to
+     PTHREAD_PER_THREAD_NP directly, once indirectly via inheritance
+     from the current (overriding the PTHREAD_PER_PROCESS_NP
+     default).  */
+  for (int do_indirect = 0; do_indirect < 2;  ++do_indirect)
+    {
+      printf ("info: test iteration with do_indirect == %d\n", do_indirect);
+      pthread_t thr1;
+      if (do_indirect)
+        thr1 = xpthread_create (&attr_perthreadfs,
+                                thread_perthreadfs_indirect, NULL);
+      else
+        thr1 = xpthread_create (&attr_perthreadfs, thread_perthreadfs, NULL);
+      pthread_t thr2 = xpthread_create (NULL, thread_sharedfs, NULL);
+
+      /* Wait for thread_perthreadfs to update current directory and
+         umask.  */
+      xpthread_barrier_wait (&barrier);
+      /* File attributes read thread_sharedfs here.  */
+      xpthread_barrier_wait (&barrier);
+
+      check_attributes ("main thread", original_umask, original_cwd);
+
+      xpthread_barrier_wait (&barrier);
+      /* File attributes changed thread_sharedfs here.  */
+      xpthread_barrier_wait (&barrier);
+      /* File attributes read in thread_perthreadfs here.  */
+      xpthread_barrier_wait (&barrier);
+
+      check_attributes ("main thread", new_umask_2, new_cwd_2);
+
+      xpthread_barrier_wait (&barrier);
+
+      xpthread_join (thr2);
+      xpthread_join (thr1);
+
+      xchdir (original_cwd);
+      umask (original_umask);
+    }
+
+  xpthread_attr_destroy (&attr_perthreadfs);
+  xpthread_barrier_destroy (&barrier);
+
+  free (new_cwd_2);
+  free (new_cwd_1);
+  free (original_cwd);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/Makefile b/support/Makefile
index f173565202..b71f2f3a72 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -76,6 +76,7 @@  libsupport-routines = \
   xasprintf \
   xbind \
   xcalloc \
+  xchdir \
   xchroot \
   xclose \
   xconnect \
@@ -83,6 +84,7 @@  libsupport-routines = \
   xdlfcn \
   xdlmopen \
   xdup2 \
+  xfchdir \
   xfclose \
   xfopen \
   xfork \
diff --git a/support/xchdir.c b/support/xchdir.c
new file mode 100644
index 0000000000..a2f728f5df
--- /dev/null
+++ b/support/xchdir.c
@@ -0,0 +1,28 @@ 
+/* chdir with error checking.
+   Copyright (C) 2017-2019 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 <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xchdir (const char *path)
+{
+  if (chdir (path) != 0)
+    FAIL_EXIT1 ("chdir (\"%s\"): %m", path);
+}
diff --git a/support/xfchdir.c b/support/xfchdir.c
new file mode 100644
index 0000000000..76c5f655bb
--- /dev/null
+++ b/support/xfchdir.c
@@ -0,0 +1,28 @@ 
+/* fchdir with error checking.
+   Copyright (C) 2017-2019 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 <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xfchdir (int fd)
+{
+  if (fchdir (fd) != 0)
+    FAIL_EXIT1 ("fchdir (%d): %m", fd);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 338eb86a1b..b470d99be1 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -38,6 +38,8 @@  int xopen (const char *path, int flags, mode_t);
 void xstat (const char *path, struct stat64 *);
 void xfstat (int fd, struct stat64 *);
 void xmkdir (const char *path, mode_t);
+void xchdir (const char *path);
+void xfchdir (int);
 void xchroot (const char *path);
 void xunlink (const char *path);
 long xsysconf (int name);
diff --git a/sysdeps/nptl/internaltypes.h b/sysdeps/nptl/internaltypes.h
index 53d037e0f1..5ad56908b5 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -48,6 +48,18 @@  struct pthread_attr
 #define ATTR_FLAG_OLDATTR		0x0010
 #define ATTR_FLAG_SCHED_SET		0x0020
 #define ATTR_FLAG_POLICY_SET		0x0040
+#define ATTR_FLAG_PERTHREADFS		0x0080
+
+/* These flags are not copied from the thread attribute at
+   pthread_create time.  */
+#define ATTR_FLAGS_IGNORED_ATTR \
+  (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)
+
+/* These flags are inherited from the current thread during
+   pthread_create even if they are not specified in the thread
+   attribute.  */
+#define ATTR_FLAGS_INHERITED \
+  ATTR_FLAG_PERTHREADFS
 
 
 /* Mutex attribute data structure.  */
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 704a3c48d6..37073e3ce7 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -181,7 +181,16 @@  enum
 #define PTHREAD_PROCESS_SHARED  PTHREAD_PROCESS_SHARED
 };
 
-
+#ifdef __USE_GNU
+/* Thread-private or process-global flag.  */
+enum
+{
+ PTHREAD_PER_PROCESS_NP,
+# define PTHREAD_PER_PROCESS_NP PTHREAD_PER_PROCESS_NP
+ PTHREAD_PER_THREAD_NP
+# define PTHREAD_PER_THREAD_NP PTHREAD_PER_THREAD_NP
+};
+#endif /* __USE_GNU */
 
 /* Conditional variable handling.  */
 #define PTHREAD_COND_INITIALIZER { { {0}, {0}, {0, 0}, {0, 0}, 0, 0, {0, 0} } }
@@ -406,6 +415,21 @@  extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr,
 					cpu_set_t *__cpuset)
      __THROW __nonnull ((1, 3));
 
+
+/* Control the flag in ATTR whether the thread has its own current
+   directory, file system root, and umask attribute.  SCOPE must be
+   PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP.  By default, new
+   threads share these attributes with their creating thread
+   (PTHREAD_PER_PROCESS_NP).  */
+int pthread_attr_setperthreadfs_np (pthread_attr_t *__attr, int __scope)
+  __THROW __nonnull ((1));
+
+/* Set *SCOPE to PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP,
+   depending on the state of *ATTR.  */
+int pthread_attr_getperthreadfs_np (const pthread_attr_t *__restrict __attr,
+				    int *__restrict __scope)
+  __THROW __nonnull ((1, 2));
+
 /* Get the default attributes used by pthread_create in this process.  */
 extern int pthread_getattr_default_np (pthread_attr_t *__attr)
      __THROW __nonnull ((1));
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index f3b44d723f..7912106998 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2142,3 +2142,5 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fd81fc4ad0..8f55111716 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2217,6 +2217,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index f451fefac9..d6595fe7d0 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -127,6 +127,8 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
index 579bd94743..3516077ded 100644
--- a/sysdeps/unix/sysv/linux/createthread.c
+++ b/sysdeps/unix/sysv/linux/createthread.c
@@ -66,7 +66,9 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
 
      CLONE_VM, CLONE_FS, CLONE_FILES
 	These flags select semantics with shared address space and
-	file descriptors according to what POSIX requires.
+	file descriptors according to what POSIX requires.  CLONE_FS
+	is optional; it can be using the function
+	pthread_attr_setperthreadfs_np.
 
      CLONE_SIGHAND, CLONE_THREAD
 	This flag selects the POSIX signal semantics and various
@@ -90,11 +92,13 @@  create_thread (struct pthread *pd, const struct pthread_attr *attr,
 
      The termination signal is chosen to be zero which means no signal
      is sent.  */
-  const int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SYSVSEM
-			   | CLONE_SIGHAND | CLONE_THREAD
-			   | CLONE_SETTLS | CLONE_PARENT_SETTID
-			   | CLONE_CHILD_CLEARTID
-			   | 0);
+  int clone_flags = (CLONE_VM | CLONE_FILES | CLONE_SYSVSEM
+		     | CLONE_SIGHAND | CLONE_THREAD
+		     | CLONE_SETTLS | CLONE_PARENT_SETTID
+		     | CLONE_CHILD_CLEARTID
+		     | 0);
+  if ((attr->flags & ATTR_FLAG_PERTHREADFS) == 0)
+    clone_flags |= CLONE_FS;
 
   TLS_DEFINE_INIT_TP (tp, pd);
 
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 018d02b414..28c54ac331 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2086,3 +2086,5 @@  GLIBC_2.29 xencrypt F
 GLIBC_2.29 xprt_register F
 GLIBC_2.29 xprt_unregister F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index fc3c5d5c27..82bbcfdb88 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2038,6 +2038,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f2b04dbbff..c258476838 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2204,6 +2204,8 @@  GLIBC_2.3.4 vm86 F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 10ecf2e47c..439b51895d 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2070,6 +2070,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 814e81b9d2..3c4b521a71 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -128,6 +128,8 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 68e80372e7..a69f98d9f3 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2147,6 +2147,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 31178e4f54..880e71b23c 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2134,3 +2134,5 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 7074573638..a7710cd95d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2121,6 +2121,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 154f9c77fc..a09634310a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2119,6 +2119,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 97b8f42d5c..a865424562 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2127,6 +2127,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 5b3e85de93..e54df4f9c1 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2121,6 +2121,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 04a130a81c..6c0e4f850a 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2175,3 +2175,5 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index a701584422..f4fb7c1108 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2177,6 +2177,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index bbb647cd98..b6482e239e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2210,6 +2210,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index bb23bf61a8..5f558722dd 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2040,6 +2040,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 7921dda979..c5bca34fde 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2244,3 +2244,5 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index da123d3867..8409b9dab0 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2104,3 +2104,5 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 2aed339af4..fe23f03d8b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2172,6 +2172,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index e46feb56e5..1c546e63d6 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2076,6 +2076,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 24a8f934cb..ad60fb32e5 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2042,6 +2042,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index ebdbd2c5ae..f6bf145af9 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2166,6 +2166,8 @@  GLIBC_2.30 __nldbl_vwarnx F
 GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 0992349b06..aeb6cc37f5 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2093,6 +2093,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index af004fcff6..eb0558be26 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2051,6 +2051,8 @@  GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 84015f0a57..92d7386d87 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2150,3 +2150,5 @@  GLIBC_2.29 getcpu F
 GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F