Patchwork Linux: Implement per-thread file system attributes

login
register
mail settings
Submitter Florian Weimer
Date July 1, 2019, 4:32 p.m.
Message ID <87pnmtaf6l.fsf@oldenburg2.str.redhat.com>
Download mbox | patch
Permalink /patch/33502/
State New
Headers show

Comments

Florian Weimer - July 1, 2019, 4:32 p.m.
* Carlos O'Donell:

> On 6/28/19 4:20 PM, Florian Weimer wrote:
>> * Carlos O'Donell:
>> 
>>> On 6/28/19 3:19 PM, Florian Weimer wrote:
>>>> * Carlos O'Donell:
>>>>
>>>>> On 6/28/19 2:34 PM, Florian Weimer wrote:
>>>>>> * Carlos O'Donell:
>>>>>>
>>>>>>>>   if (flag != PTHREAD_PER_THREAD_NP)
>>>>>>>>     TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
>>>>>>>
>>>>>>> I don't follow why you can't call this unconditionally?
>>>>>>
>>>>>> This asserts that the flag has one of two values, PTHREAD_PER_THREAD_NP
>>>>>> or PTHREAD_PER_PROCESS_NP.
>>>>>
>>>>> I still don't follow.
>>>>>
>>>>> Why can't we just use:
>>>>>
>>>>> 	TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
>>>>>
>>>>> We only expect flag to be PTHREAD_PER_PROCESS_NP.
>>>>
>>>> No, the flag can be both, and the returned boolean changes as the
>>>> result.
>>>
>>> Ah! OK, I missed that bit of logic in the test.
>> 
>> Good.  I consider this patch finished, then.  (I fixed a typo in the
>> manual locally.)
>> 
>
> OK for master.
>
> Reviewed-by: Carlos O'Donell <carlos@redhat.com>

I adjusted the pthread_attr_getperthreadfs_np comment, and fixed a race
condition in the test.

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-06-28  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_PER_PROCESS_NP, PTHREAD_PER_THREAD_NP): Define.
	(pthread_attr_setperthreadfs_np, pthread_attr_getperthreadfs_np):
	Declare.
	* sysdeps/unix/sysv/linux/hppa/pthread.h
	(PTHREAD_PER_PROCESS_NP, PTHREAD_PER_THREAD_NP): Define.
	(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.

Patch

diff --git a/NEWS b/NEWS
index 6c7de105ac..e63b69b930 100644
--- a/NEWS
+++ b/NEWS
@@ -40,6 +40,11 @@  Major new features:
   FUNCTION-NAME, version SYMBOL-VERSION not defined in file DSO-NAME with
   link time reference, is gone.
 
+* 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 copy_file_range function fails with ENOSYS if the kernel does not
diff --git a/manual/threads.texi b/manual/threads.texi
index 87fda7d8e7..827ecb0929 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 directory 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 de312b3477..1374838339 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..f5ff348cac
--- /dev/null
+++ b/nptl/pthread_attr_getperthreadfs_np.c
@@ -0,0 +1,32 @@ 
+/* 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;
+  if (iattr->flags & ATTR_FLAG_PERTHREADFS)
+    *scope = PTHREAD_PER_THREAD_NP;
+  else
+    *scope = PTHREAD_PER_PROCESS_NP;
+  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..bb30449808
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs.c
@@ -0,0 +1,322 @@ 
+/* 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;
+
+/* Return true if the thread has per-thread file system attributes.
+   Note: This function calls pthread_getattr_np on THR, so the caller
+   has to ensure that the thread is still running (and not merely
+   joinable).  */
+static bool
+perthread_flag (pthread_t thr)
+{
+  pthread_attr_t attr;
+  int ret = pthread_getattr_np (thr, &attr);
+  if (ret != 0)
+    {
+      errno = ret;
+      FAIL_EXIT1 ("pthread_getattr_np: %m");
+    }
+  int flag = -1;
+  pthread_attr_getperthreadfs_np (&attr, &flag);
+  if (flag != PTHREAD_PER_THREAD_NP)
+    TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
+  xpthread_attr_destroy (&attr);
+  return flag == PTHREAD_PER_THREAD_NP;
+}
+
+/* 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");
+  TEST_VERIFY (perthread_flag (pthread_self ()));
+  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 PTHREAD_PER_THREAD_NP, but runs the actual test on a
+   PTHREAD_PER_PROCESS_NP thread (with a default attribute).  */
+static void *
+thread_perthreadfs_indirect (void *closure)
+{
+  TEST_VERIFY (perthread_flag (pthread_self ()));
+  /* Use the default NULL attribute here.  Since the per-thread scope
+     is sticky, it will still result in a per-thread file system
+     thread.  */
+  pthread_t thr = xpthread_create (NULL, thread_perthreadfs, closure);
+  return xpthread_join (thr);
+}
+
+/* Thread function launched with shared file system attributes.  */
+static void *
+thread_sharedfs (void *closure)
+{
+  TEST_VERIFY (!perthread_flag (pthread_self ()));
+
+  /* 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;
+
+  TEST_VERIFY (!perthread_flag (pthread_self ()));
+
+  xpthread_barrier_init (&barrier, NULL, 3);
+  xpthread_attr_init (&attr_perthreadfs);
+  {
+    /* Test: Default is PTHREAD_PER_PROCESS_NP.  */
+    int scope = -1;
+    TEST_COMPARE (pthread_attr_getperthreadfs_np (&attr_perthreadfs, &scope),
+                  0);
+    TEST_COMPARE (scope, PTHREAD_PER_PROCESS_NP);
+
+    /* Test: The getter shows the effect of the setter.  */
+    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: Invalid scope values result in an error, without a
+       change.  */
+    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);
+      /* Both threads wait on the barrier before exiting, so they are
+         running at his point, and the call to perthread_flag is
+         safe.  */
+      TEST_VERIFY (perthread_flag (thr1));
+      TEST_VERIFY (!perthread_flag (thr2));
+
+      /* 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 56c1ed43bb..774b0a692a 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -80,6 +80,7 @@  libsupport-routines = \
   xasprintf \
   xbind \
   xcalloc \
+  xchdir \
   xchroot \
   xclock_gettime \
   xclose \
@@ -88,6 +89,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..4b6a90f131 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));
+
+/* Get the per-thread/per-process scope of file system attributes from
+   *ATTR and store it in *SCOPE.  */
+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 a4c31932cb..2635e16f5e 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2143,5 +2143,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index fe85a35620..12227b9800 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2218,6 +2218,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index bc3df8dcea..6d09148e1d 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -128,6 +128,8 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/createthread.c b/sysdeps/unix/sysv/linux/createthread.c
index 579bd94743..177e14e9ae 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 controlled 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 9b3cee65bb..5ee091972b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2087,5 +2087,7 @@  GLIBC_2.29 xprt_register F
 GLIBC_2.29 xprt_unregister F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 75edece94a..844eaf539b 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2039,6 +2039,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/hppa/pthread.h b/sysdeps/unix/sysv/linux/hppa/pthread.h
index 45e706c037..43ffca5551 100644
--- a/sysdeps/unix/sysv/linux/hppa/pthread.h
+++ b/sysdeps/unix/sysv/linux/hppa/pthread.h
@@ -158,6 +158,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} } }
@@ -382,6 +392,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));
+
+/* Get the per-thread/per-process scope of file system attributes from
+   *ATTR and store it in *SCOPE.  */
+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/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index edeaf8e722..efd8cb1685 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2205,6 +2205,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index b5d460eeb2..7f34f4d1e6 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2071,6 +2071,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 05633b3cb8..34d5f6c91a 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -129,6 +129,8 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 47eb7b4608..71882c6e23 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2148,6 +2148,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index f7ced487f7..4f605fa67a 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2135,5 +2135,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index e49dc4272e..62790b0a64 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2122,6 +2122,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_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 daa3b60c5b..eb2ae61601 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2120,6 +2120,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_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 457ce0b6f2..9cf1462270 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2128,6 +2128,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_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 63d5c03bfb..d116e7180e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2122,6 +2122,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 7fec0c9670..0e8d5bfcc7 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2176,5 +2176,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 9200a54309..16e66c2fa4 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2178,6 +2178,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ef7779905f..fff0295a63 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2211,6 +2211,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 2860df8ebc..808f021335 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2041,6 +2041,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index 2229a1dcc0..a894bc49be 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2245,5 +2245,7 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 31010e6cf7..e220b0fd0c 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2105,5 +2105,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 576295deff..1567d2ff1d 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2173,6 +2173,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index abf0473683..f9d88d588e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2077,6 +2077,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index 41977f6e9c..affd74df4c 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -2043,6 +2043,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 3d2f00ca52..c12cc83bb2 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2167,6 +2167,8 @@  GLIBC_2.30 __nldbl_warn F
 GLIBC_2.30 __nldbl_warnx F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 _IO_fprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 2f20643e8e..37c9dff44c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2094,6 +2094,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_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 59f85d9373..71b7cc4ff9 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2052,6 +2052,8 @@  GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
 GLIBC_2.4 __confstr_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 67a4e238d6..573fc2e01c 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2151,5 +2151,7 @@  GLIBC_2.29 posix_spawn_file_actions_addchdir_np F
 GLIBC_2.29 posix_spawn_file_actions_addfchdir_np F
 GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
+GLIBC_2.30 pthread_attr_getperthreadfs_np F
+GLIBC_2.30 pthread_attr_setperthreadfs_np F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F