diff mbox series

Linux: Implement per-thread file system attributes

Message ID 871s29zyb9.fsf@oldenburg2.str.redhat.com
State New
Headers show
Series Linux: Implement per-thread file system attributes | expand

Commit Message

Florian Weimer April 10, 2019, 3:15 p.m. UTC
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.
-----
Changes from the previous version:

- rebased to glibc 2.30
- some documentation in the manual
- closer alignment to other pthread attributes
  (e.g. the pshared flag handling)

If approved, I will put the support/ bits into a separate commit.

Thanks,
Florian

2019-04-10  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.29): 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/tst-pthread-perthreadfs.c: Likewise.
	* nptl/tst-pthread-perthreadfs-chroot.c: Likewise.
	* sysdeps/nptl/internaltypes.h (ATTR_FLAG_PERTHREADFS): 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/libc-le.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.

Comments

Carlos O'Donell April 10, 2019, 3:39 p.m. UTC | #1
On 4/10/19 11:15 AM, Florian Weimer wrote:
> 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.
> -----
> Changes from the previous version:
> 
> - rebased to glibc 2.30
> - some documentation in the manual
> - closer alignment to other pthread attributes
>    (e.g. the pshared flag handling)
> 
> If approved, I will put the support/ bits into a separate commit.

I haven't gone through the whole patch yet, but something specific came
to mind as I read through the API semantics.

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

(b) Is there any need to identify the parent that created the set of
     descendants that share all the same filesystem properties?

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

Is (a) possible?

If (a) is not possible, it's interesting to ask the following question
as a programming:

* Given the current thread.
* Does it share the filesystem attributes with the process?
* If it does, then take certain steps to avoid stomping on it.
* If it doesn't, then do whatever you want.

The problem here is this:
pthread_create (PTHREAD_PER_THREAD_NP) -> Thread A.
Thread A -> pthread_create (PTHREAD_PER_PROCESS_NP) -> Thread B.
Thread B -> Library tries to determine if it's sharing or not with the whole process.

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.

Should (b) be considered? Should we store the value of the parent task
into the attribute structure in some way? Or only store a bit to
indicate that we are in a distinct set from the process itself?

It may be that this doesn't matter and that in the future we'll just
need a new API for this kind of query if it becomes relevant.

I'm just looking at the broader design to see if it impacts any
choice you have to make today.
Florian Weimer April 10, 2019, 5:03 p.m. UTC | #2
* 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.

(The setxid mechanism could be used as well, but we probably do not want
to pile more functionality onto it.)

> (b) Is there any need to identify the parent that created the set of
>     descendants that share all the same filesystem properties?

We do not have a unique identifier for the parent thread, so I don't
think that can be implemented today.

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.

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

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

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.

Thanks,
Florian
Carlos O'Donell April 10, 2019, 8:03 p.m. UTC | #3
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).

> (The setxid mechanism could be used as well, but we probably do not want
> to pile more functionality onto it.)
> 
>> (b) Is there any need to identify the parent that created the set of
>>      descendants that share all the same filesystem properties?
> 
> We do not have a unique identifier for the parent thread, so I don't
> think that can be implemented today.

Right there is an ABA issue with TID reuse.

> 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?"
  
>> 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.

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

> 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, while
setting some new non-sticky value PTHREAD_PARENT_NP allows you to
inherit your FS from the thread calling pthread_create().

Did we miss anything else?
Florian Weimer April 15, 2019, 12:19 p.m. UTC | #4
* 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.

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
Andreas Schwab April 15, 2019, 12:35 p.m. UTC | #5
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. UTC | #6
* 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
diff mbox series

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..7bb69e16c9 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,89 @@  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
+
+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.
+
+If a thread that has been created with the
+@code{PTHREAD_PER_THREAD_NP} flag creates further threads and those
+are created with @code{PTHREAD_PER_PROCESS_NP} as the file-system
+sharing mode (either explicit or implicty), they share the file-system
+properties with the creating thread and its descendant threads (and
+not the entire process).
+
+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/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..a1be7ad7af
--- /dev/null
+++ b/nptl/tst-pthread-perthreadfs.c
@@ -0,0 +1,257 @@ 
+/* 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;
+}
+
+/* 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);
+  }
+
+  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 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);
+
+  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..4e02ee8c2b 100644
--- a/sysdeps/nptl/internaltypes.h
+++ b/sysdeps/nptl/internaltypes.h
@@ -48,6 +48,7 @@  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
 
 
 /* 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