* Re: [PATCH 05/18] Add io_uring IO interface
From: Jens Axboe @ 2019-02-05 16:58 UTC (permalink / raw)
To: Florian Weimer; +Cc: linux-aio, linux-block, linux-api, hch, jmoyer, avi, jannh
In-Reply-To: <871s4r8j5i.fsf@oldenburg2.str.redhat.com>
On 2/1/19 11:20 AM, Florian Weimer wrote:
> * Jens Axboe:
>
>> +/*
>> + * Filled with the offset for mmap(2)
>> + */
>> +struct io_sqring_offsets {
>> + __u32 head;
>> + __u32 tail;
>> + __u32 ring_mask;
>> + __u32 ring_entries;
>> + __u32 flags;
>> + __u32 dropped;
>> + __u32 array;
>> + __u32 resv[3];
>> +};
>> +
>> +struct io_cqring_offsets {
>> + __u32 head;
>> + __u32 tail;
>> + __u32 ring_mask;
>> + __u32 ring_entries;
>> + __u32 overflow;
>> + __u32 cqes;
>> + __u32 resv[4];
>> +};
>> +
>> +/*
>> + * io_uring_enter(2) flags
>> + */
>> +#define IORING_ENTER_GETEVENTS (1U << 0)
>> +
>> +/*
>> + * Passed in for io_uring_setup(2). Copied back with updated info on success
>> + */
>> +struct io_uring_params {
>> + __u32 sq_entries;
>> + __u32 cq_entries;
>> + __u32 flags;
>> + __u32 resv[7];
>> + struct io_sqring_offsets sq_off;
>> + struct io_cqring_offsets cq_off;
>> +};
>
> I still think it might be a good idea to have one reserved __u64 field
> for alignment purposes.
Sure, I can do that.
--
Jens Axboe
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [PATCH 05/18] Add io_uring IO interface
From: Jens Axboe @ 2019-02-05 16:58 UTC (permalink / raw)
To: Jeff Moyer; +Cc: linux-aio, linux-block, linux-api, hch, avi, jannh
In-Reply-To: <x49sgx3t9yv.fsf@segfault.boston.devel.redhat.com>
On 2/4/19 4:22 PM, Jeff Moyer wrote:
> Hi, Jens,
>
> Jens Axboe <axboe@kernel.dk> writes:
>
>> +static void io_ring_ctx_free(struct io_ring_ctx *ctx)
>> +{
>> + destroy_workqueue(ctx->sqo_wq);
>
> You need to make sure sqo_wq is non-null before calling
> destroy_workqueue.
Fixed, thanks.
--
Jens Axboe
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [PATCH 05/18] Add io_uring IO interface
From: Jens Axboe @ 2019-02-05 16:59 UTC (permalink / raw)
To: Jeff Moyer; +Cc: linux-aio, linux-block, linux-api, hch, avi, jannh
In-Reply-To: <x49o97rt8l8.fsf@segfault.boston.devel.redhat.com>
On 2/4/19 4:52 PM, Jeff Moyer wrote:
> Jeff Moyer <jmoyer@redhat.com> writes:
>
>> Hi, Jens,
>>
>> Jens Axboe <axboe@kernel.dk> writes:
>>
>>> +static void io_ring_ctx_free(struct io_ring_ctx *ctx)
>>> +{
>>> + destroy_workqueue(ctx->sqo_wq);
>>
>> You need to make sure sqo_wq is non-null before calling
>> destroy_workqueue.
>
> You also need to check for non-null sqo_files and sqo_mm.
>
> Basically, you're tearing things down twice if setup of the context
> fails in some way.
Also fixed, thanks!
--
Jens Axboe
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [PATCH 13/18] io_uring: add file set registration
From: Jens Axboe @ 2019-02-05 17:57 UTC (permalink / raw)
To: Al Viro, Jann Horn
Cc: linux-aio, linux-block, Linux API, hch, jmoyer, avi,
linux-fsdevel
In-Reply-To: <785c6db4-095e-65b0-ded5-72b41af5174e@kernel.dk>
On 2/4/19 7:19 PM, Jens Axboe wrote:
> On 2/3/19 7:56 PM, Al Viro wrote:
>> On Wed, Jan 30, 2019 at 02:29:05AM +0100, Jann Horn wrote:
>>> On Tue, Jan 29, 2019 at 8:27 PM Jens Axboe <axboe@kernel.dk> wrote:
>>>> We normally have to fget/fput for each IO we do on a file. Even with
>>>> the batching we do, the cost of the atomic inc/dec of the file usage
>>>> count adds up.
>>>>
>>>> This adds IORING_REGISTER_FILES, and IORING_UNREGISTER_FILES opcodes
>>>> for the io_uring_register(2) system call. The arguments passed in must
>>>> be an array of __s32 holding file descriptors, and nr_args should hold
>>>> the number of file descriptors the application wishes to pin for the
>>>> duration of the io_uring context (or until IORING_UNREGISTER_FILES is
>>>> called).
>>>>
>>>> When used, the application must set IOSQE_FIXED_FILE in the sqe->flags
>>>> member. Then, instead of setting sqe->fd to the real fd, it sets sqe->fd
>>>> to the index in the array passed in to IORING_REGISTER_FILES.
>>>>
>>>> Files are automatically unregistered when the io_uring context is
>>>> torn down. An application need only unregister if it wishes to
>>>> register a new set of fds.
>>>
>>> Crazy idea:
>>>
>>> Taking a step back, at a high level, basically this patch creates sort
>>> of the same difference that you get when you compare the following
>>> scenarios for normal multithreaded I/O in userspace:
>>
>>> This kinda makes me wonder whether this is really something that
>>> should be implemented specifically for the io_uring API, or whether it
>>> would make sense to somehow handle part of this in the generic VFS
>>> code and give the user the ability to prepare a new files_struct that
>>> can then be transferred to the worker thread, or something like
>>> that... I'm not sure whether there's a particularly clean way to do
>>> that though.
>>
>> Using files_struct for that opens a can of worms you really don't
>> want to touch.
>>
>> Consider the following scenario with any variant of this interface:
>> * create io_uring fd.
>> * send an SCM_RIGHTS with that fd to AF_UNIX socket.
>> * add the descriptor of that AF_UNIX socket to your fd
>> * close AF_UNIX fd, close io_uring fd.
>> Voila - you've got a shiny leak. No ->release() is called for
>> anyone (and you really don't want to do that on ->flush(), because
>> otherwise a library helper doing e.g. system("/bin/date") will tear
>> down all the io_uring in your process). The socket is held by
>> the reference you've stashed into io_uring (whichever way you do
>> that). io_uring is held by the reference you've stashed into
>> SCM_RIGHTS datagram in queue of the socket.
>>
>> No matter what, you need net/unix/garbage.c to be aware of that stuff.
>> And getting files_struct lifetime mixed into that would be beyond
>> any reason.
>>
>> The only reason for doing that as a descriptor table would be
>> avoiding the cost of fget() in whatever uses it, right? Since
>
> Right, the only purpose of this patch is to avoid doing fget/fput for
> each IO.
>
>> those are *not* the normal syscalls (and fdget() really should not
>> be used anywhere other than the very top of syscall's call chain -
>> that's another reason why tossing file_struct around like that
>> is insane) and since the benefit is all due to the fact that it's
>> *NOT* shared, *NOT* modified in parallel, etc., allowing us to
>> treat file references as stable... why the hell use the descriptor
>> tables at all?
>
> This one is not a regular system call, since we don't do fget, then IO,
> then fput. We hang on to it. But for the non-registered case, it's very
> much just like a regular read/write system call, where we fget to do IO
> on it, then fput when we are done.
>
>> All you need is an array of struct file *, explicitly populated.
>> With net/unix/garbage.c aware of such beasts. Guess what? We
>> do have such an object already. The one net/unix/garbage.c is
>> working with. SCM_RIGHTS datagrams, that is.
>>
>> IOW, can't we give those io_uring descriptors associated struct
>> unix_sock? No socket descriptors, no struct socket (probably),
>> just the AF_UNIX-specific part thereof. Then teach
>> unix_inflight()/unix_notinflight() about getting unix_sock out
>> of these guys (incidentally, both would seem to benefit from
>> _not_ touching unix_gc_lock in case when there's no unix_sock
>> attached to file we are dealing with - I might be missing
>> something very subtle about barriers there, but it doesn't
>> look likely).
>
> That might be workable, though I'm not sure we currently have helpers to
> just explicitly create a unix_sock by itself. Not familiar with the
> networking bits at all, I'll take a look.
>
>> And make that (i.e. registering the descriptors) mandatory.
>
> I don't want to make it mandatory, that's very inflexible for managing
> tons of files. The registration is useful for specific cases where we
> have high frequency of operations on a set of files. Besides, it'd make
> the use of the API cumbersome as well for the basic case of just wanting
> to do async IO.
>
>> Hell, combine that with creating io_uring fd, if we really
>> care about the syscall count. Benefits:
>
> We don't care about syscall count for setup as much. If you're doing
> registration of a file set, you're expected to do a LOT of IO to those
> files. Hence having an extra one for setup is not a concern. My concern
> is just making it mandatory to do registration, I don't think that's a
> workable alternative.
>
>> * no file_struct refcount wanking
>> * no fget()/fput() (conditional, at that) from kernel
>> threads
>> * no CLOEXEC-dependent anything; just the teardown
>> on the final fput(), whichever way it comes.
>> * no fun with duelling garbage collectors.
>
> The fget/fput from a kernel thread can be solved by just hanging on to
> the struct file * when we punt the IO. Right now we don't, which is a
> little silly, that should be changed.
>
> Getting rid of the files_struct{} is doable.
OK, I've reworked the initial parts to wire up the io_uring fd to the
AF_UNIX garbage collection. As I made it to the file registration part,
I wanted to wire up that too. But I don't think there's a need for that
- if we have the io_uring fd appropriately protected, we'll be dropping
our struct file ** array index when the io_uring fd is released. That
should be adequate, we don't need the garbage collection to be aware of
those individually.
The only part I had to drop for now is the sq thread polling, as that
depends on us carrying the files_struct. I'm going to fold that in
shortly, but just make it be dependent on having registered files. That
avoids needing to fget/fput for that case, and needing registered files
for the sq side submission/polling is not a usability issue like it
would be for the "normal" use cases.
--
Jens Axboe
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [PATCH 13/18] io_uring: add file set registration
From: Jens Axboe @ 2019-02-05 19:08 UTC (permalink / raw)
To: Al Viro, Jann Horn
Cc: linux-aio, linux-block, Linux API, hch, jmoyer, avi,
linux-fsdevel
In-Reply-To: <2b2137ed-8107-f7b6-f0ca-202dcfb87c97@kernel.dk>
On 2/5/19 10:57 AM, Jens Axboe wrote:
> On 2/4/19 7:19 PM, Jens Axboe wrote:
>> On 2/3/19 7:56 PM, Al Viro wrote:
>>> On Wed, Jan 30, 2019 at 02:29:05AM +0100, Jann Horn wrote:
>>>> On Tue, Jan 29, 2019 at 8:27 PM Jens Axboe <axboe@kernel.dk> wrote:
>>>>> We normally have to fget/fput for each IO we do on a file. Even with
>>>>> the batching we do, the cost of the atomic inc/dec of the file usage
>>>>> count adds up.
>>>>>
>>>>> This adds IORING_REGISTER_FILES, and IORING_UNREGISTER_FILES opcodes
>>>>> for the io_uring_register(2) system call. The arguments passed in must
>>>>> be an array of __s32 holding file descriptors, and nr_args should hold
>>>>> the number of file descriptors the application wishes to pin for the
>>>>> duration of the io_uring context (or until IORING_UNREGISTER_FILES is
>>>>> called).
>>>>>
>>>>> When used, the application must set IOSQE_FIXED_FILE in the sqe->flags
>>>>> member. Then, instead of setting sqe->fd to the real fd, it sets sqe->fd
>>>>> to the index in the array passed in to IORING_REGISTER_FILES.
>>>>>
>>>>> Files are automatically unregistered when the io_uring context is
>>>>> torn down. An application need only unregister if it wishes to
>>>>> register a new set of fds.
>>>>
>>>> Crazy idea:
>>>>
>>>> Taking a step back, at a high level, basically this patch creates sort
>>>> of the same difference that you get when you compare the following
>>>> scenarios for normal multithreaded I/O in userspace:
>>>
>>>> This kinda makes me wonder whether this is really something that
>>>> should be implemented specifically for the io_uring API, or whether it
>>>> would make sense to somehow handle part of this in the generic VFS
>>>> code and give the user the ability to prepare a new files_struct that
>>>> can then be transferred to the worker thread, or something like
>>>> that... I'm not sure whether there's a particularly clean way to do
>>>> that though.
>>>
>>> Using files_struct for that opens a can of worms you really don't
>>> want to touch.
>>>
>>> Consider the following scenario with any variant of this interface:
>>> * create io_uring fd.
>>> * send an SCM_RIGHTS with that fd to AF_UNIX socket.
>>> * add the descriptor of that AF_UNIX socket to your fd
>>> * close AF_UNIX fd, close io_uring fd.
>>> Voila - you've got a shiny leak. No ->release() is called for
>>> anyone (and you really don't want to do that on ->flush(), because
>>> otherwise a library helper doing e.g. system("/bin/date") will tear
>>> down all the io_uring in your process). The socket is held by
>>> the reference you've stashed into io_uring (whichever way you do
>>> that). io_uring is held by the reference you've stashed into
>>> SCM_RIGHTS datagram in queue of the socket.
>>>
>>> No matter what, you need net/unix/garbage.c to be aware of that stuff.
>>> And getting files_struct lifetime mixed into that would be beyond
>>> any reason.
>>>
>>> The only reason for doing that as a descriptor table would be
>>> avoiding the cost of fget() in whatever uses it, right? Since
>>
>> Right, the only purpose of this patch is to avoid doing fget/fput for
>> each IO.
>>
>>> those are *not* the normal syscalls (and fdget() really should not
>>> be used anywhere other than the very top of syscall's call chain -
>>> that's another reason why tossing file_struct around like that
>>> is insane) and since the benefit is all due to the fact that it's
>>> *NOT* shared, *NOT* modified in parallel, etc., allowing us to
>>> treat file references as stable... why the hell use the descriptor
>>> tables at all?
>>
>> This one is not a regular system call, since we don't do fget, then IO,
>> then fput. We hang on to it. But for the non-registered case, it's very
>> much just like a regular read/write system call, where we fget to do IO
>> on it, then fput when we are done.
>>
>>> All you need is an array of struct file *, explicitly populated.
>>> With net/unix/garbage.c aware of such beasts. Guess what? We
>>> do have such an object already. The one net/unix/garbage.c is
>>> working with. SCM_RIGHTS datagrams, that is.
>>>
>>> IOW, can't we give those io_uring descriptors associated struct
>>> unix_sock? No socket descriptors, no struct socket (probably),
>>> just the AF_UNIX-specific part thereof. Then teach
>>> unix_inflight()/unix_notinflight() about getting unix_sock out
>>> of these guys (incidentally, both would seem to benefit from
>>> _not_ touching unix_gc_lock in case when there's no unix_sock
>>> attached to file we are dealing with - I might be missing
>>> something very subtle about barriers there, but it doesn't
>>> look likely).
>>
>> That might be workable, though I'm not sure we currently have helpers to
>> just explicitly create a unix_sock by itself. Not familiar with the
>> networking bits at all, I'll take a look.
>>
>>> And make that (i.e. registering the descriptors) mandatory.
>>
>> I don't want to make it mandatory, that's very inflexible for managing
>> tons of files. The registration is useful for specific cases where we
>> have high frequency of operations on a set of files. Besides, it'd make
>> the use of the API cumbersome as well for the basic case of just wanting
>> to do async IO.
>>
>>> Hell, combine that with creating io_uring fd, if we really
>>> care about the syscall count. Benefits:
>>
>> We don't care about syscall count for setup as much. If you're doing
>> registration of a file set, you're expected to do a LOT of IO to those
>> files. Hence having an extra one for setup is not a concern. My concern
>> is just making it mandatory to do registration, I don't think that's a
>> workable alternative.
>>
>>> * no file_struct refcount wanking
>>> * no fget()/fput() (conditional, at that) from kernel
>>> threads
>>> * no CLOEXEC-dependent anything; just the teardown
>>> on the final fput(), whichever way it comes.
>>> * no fun with duelling garbage collectors.
>>
>> The fget/fput from a kernel thread can be solved by just hanging on to
>> the struct file * when we punt the IO. Right now we don't, which is a
>> little silly, that should be changed.
>>
>> Getting rid of the files_struct{} is doable.
>
> OK, I've reworked the initial parts to wire up the io_uring fd to the
> AF_UNIX garbage collection. As I made it to the file registration part,
> I wanted to wire up that too. But I don't think there's a need for that
> - if we have the io_uring fd appropriately protected, we'll be dropping
> our struct file ** array index when the io_uring fd is released. That
> should be adequate, we don't need the garbage collection to be aware of
> those individually.
>
> The only part I had to drop for now is the sq thread polling, as that
> depends on us carrying the files_struct. I'm going to fold that in
> shortly, but just make it be dependent on having registered files. That
> avoids needing to fget/fput for that case, and needing registered files
> for the sq side submission/polling is not a usability issue like it
> would be for the "normal" use cases.
Proof is in the pudding, here's the main commit introducing io_uring
and now wiring it up to the AF_UNIX garbage collection:
http://git.kernel.dk/cgit/linux-block/commit/?h=io_uring&id=158e6f42b67d0abe9ee84886b96ca8c4b3d3dfd5
How does that look? Outside of the inflight hookup, we simply retain
the file * for punting to the workqueue. This means that buffered
retry does NOT need to do fget/fput, so we don't need a files_struct
for that anymore.
In terms of the SQPOLL patch that's further down the series, it doesn't
allow that mode of operation without having fixed files enabled. That
eliminates the need for fget/fput from a kernel thread, and hence the
need to carry a files_struct around for that as well.
--
Jens Axboe
--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to majordomo@kvack.org. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"aart@kvack.org">aart@kvack.org</a>
^ permalink raw reply
* Re: [RFC PATCH 1/4] glibc: Perform rseq(2) registration at C startup and thread creation (v7)
From: Mathieu Desnoyers @ 2019-02-05 19:46 UTC (permalink / raw)
To: carlos
Cc: Florian Weimer, Joseph Myers, Szabolcs Nagy, libc-alpha,
Thomas Gleixner, Ben Maurer, Peter Zijlstra, Paul E. McKenney,
Boqun Feng, Will Deacon, Dave Watson, Paul Turner, Rich Felker,
linux-kernel, linux-api, H.J. Lu
In-Reply-To: <20190204212902.31474-1-mathieu.desnoyers@efficios.com>
----- On Feb 4, 2019, at 4:28 PM, Mathieu Desnoyers mathieu.desnoyers@efficios.com wrote:
> Register rseq(2) TLS for each thread (including main), and unregister
> for each thread (excluding main). "rseq" stands for Restartable
> Sequences.
>
> See the rseq(2) man page proposed here:
> https://lkml.org/lkml/2018/9/19/647
>
> This patch is based on glibc-2.29. The rseq(2) system call was merged
> into Linux 4.18.
I recently stumbled on the thread about introduction of the getcpu(2)
wrapper in glibc, which returns both the current cpu_id and the current
node_id:
https://public-inbox.org/libc-alpha/871s6p7es1.fsf@oldenburg2.str.redhat.com/
It would be great if we could speed it up using Restartable Sequences.
At the moment, struct rseq only holds the cpu_id. I foresee that we'll want to
add support for a "node_id" to rseq at some point in a near future. It's
worthwhile to go through the exercise of looking at how this change can be
introduced considering all ABIs involved.
We cannot change the size of struct rseq, because the kernel validates that the
rseq_len received as sys_rseq parameter == sizeof(*rseq). Considering that various
libraries/applications need to agree on the exact size/content of this structure
as a userspace ABI anyway, it's a good thing that it's fixed and can never
change.
One viable solution would be to introduce a new rseq flag to the kernel
UAPI, e.g.
RSEQ_FLAG_NODE_ID
which could be used to register a u32 __rseq_abi_node_id TLS. It would
be exposed as a new public glibc symbol. Its unregistration would be
done using (RSEQ_FLAG_NODE_ID | RSEQ_FLAG_UNREGISTER).
The type of the "rseq" argument expected by sys_rseq would
become __u32 __user * when the RSEQ_FLAG_NODE_ID is specified.
This would allow glibc to use rseq to implement getcpu() whenever
it runs on a kernel that supports rseq RSEQ_FLAG_NODE_ID.
Thoughts ?
Thanks,
Mathieu
>
> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> CC: Carlos O'Donell <carlos@redhat.com>
> CC: Florian Weimer <fweimer@redhat.com>
> CC: Joseph Myers <joseph@codesourcery.com>
> CC: Szabolcs Nagy <szabolcs.nagy@arm.com>
> CC: Thomas Gleixner <tglx@linutronix.de>
> CC: Ben Maurer <bmaurer@fb.com>
> CC: Peter Zijlstra <peterz@infradead.org>
> CC: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
> CC: Boqun Feng <boqun.feng@gmail.com>
> CC: Will Deacon <will.deacon@arm.com>
> CC: Dave Watson <davejwatson@fb.com>
> CC: Paul Turner <pjt@google.com>
> CC: Rich Felker <dalias@libc.org>
> CC: libc-alpha@sourceware.org
> CC: linux-kernel@vger.kernel.org
> CC: linux-api@vger.kernel.org
> ---
> Changes since v1:
> - Move __rseq_refcount to an extra field at the end of __rseq_abi to
> eliminate one symbol.
>
> All libraries/programs which try to register rseq (glibc,
> early-adopter applications, early-adopter libraries) should use the
> rseq refcount. It becomes part of the ABI within a user-space
> process, but it's not part of the ABI shared with the kernel per se.
>
> - Restructure how this code is organized so glibc keeps building on
> non-Linux targets.
>
> - Use non-weak symbol for __rseq_abi.
>
> - Move rseq registration/unregistration implementation into its own
> nptl/rseq.c compile unit.
>
> - Move __rseq_abi symbol under GLIBC_2.29.
>
> Changes since v2:
> - Move __rseq_refcount to its own symbol, which is less ugly than
> trying to play tricks with the rseq uapi.
> - Move __rseq_abi from nptl to csu (C start up), so it can be used
> across glibc, including memory allocator and sched_getcpu(). The
> __rseq_refcount symbol is kept in nptl, because there is no reason
> to use it elsewhere in glibc.
>
> Changes since v3:
> - Set __rseq_refcount TLS to 1 on register/set to 0 on unregister
> because glibc is the first/last user.
> - Unconditionally register/unregister rseq at thread start/exit, because
> glibc is the first/last user.
> - Add missing abilist items.
> - Rebase on glibc master commit a502c5294.
> - Add NEWS entry.
>
> Changes since v4:
> - Do not use "weak" symbols for __rseq_abi and __rseq_refcount. Based on
> "System V Application Binary Interface", weak only affects the link
> editor, not the dynamic linker.
> - Install a new sys/rseq.h system header on Linux, which contains the
> RSEQ_SIG definition, __rseq_abi declaration and __rseq_refcount
> declaration. Move those definition/declarations from rseq-internal.h
> to the installed sys/rseq.h header.
> - Considering that rseq is only available on Linux, move csu/rseq.c to
> sysdeps/unix/sysv/linux/rseq-sym.c.
> - Move __rseq_refcount from nptl/rseq.c to
> sysdeps/unix/sysv/linux/rseq-sym.c, so it is only defined on Linux.
> - Move both ABI definitions for __rseq_abi and __rseq_refcount to
> sysdeps/unix/sysv/linux/Versions, so they only appear on Linux.
> - Document __rseq_abi and __rseq_refcount volatile.
> - Document the RSEQ_SIG signature define.
> - Move registration functions from rseq.c to rseq-internal.h static
> inline functions. Introduce empty stubs in misc/rseq-internal.h,
> which can be overridden by architecture code in
> sysdeps/unix/sysv/linux/rseq-internal.h.
> - Rename __rseq_register_current_thread and __rseq_unregister_current_thread
> to rseq_register_current_thread and rseq_unregister_current_thread,
> now that those are only visible as internal static inline functions.
> - Invoke rseq_register_current_thread() from libc-start.c LIBC_START_MAIN
> rather than nptl init, so applications not linked against
> libpthread.so have rseq registered for their main() thread. Note that
> it is invoked separately for SHARED and !SHARED builds.
>
> Changes since v5:
> - Replace __rseq_refcount by __rseq_lib_abi, which contains two
> uint32_t: register_state and refcount. The "register_state" field
> allows inhibiting rseq registration from signal handlers nested on top
> of glibc registration and occuring after rseq unregistration by glibc.
> - Introduce enum rseq_register_state, which contains the states allowed
> for the struct rseq_lib_abi register_state field.
>
> Changes since v6:
> - Introduce bits/rseq.h to define RSEQ_SIG for each architecture.
> The generic bits/rseq.h does not define RSEQ_SIG, meaning that each
> architecture implementing rseq needs to implement bits/rseq.h.
> - Rename enum item RSEQ_REGISTER_NESTED to RSEQ_REGISTER_ONGOING.
> - Port to glibc-2.29.
> ---
> NEWS | 11 +++
> csu/libc-start.c | 12 ++-
> misc/Makefile | 3 +-
> misc/rseq-internal.h | 34 +++++++
> nptl/pthread_create.c | 9 ++
> sysdeps/unix/sysv/linux/Makefile | 4 +-
> sysdeps/unix/sysv/linux/Versions | 4 +
> sysdeps/unix/sysv/linux/aarch64/bits/rseq.h | 24 +++++
> sysdeps/unix/sysv/linux/aarch64/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/alpha/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/arm/bits/rseq.h | 24 +++++
> sysdeps/unix/sysv/linux/arm/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/bits/rseq.h | 24 +++++
> sysdeps/unix/sysv/linux/hppa/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/i386/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/ia64/libc.abilist | 2 +
> .../sysv/linux/m68k/coldfire/libc.abilist | 2 +
> .../unix/sysv/linux/m68k/m680x0/libc.abilist | 2 +
> .../unix/sysv/linux/microblaze/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/mips/bits/rseq.h | 24 +++++
> .../sysv/linux/mips/mips32/fpu/libc.abilist | 2 +
> .../sysv/linux/mips/mips32/nofpu/libc.abilist | 2 +
> .../sysv/linux/mips/mips64/n32/libc.abilist | 2 +
> .../sysv/linux/mips/mips64/n64/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/nios2/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/powerpc/bits/rseq.h | 24 +++++
> .../linux/powerpc/powerpc32/fpu/libc.abilist | 2 +
> .../powerpc/powerpc32/nofpu/libc.abilist | 2 +
> .../linux/powerpc/powerpc64/be/libc.abilist | 2 +
> .../linux/powerpc/powerpc64/le/libc.abilist | 2 +
> .../unix/sysv/linux/riscv/rv64/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/rseq-internal.h | 91 +++++++++++++++++++
> sysdeps/unix/sysv/linux/rseq-sym.c | 54 +++++++++++
> sysdeps/unix/sysv/linux/s390/bits/rseq.h | 24 +++++
> .../unix/sysv/linux/s390/s390-32/libc.abilist | 2 +
> .../unix/sysv/linux/s390/s390-64/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/sh/libc.abilist | 2 +
> .../sysv/linux/sparc/sparc32/libc.abilist | 2 +
> .../sysv/linux/sparc/sparc64/libc.abilist | 2 +
> sysdeps/unix/sysv/linux/sys/rseq.h | 65 +++++++++++++
> sysdeps/unix/sysv/linux/x86/bits/rseq.h | 24 +++++
> .../unix/sysv/linux/x86_64/64/libc.abilist | 2 +
> .../unix/sysv/linux/x86_64/x32/libc.abilist | 2 +
> 43 files changed, 501 insertions(+), 6 deletions(-)
> create mode 100644 misc/rseq-internal.h
> create mode 100644 sysdeps/unix/sysv/linux/aarch64/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/arm/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/mips/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/powerpc/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/rseq-internal.h
> create mode 100644 sysdeps/unix/sysv/linux/rseq-sym.c
> create mode 100644 sysdeps/unix/sysv/linux/s390/bits/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/sys/rseq.h
> create mode 100644 sysdeps/unix/sysv/linux/x86/bits/rseq.h
>
> diff --git a/NEWS b/NEWS
> index 912a9bdc0f..0608c60f7d 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -5,6 +5,17 @@ See the end for copying conditions.
> Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
> using `glibc' in the "product" field.
>
> +Version 2.30
> +
> +Major new features:
> +
> +* Support for automatically registering threads with the Linux rseq(2)
> + system call has been added. This system call is implemented starting
> + from Linux 4.18. In order to be activated, it requires that glibc is built
> + against kernel headers that include this system call, and that glibc
> + detects availability of that system call at runtime.
> +
> +
> Version 2.29
>
> Major new features:
> diff --git a/csu/libc-start.c b/csu/libc-start.c
> index 5d9c3675fa..8680afc0ef 100644
> --- a/csu/libc-start.c
> +++ b/csu/libc-start.c
> @@ -22,6 +22,7 @@
> #include <ldsodefs.h>
> #include <exit-thread.h>
> #include <libc-internal.h>
> +#include <rseq-internal.h>
>
> #include <elf/dl-tunables.h>
>
> @@ -140,7 +141,10 @@ LIBC_START_MAIN (int (*main) (int, char **, char **
> MAIN_AUXVEC_DECL),
>
> __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;
>
> -#ifndef SHARED
> +#ifdef SHARED
> + /* Register rseq ABI to the kernel. */
> + (void) rseq_register_current_thread ();
> +#else
> _dl_relocate_static_pie ();
>
> char **ev = &argv[argc + 1];
> @@ -218,6 +222,9 @@ LIBC_START_MAIN (int (*main) (int, char **, char **
> MAIN_AUXVEC_DECL),
> }
> # endif
>
> + /* Register rseq ABI to the kernel. */
> + (void) rseq_register_current_thread ();
> +
> /* Initialize libpthread if linked in. */
> if (__pthread_initialize_minimal != NULL)
> __pthread_initialize_minimal ();
> @@ -230,8 +237,7 @@ LIBC_START_MAIN (int (*main) (int, char **, char **
> MAIN_AUXVEC_DECL),
> # else
> __pointer_chk_guard_local = pointer_chk_guard;
> # endif
> -
> -#endif /* !SHARED */
> +#endif
>
> /* Register the destructor of the dynamic linker if there is any. */
> if (__glibc_likely (rtld_fini != NULL))
> diff --git a/misc/Makefile b/misc/Makefile
> index cf0daa1161..0ae1dbaf80 100644
> --- a/misc/Makefile
> +++ b/misc/Makefile
> @@ -36,7 +36,8 @@ headers := sys/uio.h bits/uio-ext.h bits/uio_lim.h \
> syslog.h sys/syslog.h \
> bits/syslog.h bits/syslog-ldbl.h bits/syslog-path.h bits/error.h \
> bits/select2.h bits/hwcap.h sys/auxv.h \
> - sys/sysmacros.h bits/sysmacros.h bits/types/struct_iovec.h
> + sys/sysmacros.h bits/sysmacros.h bits/types/struct_iovec.h \
> + rseq-internal.h
>
> routines := brk sbrk sstk ioctl \
> readv writev preadv preadv64 pwritev pwritev64 \
> diff --git a/misc/rseq-internal.h b/misc/rseq-internal.h
> new file mode 100644
> index 0000000000..915122e4bf
> --- /dev/null
> +++ b/misc/rseq-internal.h
> @@ -0,0 +1,34 @@
> +/* Copyright (C) 2018 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2018.
> +
> + 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/>. */
> +
> +#ifndef RSEQ_INTERNAL_H
> +#define RSEQ_INTERNAL_H
> +
> +static inline int
> +rseq_register_current_thread (void)
> +{
> + return -1;
> +}
> +
> +static inline int
> +rseq_unregister_current_thread (void)
> +{
> + return -1;
> +}
> +
> +#endif /* rseq-internal.h */
> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
> index 2bd2b10727..90b3419390 100644
> --- a/nptl/pthread_create.c
> +++ b/nptl/pthread_create.c
> @@ -33,6 +33,7 @@
> #include <default-sched.h>
> #include <futex-internal.h>
> #include <tls-setup.h>
> +#include <rseq-internal.h>
> #include "libioP.h"
>
> #include <shlib-compat.h>
> @@ -378,6 +379,7 @@ __free_tcb (struct pthread *pd)
> START_THREAD_DEFN
> {
> struct pthread *pd = START_THREAD_SELF;
> + bool has_rseq = false;
>
> #if HP_TIMING_AVAIL
> /* Remember the time when the thread was started. */
> @@ -396,6 +398,9 @@ START_THREAD_DEFN
> if (__glibc_unlikely (atomic_exchange_acq (&pd->setxid_futex, 0) == -2))
> futex_wake (&pd->setxid_futex, 1, FUTEX_PRIVATE);
>
> + /* Register rseq TLS to the kernel. */
> + has_rseq = !rseq_register_current_thread ();
> +
> #ifdef __NR_set_robust_list
> # ifndef __ASSUME_SET_ROBUST_LIST
> if (__set_robust_list_avail >= 0)
> @@ -573,6 +578,10 @@ START_THREAD_DEFN
> }
> #endif
>
> + /* Unregister rseq TLS from kernel. */
> + if (has_rseq && rseq_unregister_current_thread ())
> + abort();
> +
> advise_stack_range (pd->stackblock, pd->stackblock_size, (uintptr_t) pd,
> pd->guardsize);
>
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index 5f8c2c7c7d..5b541469ec 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -1,5 +1,5 @@
> ifeq ($(subdir),csu)
> -sysdep_routines += errno-loc
> +sysdep_routines += errno-loc rseq-sym
> endif
>
> ifeq ($(subdir),assert)
> @@ -48,7 +48,7 @@ sysdep_headers += sys/mount.h sys/acct.h sys/sysctl.h \
> bits/termios-c_iflag.h bits/termios-c_oflag.h \
> bits/termios-baud.h bits/termios-c_cflag.h \
> bits/termios-c_lflag.h bits/termios-tcflow.h \
> - bits/termios-misc.h
> + bits/termios-misc.h sys/rseq.h bits/rseq.h
>
> tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
> tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
> diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
> index f1e12d9c69..ad88c2b7ff 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -174,6 +174,10 @@ libc {
> GLIBC_2.29 {
> getcpu;
> }
> + GLIBC_2.30 {
> + __rseq_abi;
> + __rseq_lib_abi;
> + }
> GLIBC_PRIVATE {
> # functions used in other libraries
> __syscall_rt_sigqueueinfo;
> diff --git a/sysdeps/unix/sysv/linux/aarch64/bits/rseq.h
> b/sysdeps/unix/sysv/linux/aarch64/bits/rseq.h
> new file mode 100644
> index 0000000000..543bc5388a
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/aarch64/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0xd428bc00 /* BRK #0x45E0. */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 9c330f325e..bc937f585d 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2141,3 +2141,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index f630fa4c6f..89cc8b1cfb 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2036,6 +2036,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/arm/bits/rseq.h
> b/sysdeps/unix/sysv/linux/arm/bits/rseq.h
> new file mode 100644
> index 0000000000..19d3755837
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/arm/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0x53053053
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist
> b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index b96f45590f..e5055f2d4e 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -126,6 +126,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> 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/bits/rseq.h
> b/sysdeps/unix/sysv/linux/bits/rseq.h
> new file mode 100644
> index 0000000000..d60f02cfeb
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Each architecture supporting rseq should define RSEQ_SIG as a 32-bit
> + signature inserted before each rseq abort label in the code section. */
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 088a8ee369..546d073cdb 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -1883,6 +1883,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist
> b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index f7ff2c57b9..ac1de6e4b3 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2048,6 +2048,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index becd8b1033..cc3445b958 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -1917,6 +1917,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 74e42a5209..f7e28bd5a0 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -127,6 +127,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> 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 4af5a74e8a..b8f00f6111 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1992,6 +1992,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index ccef673fd2..19f191434f 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2133,3 +2133,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> diff --git a/sysdeps/unix/sysv/linux/mips/bits/rseq.h
> b/sysdeps/unix/sysv/linux/mips/bits/rseq.h
> new file mode 100644
> index 0000000000..19d3755837
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/mips/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0x53053053
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 1054bb599e..fe43507f55 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1970,6 +1970,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 4f5b5ffebf..b247c6ea9b 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1968,6 +1968,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 943aee58d4..5339ca52b6 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1976,6 +1976,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 17a5d17ef9..11f24eb440 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1971,6 +1971,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index 4d62a540fd..fd223bfc44 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2174,3 +2174,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/rseq.h
> b/sysdeps/unix/sysv/linux/powerpc/bits/rseq.h
> new file mode 100644
> index 0000000000..19d3755837
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/powerpc/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0x53053053
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index ecc2d6fa13..cc53178e81 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1996,6 +1996,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index f5830f9c33..2de3134bc7 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2000,6 +2000,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> index 633d8f4792..aae3def700 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -126,6 +126,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 _Exit F
> GLIBC_2.3 _IO_2_1_stderr_ D 0xe0
> GLIBC_2.3 _IO_2_1_stdin_ D 0xe0
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> index 2c712636ef..8d582a3a9b 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2231,3 +2231,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 195bc8b2cf..155953f6cf 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2103,3 +2103,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h
> b/sysdeps/unix/sysv/linux/rseq-internal.h
> new file mode 100644
> index 0000000000..d676da3701
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/rseq-internal.h
> @@ -0,0 +1,91 @@
> +/* Copyright (C) 2018 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2018.
> +
> + 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/>. */
> +
> +#ifndef RSEQ_INTERNAL_H
> +#define RSEQ_INTERNAL_H
> +
> +#include <sysdep.h>
> +
> +#ifdef __NR_rseq
> +
> +#include <errno.h>
> +#include <sys/rseq.h>
> +
> +static inline int
> +rseq_register_current_thread (void)
> +{
> + int rc, ret = 0;
> + INTERNAL_SYSCALL_DECL (err);
> +
> + if (__rseq_abi.cpu_id == RSEQ_CPU_ID_REGISTRATION_FAILED)
> + return -1;
> + /* Temporarily prevent nested signal handlers from registering rseq. */
> + __rseq_lib_abi.register_state = RSEQ_REGISTER_ONGOING;
> + if (__rseq_lib_abi.refcount == UINT_MAX)
> + {
> + ret = -1;
> + goto end;
> + }
> + if (__rseq_lib_abi.refcount++)
> + goto end;
> + rc = INTERNAL_SYSCALL_CALL (rseq, err, &__rseq_abi, sizeof (struct rseq),
> + 0, RSEQ_SIG);
> + if (!rc)
> + goto end;
> + if (INTERNAL_SYSCALL_ERRNO (rc, err) != EBUSY)
> + __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
> + ret = -1;
> +end:
> + __rseq_lib_abi.register_state = RSEQ_REGISTER_ALLOWED;
> + return ret;
> +}
> +
> +static inline int
> +rseq_unregister_current_thread (void)
> +{
> + int rc, ret = 0;
> + INTERNAL_SYSCALL_DECL (err);
> +
> + /* Setting __rseq_register_state = RSEQ_REGISTER_EXITING for the rest of the
> + thread lifetime. Ensures signal handlers nesting just before thread exit
> + don't try to register rseq. */
> + __rseq_lib_abi.register_state = RSEQ_REGISTER_EXITING;
> + __rseq_lib_abi.refcount = 0;
> + rc = INTERNAL_SYSCALL_CALL (rseq, err, &__rseq_abi, sizeof (struct rseq),
> + RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
> + if (!rc)
> + goto end;
> + ret = -1;
> +end:
> + return ret;
> +}
> +#else
> +static inline int
> +rseq_register_current_thread (void)
> +{
> + return -1;
> +}
> +
> +static inline int
> +rseq_unregister_current_thread (void)
> +{
> + return -1;
> +}
> +#endif
> +
> +#endif /* rseq-internal.h */
> diff --git a/sysdeps/unix/sysv/linux/rseq-sym.c
> b/sysdeps/unix/sysv/linux/rseq-sym.c
> new file mode 100644
> index 0000000000..99b277e9d6
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/rseq-sym.c
> @@ -0,0 +1,54 @@
> +/* Copyright (C) 2018 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2018.
> +
> + 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 <sys/syscall.h>
> +#include <stdint.h>
> +
> +#ifdef __NR_rseq
> +#include <sys/rseq.h>
> +#else
> +
> +enum rseq_cpu_id_state {
> + RSEQ_CPU_ID_UNINITIALIZED = -1,
> + RSEQ_CPU_ID_REGISTRATION_FAILED = -2,
> +};
> +
> +/* linux/rseq.h defines struct rseq as aligned on 32 bytes. The kernel ABI
> + size is 20 bytes. */
> +struct rseq {
> + uint32_t cpu_id_start;
> + uint32_t cpu_id;
> + uint64_t rseq_cs;
> + uint32_t flags;
> +} __attribute__ ((aligned(4 * sizeof(uint64_t))));
> +
> +struct rseq_lib_abi
> +{
> + uint32_t register_state;
> + uint32_t refcount;
> +};
> +
> +#endif
> +
> +/* volatile because fields can be read/updated by the kernel. */
> +__thread volatile struct rseq __rseq_abi = {
> + .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
> +};
> +
> +/* volatile because fields can be read/updated by signal handlers. */
> +__thread volatile struct rseq_lib_abi __rseq_lib_abi;
> diff --git a/sysdeps/unix/sysv/linux/s390/bits/rseq.h
> b/sysdeps/unix/sysv/linux/s390/bits/rseq.h
> new file mode 100644
> index 0000000000..19d3755837
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/s390/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0x53053053
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 334def033c..42316d8666 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2005,6 +2005,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index 536f4c4ced..c6c4e55a77 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1911,6 +1911,8 @@ GLIBC_2.29 __fentry__ F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist
> b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index 30ae3b6ebb..8652dfea59 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1887,6 +1887,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 68b107d080..95b58dfa67 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1999,6 +1999,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index e5b6a4da50..bfd24f9d1c 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1940,6 +1940,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/sys/rseq.h
> b/sysdeps/unix/sysv/linux/sys/rseq.h
> new file mode 100644
> index 0000000000..83c8976f50
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/sys/rseq.h
> @@ -0,0 +1,65 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +#define _SYS_RSEQ_H 1
> +
> +/* We use the structures declarations from the kernel headers. */
> +#include <linux/rseq.h>
> +/* Architecture-specific rseq signature. */
> +#include <bits/rseq.h>
> +#include <stdint.h>
> +
> +enum rseq_register_state
> +{
> + /* Value RSEQ_REGISTER_ALLOWED means it is allowed to update
> + the refcount field and to register/unregister rseq. */
> + RSEQ_REGISTER_ALLOWED = 0,
> + /* Value RSEQ_REGISTER_ONGOING means a rseq registration is in progress,
> + so it is temporarily forbidden to update the refcount field or to
> + register/unregister rseq for this thread or signal handlers nested
> + on this thread. */
> + RSEQ_REGISTER_ONGOING = 1,
> + /* Value RSEQ_REGISTER_EXITING means it is forbidden to update the
> + refcount field or to register/unregister rseq for the rest of the
> + thread's lifetime. */
> + RSEQ_REGISTER_EXITING = 2,
> +};
> +
> +struct rseq_lib_abi
> +{
> + uint32_t register_state; /* enum rseq_register_state. */
> + /* The refcount field keeps track of rseq users, so early adopters
> + of rseq can cooperate amongst each other and with glibc to
> + share rseq thread registration. The refcount field can only be
> + updated when allowed by the value of field register_state.
> + Registering rseq should be performed when incrementing refcount
> + from 0 to 1, and unregistering rseq should be performed when
> + decrementing refcount from 1 to 0. */
> + uint32_t refcount;
> +};
> +
> +/* volatile because fields can be read/updated by the kernel. */
> +extern __thread volatile struct rseq __rseq_abi
> +__attribute__ ((tls_model ("initial-exec")));
> +
> +/* volatile because fields can be read/updated by signal handlers. */
> +extern __thread volatile struct rseq_lib_abi __rseq_lib_abi
> +__attribute__ ((tls_model ("initial-exec")));
> +
> +#endif /* sys/rseq.h */
> diff --git a/sysdeps/unix/sysv/linux/x86/bits/rseq.h
> b/sysdeps/unix/sysv/linux/x86/bits/rseq.h
> new file mode 100644
> index 0000000000..19d3755837
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/x86/bits/rseq.h
> @@ -0,0 +1,24 @@
> +/* Copyright (C) 2019 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> + Contributed by Mathieu Desnoyers <mathieu.desnoyers@efficios.com>, 2019.
> +
> + 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/>. */
> +
> +#ifndef _SYS_RSEQ_H
> +# error "Never use <bits/rseq.h> directly; include <sys/rseq.h> instead."
> +#endif
> +
> +/* Signature required before each abort handler code. */
> +#define RSEQ_SIG 0x53053053
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 86dfb0c94d..e9f8411fb2 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1898,6 +1898,8 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> GLIBC_2.3 __ctype_b_loc F
> GLIBC_2.3 __ctype_tolower_loc F
> GLIBC_2.3 __ctype_toupper_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index dd688263aa..f9432d07f1 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2149,3 +2149,5 @@ GLIBC_2.28 thrd_yield F
> 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 __rseq_abi T 0x20
> +GLIBC_2.30 __rseq_lib_abi T 0x8
> --
> 2.17.1
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply
* [PATCH 00/32] kernel: Introduce Time Namespace
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
Discussions around time namespace are there for a long time. The first
attempt to implement it was in 2006 by Jeff Dike. From that time, the
topic appears on and off in various discussions.
There are two main use cases for time namespaces:
1. change date and time inside a container;
2. adjust clocks for a container restored from a checkpoint.
“It seems like this might be one of the last major obstacles keeping
migration from being used in production systems, given that not all
containers and connections can be migrated as long as a time dependency
is capable of messing it up.” (by github.com/dav-ell)
The kernel provides access to several clocks: CLOCK_REALTIME,
CLOCK_MONOTONIC, CLOCK_BOOTTIME. Last two clocks are monotonous, but the
start points for them are not defined and are different for each
system. When a container is migrated from one node to another, all
clocks have to be restored into consistent states; in other words, they
have to continue running from the same points where they have been
dumped.
The main idea of this patch set is adding per-namespace offsets for
system clocks. When a process in a non-root time namespace requests
time of a clock, a namespace offset is added to the current value of
this clock and the sum is returned.
All offsets are placed on a separate page, this allows us to map it as
part of VVAR into user processes and use offsets from VDSO calls.
Now offsets are implemented for CLOCK_MONOTONIC and CLOCK_BOOTTIME
clocks.
v2: There are two major changes from the previous version:
* Two versions of the VDSO library to avoid a performance penalty for
host tasks outside time namespace (as suggested by Andy and Thomas).
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
It will add a penalty for everybody as branch predictor may mispredict
the jump. Also there are instruction cache lines wasted on cmp/jmp.
Those effects of introducing time namespace are very much unwanted
having in mind how much work have been spent on micro-optimisation
VDSO code.
Addressing those problems, there are two versions of VDSO's .so:
for host tasks (without any penalty) and for processes inside of time
namespace with clk_to_ns() that subtracts offsets from host's time.
* Allow to set clock offsets for a namespace only before any processes
appear in it.
Now a time namespace looks similar to a pid namespace in a way how it is
created: unshare(CLONE_NEWTIME) system call creates a new time namespace,
but doesn't set it to the current process. Then all children of
the process will be born in the new time namespace, or a process can
use the setns() system call to join a namespace.
This scheme allows to create a new time namespaces, set clock offsets
and then populate the namespace with processes.
Our performance measurements show that the price of VDSO's clock_gettime()
in a child time namespace is about 8% with a hot CPU cache and about 90%
with a cold CPU cache. There is no performance regression for host
processes outside time namespace on those tests.
We wrote two small benchmarks. The first one gettime_perf.c calls
clock_gettime() in a loop for 3 seconds. It shows us performance with
a hot CPU cache (more clock_gettime() cycles - the better):
| before | CONFIG_TIME_NS=n | host | inside timens
--------|------------|------------------|-------------|-------------
cycles | 139887013 | 139453003 | 139899785 | 128792458
diff (%)| 100 | 99.7 | 100 | 92
The second one gettime_perf_cold.c calls rdtsc, clock_gettime(), rdtsc
and shows a difference between second and first rdtsc. The binary is
called in a loop 1000 times, then calculate MODE for 1000 values.
It should show us performance with a cold CPU cache
(lesser tsc per cycle - the better):
| before | CONFIG_TIME_NS=n | host | inside timens
--------|------------|------------------|-------------|-------------
tsc | 6748 | 6718 | 6862 | 12682
diff (%)| 100 | 99.6 | 101.7 | 188
The numbers gathered on Intel(R) Core(TM) i5-6300U CPU @ 2.40GHz.
Cc: Adrian Reber <adrian@lisas.de>
Cc: Andrei Vagin <avagin@openvz.org>
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Andy Tucker <agtucker@google.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: containers@lists.linux-foundation.org
Cc: criu@openvz.org
Cc: linux-api@vger.kernel.org
Cc: x86@kernel.org
Andrei Vagin (15):
ns: Introduce Time Namespace
timens: Add timens_offsets
timens: Introduce CLOCK_MONOTONIC offsets
timens: Introduce CLOCK_BOOTTIME offset
timerfd/timens: Take into account ns clock offsets
posix-timers/timens: Take into account clock offsets
timens/kernel: Take into account timens clock offsets in
clock_nanosleep
x86/vdso/timens: Add offsets page in vvar
timens/fs/proc: Introduce /proc/pid/timens_offsets
selftest/timens: Add a test for timerfd
selftest/timens: Add a test for clock_nanosleep()
selftest/timens: Add timer offsets test
selftests: Add a simple perf test for clock_gettime()
selftest/timens: Check that a right vdso is mapped after fork and exec
x86/vdso: Align VDSO functions by CPU L1 cache line
Dmitry Safonov (17):
timens: Shift /proc/uptime
x86/vdso2c: Correct err messages on file opening
x86/vdso2c: Convert iterator to unsigned
x86/vdso/Makefile: Add vobjs32
x86/vdso: Build timens .so(s)
x86/VDSO: Build VDSO with -ffunction-sections
x86/vdso2c: Optionally produce linker script for vdso entries
x86/vdso: Generate vdso{,32}-timens.lds
x86/vdso2c: Sort vdso entries by addresses for linker script
x86/vdso.lds: Align !timens (host's) vdso.so entries
x86/vdso2c: Align LOCAL symbols between vdso{-timens,}.so
x86/vdso: Initialize timens 64-bit vdso
x86/vdso: Switch image on setns()/unshare()/clone()
timens: Add align for timens_offsets
selftest/timens: Add Time Namespace test for supported clocks
selftest/timens: Add procfs selftest
x86/vdso: Restrict splitting VVAR VMA
MAINTAINERS | 3 +
arch/Kconfig | 5 +
arch/x86/Kconfig | 1 +
arch/x86/entry/vdso/.gitignore | 2 +
arch/x86/entry/vdso/Makefile | 61 ++-
arch/x86/entry/vdso/vclock_gettime-timens.c | 6 +
arch/x86/entry/vdso/vclock_gettime.c | 42 +++
arch/x86/entry/vdso/vdso-layout.lds.S | 21 +-
arch/x86/entry/vdso/vdso-timens.lds.S | 7 +
arch/x86/entry/vdso/vdso2c.c | 46 ++-
arch/x86/entry/vdso/vdso2c.h | 52 ++-
arch/x86/entry/vdso/vdso32/.gitignore | 1 +
arch/x86/entry/vdso/vdso32/sigreturn.S | 2 +
arch/x86/entry/vdso/vdso32/system_call.S | 2 +-
.../entry/vdso/vdso32/vclock_gettime-timens.c | 6 +
.../x86/entry/vdso/vdso32/vdso32-timens.lds.S | 8 +
arch/x86/entry/vdso/vma.c | 110 ++++++
arch/x86/include/asm/vdso.h | 8 +
fs/proc/base.c | 101 +++++
fs/proc/namespaces.c | 4 +
fs/proc/uptime.c | 3 +
fs/timerfd.c | 16 +-
include/linux/nsproxy.h | 2 +
include/linux/proc_ns.h | 2 +
include/linux/time_namespace.h | 91 +++++
include/linux/timens_offsets.h | 18 +
include/linux/user_namespace.h | 1 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 8 +
kernel/Makefile | 1 +
kernel/fork.c | 3 +-
kernel/nsproxy.c | 41 ++-
kernel/time/hrtimer.c | 8 +
kernel/time/posix-timers.c | 24 +-
kernel/time/posix-timers.h | 1 +
kernel/time_namespace.c | 348 ++++++++++++++++++
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/timens/.gitignore | 7 +
tools/testing/selftests/timens/Makefile | 12 +
.../selftests/timens/clock_nanosleep.c | 99 +++++
tools/testing/selftests/timens/config | 1 +
tools/testing/selftests/timens/exec.c | 91 +++++
tools/testing/selftests/timens/gettime_perf.c | 74 ++++
.../selftests/timens/gettime_perf_cold.c | 63 ++++
tools/testing/selftests/timens/log.h | 26 ++
tools/testing/selftests/timens/procfs.c | 142 +++++++
tools/testing/selftests/timens/timens.c | 191 ++++++++++
tools/testing/selftests/timens/timens.h | 63 ++++
tools/testing/selftests/timens/timer.c | 115 ++++++
tools/testing/selftests/timens/timerfd.c | 119 ++++++
50 files changed, 2008 insertions(+), 52 deletions(-)
create mode 100644 arch/x86/entry/vdso/vclock_gettime-timens.c
create mode 100644 arch/x86/entry/vdso/vdso-timens.lds.S
create mode 100644 arch/x86/entry/vdso/vdso32/vclock_gettime-timens.c
create mode 100644 arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S
create mode 100644 include/linux/time_namespace.h
create mode 100644 include/linux/timens_offsets.h
create mode 100644 kernel/time_namespace.c
create mode 100644 tools/testing/selftests/timens/.gitignore
create mode 100644 tools/testing/selftests/timens/Makefile
create mode 100644 tools/testing/selftests/timens/clock_nanosleep.c
create mode 100644 tools/testing/selftests/timens/config
create mode 100644 tools/testing/selftests/timens/exec.c
create mode 100644 tools/testing/selftests/timens/gettime_perf.c
create mode 100644 tools/testing/selftests/timens/gettime_perf_cold.c
create mode 100644 tools/testing/selftests/timens/log.h
create mode 100644 tools/testing/selftests/timens/procfs.c
create mode 100644 tools/testing/selftests/timens/timens.c
create mode 100644 tools/testing/selftests/timens/timens.h
create mode 100644 tools/testing/selftests/timens/timer.c
create mode 100644 tools/testing/selftests/timens/timerfd.c
--
2.20.1
^ permalink raw reply
* [PATCH 01/32] ns: Introduce Time Namespace
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Time Namespace isolates clock values.
The kernel provides access to several clocks CLOCK_REALTIME,
CLOCK_MONOTONIC, CLOCK_BOOTTIME, etc.
CLOCK_REALTIME
System-wide clock that measures real (i.e., wall-clock) time.
CLOCK_MONOTONIC
Clock that cannot be set and represents monotonic time since
some unspecified starting point.
CLOCK_BOOTTIME
Identical to CLOCK_MONOTONIC, except it also includes any time
that the system is suspended.
For many users, the time namespace means the ability to changes date and
time in a container (CLOCK_REALTIME).
But in a context of the checkpoint/restore functionality, monotonic and
bootime clocks become interesting. Both clocks are monotonic with
unspecified staring points. These clocks are widely used to measure time
slices and set timers. After restoring or migrating processes, we have to
guarantee that they never go backward. In an ideal case, the behavior of
these clocks should be the same as for a case when a whole system is
suspended. All this means that we need to be able to set CLOCK_MONOTONIC
and CLOCK_BOOTTIME clocks, what can be done by adding per-namespace
offsets for clocks.
A time namespace is similar to a pid namespace in a way how it is
created: unshare(CLONE_NEWTIME) system call creates a new time namespace,
but doesn't set it to the current process. Then all children of
the process will be born in the new time namespace, or a process can
use the setns() system call to join a namespace.
This scheme allows setting clock offsets for a namespace, before any
processes appear in it.
Link: https://criu.org/Time_namespace
Link: https://lists.openvz.org/pipermail/criu/2018-June/041504.html
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
MAINTAINERS | 2 +
fs/proc/namespaces.c | 4 +
include/linux/nsproxy.h | 2 +
include/linux/proc_ns.h | 2 +
include/linux/time_namespace.h | 69 +++++++++++
include/linux/user_namespace.h | 1 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 7 ++
kernel/Makefile | 1 +
kernel/fork.c | 3 +-
kernel/nsproxy.c | 41 +++++--
kernel/time_namespace.c | 215 +++++++++++++++++++++++++++++++++
12 files changed, 340 insertions(+), 8 deletions(-)
create mode 100644 include/linux/time_namespace.h
create mode 100644 kernel/time_namespace.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8c68de3cfd80..e03f160012e3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12144,6 +12144,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
S: Maintained
F: fs/timerfd.c
F: include/linux/timer*
+F: include/linux/time_namespace.h
+F: kernel/time_namespace.c
F: kernel/time/*timer*
POWER MANAGEMENT CORE
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index dd2b35f78b09..8b5c720fe5d7 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -33,6 +33,10 @@ static const struct proc_ns_operations *ns_entries[] = {
#ifdef CONFIG_CGROUPS
&cgroupns_operations,
#endif
+#ifdef CONFIG_TIME_NS
+ &timens_operations,
+ &timens_for_children_operations,
+#endif
};
static const char *proc_ns_get_link(struct dentry *dentry,
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index 2ae1b1a4d84d..074f395b9ad2 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -35,6 +35,8 @@ struct nsproxy {
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns_for_children;
struct net *net_ns;
+ struct time_namespace *time_ns;
+ struct time_namespace *time_ns_for_children;
struct cgroup_namespace *cgroup_ns;
};
extern struct nsproxy init_nsproxy;
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index d31cb6215905..3e6f332da465 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -32,6 +32,8 @@ extern const struct proc_ns_operations pidns_for_children_operations;
extern const struct proc_ns_operations userns_operations;
extern const struct proc_ns_operations mntns_operations;
extern const struct proc_ns_operations cgroupns_operations;
+extern const struct proc_ns_operations timens_operations;
+extern const struct proc_ns_operations timens_for_children_operations;
/*
* We always define these enumerators
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
new file mode 100644
index 000000000000..9507ed7072fe
--- /dev/null
+++ b/include/linux/time_namespace.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_TIMENS_H
+#define _LINUX_TIMENS_H
+
+
+#include <linux/sched.h>
+#include <linux/kref.h>
+#include <linux/nsproxy.h>
+#include <linux/ns_common.h>
+#include <linux/err.h>
+
+struct user_namespace;
+extern struct user_namespace init_user_ns;
+
+struct time_namespace {
+ struct kref kref;
+ struct user_namespace *user_ns;
+ struct ucounts *ucounts;
+ struct ns_common ns;
+ struct timens_offsets *offsets;
+ bool initialized;
+} __randomize_layout;
+extern struct time_namespace init_time_ns;
+
+#ifdef CONFIG_TIME_NS
+static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
+{
+ kref_get(&ns->kref);
+ return ns;
+}
+
+extern struct time_namespace *copy_time_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct time_namespace *old_ns);
+extern void free_time_ns(struct kref *kref);
+extern int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
+
+static inline void put_time_ns(struct time_namespace *ns)
+{
+ kref_put(&ns->kref, free_time_ns);
+}
+
+
+#else
+static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
+{
+ return NULL;
+}
+
+static inline void put_time_ns(struct time_namespace *ns)
+{
+}
+
+static inline struct time_namespace *copy_time_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct time_namespace *old_ns)
+{
+ if (flags & CLONE_NEWTIME)
+ return ERR_PTR(-EINVAL);
+
+ return old_ns;
+}
+
+static inline int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
+{
+ return 0;
+}
+
+#endif
+
+#endif /* _LINUX_TIMENS_H */
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index d6b74b91096b..bf84f93dc411 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -45,6 +45,7 @@ enum ucount_type {
UCOUNT_NET_NAMESPACES,
UCOUNT_MNT_NAMESPACES,
UCOUNT_CGROUP_NAMESPACES,
+ UCOUNT_TIME_NAMESPACES,
#ifdef CONFIG_INOTIFY_USER
UCOUNT_INOTIFY_INSTANCES,
UCOUNT_INOTIFY_WATCHES,
diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h
index 22627f80063e..adffac53c76e 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -10,6 +10,7 @@
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
+#define CLONE_NEWTIME 0x00001000 /* New time namespace */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
diff --git a/init/Kconfig b/init/Kconfig
index c9386a365eea..03ed7b2694b5 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -965,6 +965,13 @@ config UTS_NS
In this namespace tasks see different info provided with the
uname() system call
+config TIME_NS
+ bool "TIME namespace"
+ default y
+ help
+ In this namespace boottime and monotonic clocks can be set.
+ The time will keep going with the same pace.
+
config IPC_NS
bool "IPC namespace"
depends on (SYSVIPC || POSIX_MQUEUE)
diff --git a/kernel/Makefile b/kernel/Makefile
index 6aa7543bcdb2..62c83975937f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/
obj-$(CONFIG_UTS_NS) += utsname.o
+obj-$(CONFIG_TIME_NS) += time_namespace.o
obj-$(CONFIG_USER_NS) += user_namespace.o
obj-$(CONFIG_PID_NS) += pid_namespace.o
obj-$(CONFIG_IKCONFIG) += configs.o
diff --git a/kernel/fork.c b/kernel/fork.c
index b69248e6f0e0..c653a8a62fec 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2426,7 +2426,8 @@ static int check_unshare_flags(unsigned long unshare_flags)
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
- CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP))
+ CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP|
+ CLONE_NEWTIME))
return -EINVAL;
/*
* Not implemented, but pretend it works if there is nothing
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index f6c5d330059a..586c9e2017dc 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -22,6 +22,7 @@
#include <linux/pid_namespace.h>
#include <net/net_namespace.h>
#include <linux/ipc_namespace.h>
+#include <linux/time_namespace.h>
#include <linux/proc_ns.h>
#include <linux/file.h>
#include <linux/syscalls.h>
@@ -44,6 +45,10 @@ struct nsproxy init_nsproxy = {
#ifdef CONFIG_CGROUPS
.cgroup_ns = &init_cgroup_ns,
#endif
+#ifdef CONFIG_TIME_NS
+ .time_ns = &init_time_ns,
+ .time_ns_for_children = &init_time_ns,
+#endif
};
static inline struct nsproxy *create_nsproxy(void)
@@ -110,8 +115,18 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
goto out_net;
}
+ new_nsp->time_ns_for_children = copy_time_ns(flags, user_ns,
+ tsk->nsproxy->time_ns_for_children);
+ if (IS_ERR(new_nsp->time_ns_for_children)) {
+ err = PTR_ERR(new_nsp->time_ns_for_children);
+ goto out_time;
+ }
+ new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns);
+
return new_nsp;
+out_time:
+ put_net(new_nsp->net_ns);
out_net:
put_cgroup_ns(new_nsp->cgroup_ns);
out_cgroup:
@@ -140,15 +155,16 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
struct nsproxy *old_ns = tsk->nsproxy;
struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
struct nsproxy *new_ns;
+ int ret;
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET |
- CLONE_NEWCGROUP)))) {
- get_nsproxy(old_ns);
- return 0;
- }
-
- if (!ns_capable(user_ns, CAP_SYS_ADMIN))
+ CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
+ if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) {
+ get_nsproxy(old_ns);
+ return 0;
+ }
+ } else if (!ns_capable(user_ns, CAP_SYS_ADMIN))
return -EPERM;
/*
@@ -166,6 +182,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (IS_ERR(new_ns))
return PTR_ERR(new_ns);
+ ret = timens_on_fork(new_ns, tsk);
+ if (ret) {
+ free_nsproxy(new_ns);
+ return ret;
+ }
+
tsk->nsproxy = new_ns;
return 0;
}
@@ -180,6 +202,10 @@ void free_nsproxy(struct nsproxy *ns)
put_ipc_ns(ns->ipc_ns);
if (ns->pid_ns_for_children)
put_pid_ns(ns->pid_ns_for_children);
+ if (ns->time_ns)
+ put_time_ns(ns->time_ns);
+ if (ns->time_ns_for_children)
+ put_time_ns(ns->time_ns_for_children);
put_cgroup_ns(ns->cgroup_ns);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
@@ -196,7 +222,8 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
int err = 0;
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
- CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP)))
+ CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
+ CLONE_NEWTIME)))
return 0;
user_ns = new_cred ? new_cred->user_ns : current_user_ns();
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
new file mode 100644
index 000000000000..8c600df9771d
--- /dev/null
+++ b/kernel/time_namespace.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Andrei Vagin <avagin@openvz.org>
+ * Author: Dmitry Safonov <dima@arista.com>
+ */
+
+#include <linux/export.h>
+#include <linux/time.h>
+#include <linux/time_namespace.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/user_namespace.h>
+#include <linux/proc_ns.h>
+#include <linux/sched/task.h>
+
+static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
+{
+ return inc_ucount(ns, current_euid(), UCOUNT_TIME_NAMESPACES);
+}
+
+static void dec_time_namespaces(struct ucounts *ucounts)
+{
+ dec_ucount(ucounts, UCOUNT_TIME_NAMESPACES);
+}
+
+static struct time_namespace *create_time_ns(void)
+{
+ struct time_namespace *time_ns;
+
+ time_ns = kmalloc(sizeof(struct time_namespace), GFP_KERNEL);
+ if (time_ns) {
+ kref_init(&time_ns->kref);
+ time_ns->initialized = false;
+ }
+ return time_ns;
+}
+
+/*
+ * Clone a new ns copying @old_ns, setting refcount to 1
+ * @old_ns: namespace to clone
+ * Return the new ns or ERR_PTR.
+ */
+static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
+ struct time_namespace *old_ns)
+{
+ struct time_namespace *ns;
+ struct ucounts *ucounts;
+ int err;
+
+ err = -ENOSPC;
+ ucounts = inc_time_namespaces(user_ns);
+ if (!ucounts)
+ goto fail;
+
+ err = -ENOMEM;
+ ns = create_time_ns();
+ if (!ns)
+ goto fail_dec;
+
+ err = ns_alloc_inum(&ns->ns);
+ if (err)
+ goto fail_free;
+
+ ns->ucounts = ucounts;
+ ns->ns.ops = &timens_operations;
+ ns->user_ns = get_user_ns(user_ns);
+ return ns;
+
+fail_free:
+ kfree(ns);
+fail_dec:
+ dec_time_namespaces(ucounts);
+fail:
+ return ERR_PTR(err);
+}
+
+/*
+ * Add a reference to old_ns, or clone it if @flags specify CLONE_NEWTIME.
+ * In latter case, changes to the time of this process won't be seen by parent,
+ * and vice versa.
+ */
+struct time_namespace *copy_time_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct time_namespace *old_ns)
+{
+ if (!(flags & CLONE_NEWTIME))
+ return get_time_ns(old_ns);
+
+ return clone_time_ns(user_ns, old_ns);
+}
+
+void free_time_ns(struct kref *kref)
+{
+ struct time_namespace *ns;
+
+ ns = container_of(kref, struct time_namespace, kref);
+ dec_time_namespaces(ns->ucounts);
+ put_user_ns(ns->user_ns);
+ ns_free_inum(&ns->ns);
+ kfree(ns);
+}
+
+static struct time_namespace *to_time_ns(struct ns_common *ns)
+{
+ return container_of(ns, struct time_namespace, ns);
+}
+
+static struct ns_common *timens_get(struct task_struct *task)
+{
+ struct time_namespace *ns = NULL;
+ struct nsproxy *nsproxy;
+
+ task_lock(task);
+ nsproxy = task->nsproxy;
+ if (nsproxy) {
+ ns = nsproxy->time_ns;
+ get_time_ns(ns);
+ }
+ task_unlock(task);
+
+ return ns ? &ns->ns : NULL;
+}
+
+static struct ns_common *timens_for_children_get(struct task_struct *task)
+{
+ struct time_namespace *ns = NULL;
+ struct nsproxy *nsproxy;
+
+ task_lock(task);
+ nsproxy = task->nsproxy;
+ if (nsproxy) {
+ ns = nsproxy->time_ns_for_children;
+ get_time_ns(ns);
+ }
+ task_unlock(task);
+
+ return ns ? &ns->ns : NULL;
+}
+
+static void timens_put(struct ns_common *ns)
+{
+ put_time_ns(to_time_ns(ns));
+}
+
+static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)
+{
+ struct time_namespace *ns = to_time_ns(new);
+
+ if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+ !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
+ return -EPERM;
+
+ get_time_ns(ns);
+ get_time_ns(ns);
+ put_time_ns(nsproxy->time_ns);
+ put_time_ns(nsproxy->time_ns_for_children);
+ nsproxy->time_ns = ns;
+ nsproxy->time_ns_for_children = ns;
+ ns->initialized = true;
+ return 0;
+}
+
+int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
+{
+ struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
+ struct time_namespace *ns = to_time_ns(nsc);
+
+ if (nsproxy->time_ns == nsproxy->time_ns_for_children)
+ return 0;
+
+ get_time_ns(ns);
+ put_time_ns(nsproxy->time_ns);
+ nsproxy->time_ns = ns;
+ ns->initialized = true;
+
+ return 0;
+}
+
+static struct user_namespace *timens_owner(struct ns_common *ns)
+{
+ return to_time_ns(ns)->user_ns;
+}
+
+const struct proc_ns_operations timens_operations = {
+ .name = "time",
+ .type = CLONE_NEWTIME,
+ .get = timens_get,
+ .put = timens_put,
+ .install = timens_install,
+ .owner = timens_owner,
+};
+
+const struct proc_ns_operations timens_for_children_operations = {
+ .name = "time_for_children",
+ .type = CLONE_NEWTIME,
+ .get = timens_for_children_get,
+ .put = timens_put,
+ .install = timens_install,
+ .owner = timens_owner,
+};
+
+struct time_namespace init_time_ns = {
+ .kref = KREF_INIT(3),
+ .user_ns = &init_user_ns,
+ .ns.inum = PROC_UTS_INIT_INO,
+#ifdef CONFIG_UTS_NS
+ .ns.ops = &timens_operations,
+#endif
+};
+
+static int __init time_ns_init(void)
+{
+ return 0;
+}
+subsys_initcall(time_ns_init);
--
2.20.1
^ permalink raw reply related
* [PATCH 02/32] timens: Add timens_offsets
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Introduce offsets for time namespace. They will contain an adjustment
needed to convert clocks to/from host's.
Allocate one page for each time namespace that will be premapped into
userspace among vvar pages.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
MAINTAINERS | 1 +
include/linux/time_namespace.h | 1 +
include/linux/timens_offsets.h | 8 ++++++++
kernel/time_namespace.c | 14 ++++++++++++--
4 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 include/linux/timens_offsets.h
diff --git a/MAINTAINERS b/MAINTAINERS
index e03f160012e3..cc9054a74886 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12145,6 +12145,7 @@ S: Maintained
F: fs/timerfd.c
F: include/linux/timer*
F: include/linux/time_namespace.h
+F: include/linux/timens_offsets.h
F: kernel/time_namespace.c
F: kernel/time/*timer*
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
index 9507ed7072fe..b6985aa87479 100644
--- a/include/linux/time_namespace.h
+++ b/include/linux/time_namespace.h
@@ -8,6 +8,7 @@
#include <linux/nsproxy.h>
#include <linux/ns_common.h>
#include <linux/err.h>
+#include <linux/timens_offsets.h>
struct user_namespace;
extern struct user_namespace init_user_ns;
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
new file mode 100644
index 000000000000..7d7cb68ea778
--- /dev/null
+++ b/include/linux/timens_offsets.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_TIME_OFFSETS_H
+#define _LINUX_TIME_OFFSETS_H
+
+struct timens_offsets {
+};
+
+#endif
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index 8c600df9771d..4828447721ec 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -13,6 +13,7 @@
#include <linux/user_namespace.h>
#include <linux/proc_ns.h>
#include <linux/sched/task.h>
+#include <linux/mm.h>
static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
{
@@ -46,6 +47,7 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
{
struct time_namespace *ns;
struct ucounts *ucounts;
+ struct page *page;
int err;
err = -ENOSPC;
@@ -58,15 +60,22 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
if (!ns)
goto fail_dec;
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
+ goto fail_free;
+ ns->offsets = page_address(page);
+ BUILD_BUG_ON(sizeof(*ns->offsets) > PAGE_SIZE);
+
err = ns_alloc_inum(&ns->ns);
if (err)
- goto fail_free;
+ goto fail_page;
ns->ucounts = ucounts;
ns->ns.ops = &timens_operations;
ns->user_ns = get_user_ns(user_ns);
return ns;
-
+fail_page:
+ free_page((unsigned long)ns->offsets);
fail_free:
kfree(ns);
fail_dec:
@@ -94,6 +103,7 @@ void free_time_ns(struct kref *kref)
struct time_namespace *ns;
ns = container_of(kref, struct time_namespace, kref);
+ free_page((unsigned long)ns->offsets);
dec_time_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
--
2.20.1
^ permalink raw reply related
* [PATCH 03/32] timens: Introduce CLOCK_MONOTONIC offsets
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Add monotonic time virtualisation for time namespace.
Introduce timespec for monotionic clock into timens offsets and wire
clock_gettime() syscall.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/time_namespace.h | 11 ++++++++++
include/linux/timens_offsets.h | 1 +
kernel/time/posix-timers.c | 10 +++++++++
kernel/time/posix-timers.h | 1 +
kernel/time_namespace.c | 38 ++++++++++++++++++++++++++++++++++
5 files changed, 61 insertions(+)
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
index b6985aa87479..f1807d7f524d 100644
--- a/include/linux/time_namespace.h
+++ b/include/linux/time_namespace.h
@@ -41,6 +41,9 @@ static inline void put_time_ns(struct time_namespace *ns)
}
+extern void timens_clock_to_host(int clockid, struct timespec64 *val);
+extern void timens_clock_from_host(int clockid, struct timespec64 *val);
+
#else
static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
{
@@ -65,6 +68,14 @@ static inline int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *ts
return 0;
}
+static inline void timens_clock_to_host(int clockid, struct timespec64 *val)
+{
+}
+
+static inline void timens_clock_from_host(int clockid, struct timespec64 *val)
+{
+}
+
#endif
#endif /* _LINUX_TIMENS_H */
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 7d7cb68ea778..248b0c0bb92a 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -3,6 +3,7 @@
#define _LINUX_TIME_OFFSETS_H
struct timens_offsets {
+ struct timespec64 monotonic_time_offset;
};
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 0e84bb72a3da..b6d5145858a3 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -30,6 +30,7 @@
#include <linux/hashtable.h>
#include <linux/compat.h>
#include <linux/nospec.h>
+#include <linux/time_namespace.h>
#include "timekeeping.h"
#include "posix-timers.h"
@@ -1041,6 +1042,9 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
error = kc->clock_get(which_clock, &kernel_tp);
+ if (!error && kc->clock_timens_adjust)
+ timens_clock_from_host(which_clock, &kernel_tp);
+
if (!error && put_timespec64(&kernel_tp, tp))
error = -EFAULT;
@@ -1117,6 +1121,9 @@ COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock,
err = kc->clock_get(which_clock, &ts);
+ if (!err && kc->clock_timens_adjust)
+ timens_clock_from_host(which_clock, &ts);
+
if (!err && put_old_timespec32(&ts, tp))
err = -EFAULT;
@@ -1259,6 +1266,7 @@ static const struct k_clock clock_realtime = {
static const struct k_clock clock_monotonic = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_ktime_get_ts,
+ .clock_timens_adjust = true,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
@@ -1274,6 +1282,7 @@ static const struct k_clock clock_monotonic = {
static const struct k_clock clock_monotonic_raw = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_monotonic_raw,
+ .clock_timens_adjust = true,
};
static const struct k_clock clock_realtime_coarse = {
@@ -1284,6 +1293,7 @@ static const struct k_clock clock_realtime_coarse = {
static const struct k_clock clock_monotonic_coarse = {
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
+ .clock_timens_adjust = true,
};
static const struct k_clock clock_tai = {
diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
index ddb21145211a..1cf306bde639 100644
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -24,6 +24,7 @@ struct k_clock {
int (*timer_try_to_cancel)(struct k_itimer *timr);
void (*timer_arm)(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none);
+ bool clock_timens_adjust;
};
extern const struct k_clock clock_posix_cpu;
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index 4828447721ec..57694be9e9db 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -191,6 +191,44 @@ static struct user_namespace *timens_owner(struct ns_common *ns)
return to_time_ns(ns)->user_ns;
}
+static void clock_timens_fixup(int clockid, struct timespec64 *val, bool to_ns)
+{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+ struct timespec64 *offsets = NULL;
+
+ if (!ns_offsets)
+ return;
+
+ if (val->tv_sec == 0 && val->tv_nsec == 0)
+ return;
+
+ switch (clockid) {
+ case CLOCK_MONOTONIC:
+ case CLOCK_MONOTONIC_RAW:
+ case CLOCK_MONOTONIC_COARSE:
+ offsets = &ns_offsets->monotonic_time_offset;
+ break;
+ }
+
+ if (!offsets)
+ return;
+
+ if (to_ns)
+ *val = timespec64_add(*val, *offsets);
+ else
+ *val = timespec64_sub(*val, *offsets);
+}
+
+void timens_clock_to_host(int clockid, struct timespec64 *val)
+{
+ clock_timens_fixup(clockid, val, false);
+}
+
+void timens_clock_from_host(int clockid, struct timespec64 *val)
+{
+ clock_timens_fixup(clockid, val, true);
+}
+
const struct proc_ns_operations timens_operations = {
.name = "time",
.type = CLONE_NEWTIME,
--
2.20.1
^ permalink raw reply related
* [PATCH 04/32] timens: Introduce CLOCK_BOOTTIME offset
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Adds boottime virtualisation for time namespace.
Introduce timespec for boottime clock into timens offsets and wire
clock_gettime() syscall.
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/timens_offsets.h | 1 +
kernel/time/posix-timers.c | 1 +
kernel/time_namespace.c | 3 +++
3 files changed, 5 insertions(+)
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 248b0c0bb92a..777530c46852 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -4,6 +4,7 @@
struct timens_offsets {
struct timespec64 monotonic_time_offset;
+ struct timespec64 monotonic_boottime_offset;
};
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index b6d5145858a3..782708054df2 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -1314,6 +1314,7 @@ static const struct k_clock clock_tai = {
static const struct k_clock clock_boottime = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_boottime,
+ .clock_timens_adjust = true,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index 57694be9e9db..36b31f234472 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -208,6 +208,9 @@ static void clock_timens_fixup(int clockid, struct timespec64 *val, bool to_ns)
case CLOCK_MONOTONIC_COARSE:
offsets = &ns_offsets->monotonic_time_offset;
break;
+ case CLOCK_BOOTTIME:
+ offsets = &ns_offsets->monotonic_boottime_offset;
+ break;
}
if (!offsets)
--
2.20.1
^ permalink raw reply related
* [PATCH 05/32] timerfd/timens: Take into account ns clock offsets
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Make timerfd respect timens offsets.
Provide two helpers timens_clock_to_host() timens_clock_from_host() that
are useful to wire up timens to different kernel subsystems.
Following patches will use timens_clock_from_host(), added here for
completeness.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
fs/timerfd.c | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 803ca070d42e..c7ae1e371912 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -26,6 +26,7 @@
#include <linux/syscalls.h>
#include <linux/compat.h>
#include <linux/rcupdate.h>
+#include <linux/time_namespace.h>
struct timerfd_ctx {
union {
@@ -433,22 +434,27 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
}
static int do_timerfd_settime(int ufd, int flags,
- const struct itimerspec64 *new,
+ struct itimerspec64 *new,
struct itimerspec64 *old)
{
struct fd f;
struct timerfd_ctx *ctx;
int ret;
- if ((flags & ~TFD_SETTIME_FLAGS) ||
- !itimerspec64_valid(new))
- return -EINVAL;
-
ret = timerfd_fget(ufd, &f);
if (ret)
return ret;
ctx = f.file->private_data;
+ if (flags & TFD_TIMER_ABSTIME)
+ timens_clock_to_host(ctx->clockid, &new->it_value);
+
+ if ((flags & ~TFD_SETTIME_FLAGS) ||
+ !itimerspec64_valid(new)) {
+ fdput(f);
+ return -EINVAL;
+ }
+
if (isalarm(ctx) && !capable(CAP_WAKE_ALARM)) {
fdput(f);
return -EPERM;
--
2.20.1
^ permalink raw reply related
* [PATCH 06/32] posix-timers/timens: Take into account clock offsets
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Wire timer_settime() syscall into time namespace virtualization.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
kernel/time/posix-timers.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 782708054df2..d008dfd5b081 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -854,10 +854,6 @@ static int do_timer_settime(timer_t timer_id, int flags,
unsigned long flag;
int error = 0;
- if (!timespec64_valid(&new_spec64->it_interval) ||
- !timespec64_valid(&new_spec64->it_value))
- return -EINVAL;
-
if (old_spec64)
memset(old_spec64, 0, sizeof(*old_spec64));
retry:
@@ -865,6 +861,15 @@ static int do_timer_settime(timer_t timer_id, int flags,
if (!timr)
return -EINVAL;
+ if (flags & TIMER_ABSTIME)
+ timens_clock_to_host(timr->it_clock, &new_spec64->it_value);
+
+ if (!timespec64_valid(&new_spec64->it_interval) ||
+ !timespec64_valid(&new_spec64->it_value)) {
+ unlock_timer(timr, flag);
+ return -EINVAL;
+ }
+
kc = timr->kclock;
if (WARN_ON_ONCE(!kc || !kc->timer_set))
error = -EINVAL;
--
2.20.1
^ permalink raw reply related
* [PATCH 07/32] timens/kernel: Take into account timens clock offsets in clock_nanosleep
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Wire up clock_nanosleep() to timens offsets.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
kernel/time/hrtimer.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index f5cfa1b73d6f..47345aea406d 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -42,6 +42,7 @@
#include <linux/timer.h>
#include <linux/freezer.h>
#include <linux/compat.h>
+#include <linux/time_namespace.h>
#include <linux/uaccess.h>
@@ -1721,9 +1722,16 @@ long hrtimer_nanosleep(const struct timespec64 *rqtp,
{
struct restart_block *restart;
struct hrtimer_sleeper t;
+ struct timespec64 tp;
int ret = 0;
u64 slack;
+ if (!(mode & HRTIMER_MODE_REL)) {
+ tp = *rqtp;
+ rqtp = &tp;
+ timens_clock_to_host(clockid, &tp);
+ }
+
slack = current->timer_slack_ns;
if (dl_task(current) || rt_task(current))
slack = 0;
--
2.20.1
^ permalink raw reply related
* [PATCH 08/32] timens: Shift /proc/uptime
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
Respect boottime inside time namespace for /proc/uptime
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
fs/proc/uptime.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c
index a4c2791ab70b..4421ec058472 100644
--- a/fs/proc/uptime.c
+++ b/fs/proc/uptime.c
@@ -5,6 +5,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/time.h>
+#include <linux/time_namespace.h>
#include <linux/kernel_stat.h>
static int uptime_proc_show(struct seq_file *m, void *v)
@@ -20,6 +21,8 @@ static int uptime_proc_show(struct seq_file *m, void *v)
nsec += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
ktime_get_boottime_ts64(&uptime);
+ timens_clock_from_host(CLOCK_BOOTTIME, &uptime);
+
idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
idle.tv_nsec = rem;
seq_printf(m, "%lu.%02lu %lu.%02lu\n",
--
2.20.1
^ permalink raw reply related
* [PATCH 09/32] x86/vdso2c: Correct err messages on file opening
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
err() message in main() is misleading: it should print `outfilename`,
which is argv[3], not argv[2].
Correct error messages to be more precise about what failed and for
which file.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vdso2c.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index 8e470b018512..26d7177c119e 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -187,7 +187,7 @@ static void map_input(const char *name, void **addr, size_t *len, int prot)
int fd = open(name, O_RDONLY);
if (fd == -1)
- err(1, "%s", name);
+ err(1, "open(%s)", name);
tmp_len = lseek(fd, 0, SEEK_END);
if (tmp_len == (off_t)-1)
@@ -240,7 +240,7 @@ int main(int argc, char **argv)
outfilename = argv[3];
outfile = fopen(outfilename, "w");
if (!outfile)
- err(1, "%s", argv[2]);
+ err(1, "fopen(%s)", outfilename);
go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name);
--
2.20.1
^ permalink raw reply related
* [PATCH 10/32] x86/vdso2c: Convert iterator to unsigned
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
i and j are used everywhere with unsigned types.
Cleanup and prettify the code a bit.
Introduce syms_nr for readability and as a preparation for allocating an
array of vDSO entries that will be needed for creating two vdso .so's:
one for host tasks and another for processes inside time namespace.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vdso2c.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index fa847a620f40..61c8bb2e5af8 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -13,7 +13,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
unsigned long load_size = -1; /* Work around bogus warning */
unsigned long mapping_size;
ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
- int i;
+ unsigned int i, syms_nr;
unsigned long j;
ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
*alt_sec = NULL;
@@ -86,11 +86,10 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link);
+ syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
/* Walk the symbol table */
- for (i = 0;
- i < GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
- i++) {
- int k;
+ for (i = 0; i < syms_nr; i++) {
+ unsigned int k;
ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) +
GET_LE(&symtab_hdr->sh_entsize) * i;
const char *name = raw_addr + GET_LE(&strtab_hdr->sh_offset) +
--
2.20.1
^ permalink raw reply related
* [PATCH 11/32] x86/vdso/Makefile: Add vobjs32
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
As for 64-bit vdso objects, handle ia32/i386 objects in array.
This is a preparation ground to avoid code duplication on introduction
of timens vdso.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/Makefile | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 5bfe2243a08f..bc0bdbf49397 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -18,6 +18,8 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y
# files to link into the vdso
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
+vobjs32-y := vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
+vobjs32-y += vdso32/vclock_gettime.o
# files to link into kernel
obj-y += vma.o
@@ -31,10 +33,12 @@ vdso_img-$(VDSO32-y) += 32
obj-$(VDSO32-y) += vdso32-setup.o
vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
+vobjs32 := $(foreach F,$(vobjs32-y),$(obj)/$F)
$(obj)/vdso.o: $(obj)/vdso.so
targets += vdso.lds $(vobjs-y)
+targets += vdso32/vdso32.lds $(vobjs32-y)
# Build the vDSO image C files and link them in.
vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o)
@@ -125,10 +129,6 @@ $(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE
CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds)
VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -soname linux-gate.so.1
-targets += vdso32/vdso32.lds
-targets += vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
-targets += vdso32/vclock_gettime.o
-
KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO
$(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
$(obj)/vdso32.so.dbg: asflags-$(CONFIG_X86_64) += -m32
@@ -153,12 +153,7 @@ endif
$(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
-$(obj)/vdso32.so.dbg: FORCE \
- $(obj)/vdso32/vdso32.lds \
- $(obj)/vdso32/vclock_gettime.o \
- $(obj)/vdso32/note.o \
- $(obj)/vdso32/system_call.o \
- $(obj)/vdso32/sigreturn.o
+$(obj)/vdso32.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32) FORCE
$(call if_changed,vdso)
#
--
2.20.1
^ permalink raw reply related
* [PATCH 12/32] x86/vdso/timens: Add offsets page in vvar
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Andrei Vagin, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
As modern applications fetch time from VDSO without entering the kernel,
it's needed to provide offsets for userspace code inside time namespace.
A page for timens offsets is allocated on time namespace construction.
Put that page into VVAR for tasks inside timens and zero page for
host processes.
As VDSO code is already optimized as much as possible in terms of speed,
any new if-condition in VDSO code is undesirable; the goal is to provide
two .so(s), as was originally suggested by Andy and Thomas:
- for host tasks with optimized-out clk_to_ns() without any penalty
- for processes inside timens with clk_to_ns()
For this purpose, define clk_to_ns() under BUILD_VDSO_TIME_NS, which
will be enabled in the makefile for timens.so in following patches.
VDSO mappings are platform-specific, add Kconfig dependency for arch.
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/Kconfig | 5 ++++
arch/x86/Kconfig | 1 +
arch/x86/entry/vdso/vclock_gettime.c | 42 +++++++++++++++++++++++++++
arch/x86/entry/vdso/vdso-layout.lds.S | 9 +++++-
arch/x86/entry/vdso/vdso2c.c | 3 ++
arch/x86/entry/vdso/vma.c | 12 ++++++++
arch/x86/include/asm/vdso.h | 1 +
init/Kconfig | 1 +
8 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 4cfb6de48f79..fd2f96993db9 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -704,6 +704,11 @@ config HAVE_ARCH_HASH
config ISA_BUS_API
def_bool ISA
+config ARCH_HAS_VDSO_TIME_NS
+ bool
+ help
+ VDSO can add time-ns offsets without entering kernel.
+
#
# ABI hall of shame
#
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 68261430fe6e..b415519a293f 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -70,6 +70,7 @@ config X86
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE
select ARCH_HAS_UBSAN_SANITIZE_ALL
+ select ARCH_HAS_VDSO_TIME_NS
select ARCH_HAS_ZONE_DEVICE if X86_64
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index 007b3fe9d727..cb55bd994497 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -21,6 +21,7 @@
#include <linux/math64.h>
#include <linux/time.h>
#include <linux/kernel.h>
+#include <linux/timens_offsets.h>
#define gtod (&VVAR(vsyscall_gtod_data))
@@ -38,6 +39,11 @@ extern u8 hvclock_page
__attribute__((visibility("hidden")));
#endif
+#ifdef BUILD_VDSO_TIME_NS
+extern u8 timens_page
+ __attribute__((visibility("hidden")));
+#endif
+
#ifndef BUILD_VDSO32
notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
@@ -139,6 +145,38 @@ notrace static inline u64 vgetcyc(int mode)
return U64_MAX;
}
+notrace static __always_inline void clk_to_ns(clockid_t clk, struct timespec *ts)
+{
+#ifdef BUILD_VDSO_TIME_NS
+ struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
+ struct timespec64 *offset64;
+
+ switch (clk) {
+ case CLOCK_MONOTONIC:
+ case CLOCK_MONOTONIC_COARSE:
+ case CLOCK_MONOTONIC_RAW:
+ offset64 = &timens->monotonic_time_offset;
+ break;
+ case CLOCK_BOOTTIME:
+ offset64 = &timens->monotonic_boottime_offset;
+ default:
+ return;
+ }
+
+ ts->tv_nsec += offset64->tv_nsec;
+ ts->tv_sec += offset64->tv_sec;
+ if (ts->tv_nsec >= NSEC_PER_SEC) {
+ ts->tv_nsec -= NSEC_PER_SEC;
+ ts->tv_sec++;
+ }
+ if (ts->tv_nsec < 0) {
+ ts->tv_nsec += NSEC_PER_SEC;
+ ts->tv_sec--;
+ }
+
+#endif
+}
+
notrace static int do_hres(clockid_t clk, struct timespec *ts)
{
struct vgtod_ts *base = >od->basetime[clk];
@@ -165,6 +203,8 @@ notrace static int do_hres(clockid_t clk, struct timespec *ts)
ts->tv_sec = sec + __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
+ clk_to_ns(clk, ts);
+
return 0;
}
@@ -178,6 +218,8 @@ notrace static void do_coarse(clockid_t clk, struct timespec *ts)
ts->tv_sec = base->sec;
ts->tv_nsec = base->nsec;
} while (unlikely(gtod_read_retry(gtod, seq)));
+
+ clk_to_ns(clk, ts);
}
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index 93c6dc7812d0..ba216527e59f 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -7,6 +7,12 @@
* This script controls its layout.
*/
+#ifdef CONFIG_TIME_NS
+# define TIMENS_SZ PAGE_SIZE
+#else
+# define TIMENS_SZ 0
+#endif
+
SECTIONS
{
/*
@@ -16,7 +22,7 @@ SECTIONS
* segment.
*/
- vvar_start = . - 3 * PAGE_SIZE;
+ vvar_start = . - (3 * PAGE_SIZE + TIMENS_SZ);
vvar_page = vvar_start;
/* Place all vvars at the offsets in asm/vvar.h. */
@@ -28,6 +34,7 @@ SECTIONS
pvclock_page = vvar_start + PAGE_SIZE;
hvclock_page = vvar_start + 2 * PAGE_SIZE;
+ timens_page = vvar_start + 3 * PAGE_SIZE;
. = SIZEOF_HEADERS;
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index 26d7177c119e..ed66b023d4b9 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -76,6 +76,7 @@ enum {
sym_hpet_page,
sym_pvclock_page,
sym_hvclock_page,
+ sym_timens_page,
};
const int special_pages[] = {
@@ -83,6 +84,7 @@ const int special_pages[] = {
sym_hpet_page,
sym_pvclock_page,
sym_hvclock_page,
+ sym_timens_page,
};
struct vdso_sym {
@@ -96,6 +98,7 @@ struct vdso_sym required_syms[] = {
[sym_hpet_page] = {"hpet_page", true},
[sym_pvclock_page] = {"pvclock_page", true},
[sym_hvclock_page] = {"hvclock_page", true},
+ [sym_timens_page] = {"timens_page", true},
{"VDSO32_NOTE_MASK", true},
{"__kernel_vsyscall", true},
{"__kernel_sigreturn", true},
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index babc4e7a519c..d1031db94093 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -14,6 +14,7 @@
#include <linux/elf.h>
#include <linux/cpu.h>
#include <linux/ptrace.h>
+#include <linux/time_namespace.h>
#include <asm/pvclock.h>
#include <asm/vgtod.h>
#include <asm/proto.h>
@@ -23,6 +24,7 @@
#include <asm/desc.h>
#include <asm/cpufeature.h>
#include <asm/mshyperv.h>
+#include <asm/page.h>
#if defined(CONFIG_X86_64)
unsigned int __read_mostly vdso64_enabled = 1;
@@ -123,6 +125,16 @@ static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
if (tsc_pg && vclock_was_used(VCLOCK_HVCLOCK))
return vmf_insert_pfn(vma, vmf->address,
vmalloc_to_pfn(tsc_pg));
+ } else if (sym_offset == image->sym_timens_page) {
+ struct time_namespace *ns = current->nsproxy->time_ns;
+ unsigned long pfn;
+
+ if (!ns->offsets)
+ pfn = page_to_pfn(ZERO_PAGE(0));
+ else
+ pfn = page_to_pfn(virt_to_page(ns->offsets));
+
+ return vmf_insert_pfn(vma, vmf->address, pfn);
}
return VM_FAULT_SIGBUS;
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 27566e57e87d..619322065b8e 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -22,6 +22,7 @@ struct vdso_image {
long sym_hpet_page;
long sym_pvclock_page;
long sym_hvclock_page;
+ long sym_timens_page;
long sym_VDSO32_NOTE_MASK;
long sym___kernel_sigreturn;
long sym___kernel_rt_sigreturn;
diff --git a/init/Kconfig b/init/Kconfig
index 03ed7b2694b5..14e94a64064a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -967,6 +967,7 @@ config UTS_NS
config TIME_NS
bool "TIME namespace"
+ depends on ARCH_HAS_VDSO_TIME_NS
default y
help
In this namespace boottime and monotonic clocks can be set.
--
2.20.1
^ permalink raw reply related
* [PATCH 13/32] x86/vdso: Build timens .so(s)
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
It will add a penalty for everybody as branch predictor may mispredict
the jump. Also there are instruction cache lines wasted on cmp/jmp.
Those effects of introducing time namespace are very much unwanted
having in mind how much work have been spent on micro-optimisation
vdso code.
Addressing those problems, build two VDSO images instead.
At this moment timens is unsupported for x32 binaries (only x86_64 and
ia32). This may be added on top afterwards.
Suggested-by: Andy Lutomirski <luto@kernel.org>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/Makefile | 28 +++++++++++++++++--
arch/x86/entry/vdso/vclock_gettime-timens.c | 6 ++++
.../entry/vdso/vdso32/vclock_gettime-timens.c | 6 ++++
3 files changed, 37 insertions(+), 3 deletions(-)
create mode 100644 arch/x86/entry/vdso/vclock_gettime-timens.c
create mode 100644 arch/x86/entry/vdso/vdso32/vclock_gettime-timens.c
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index bc0bdbf49397..2433ed9342fd 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -17,8 +17,12 @@ VDSO32-$(CONFIG_X86_32) := y
VDSO32-$(CONFIG_IA32_EMULATION) := y
# files to link into the vdso
-vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o
+vobjs-y := vdso-note.o vgetcpu.o
+vobjs-timens-y := $(vobjs-y) vclock_gettime-timens.o
+vobjs-y += vclock_gettime.o
+
vobjs32-y := vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o
+vobjs32-timens-y := $(vobjs32-y) vdso32/vclock_gettime-timens.o
vobjs32-y += vdso32/vclock_gettime.o
# files to link into kernel
@@ -30,15 +34,21 @@ vdso_img-$(VDSO64-y) += 64
vdso_img-$(VDSOX32-y) += x32
vdso_img-$(VDSO32-y) += 32
+vdso_timens_img-$(VDSO64-y) += 64-timens
+vdso_timens_img-$(VDSO32-y) += 32-timens
+vdso_img-$(CONFIG_TIME_NS) += $(vdso_timens_img-y)
+
obj-$(VDSO32-y) += vdso32-setup.o
vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
vobjs32 := $(foreach F,$(vobjs32-y),$(obj)/$F)
+vobjs-timens := $(foreach F,$(vobjs-timens-y),$(obj)/$F)
+vobjs32-timens := $(foreach F,$(vobjs32-timens-y),$(obj)/$F)
$(obj)/vdso.o: $(obj)/vdso.so
-targets += vdso.lds $(vobjs-y)
-targets += vdso32/vdso32.lds $(vobjs32-y)
+targets += vdso.lds $(vobjs-y) $(vobjs-timens-y)
+targets += vdso32/vdso32.lds $(vobjs32-y) $(vobjs32-timens-y)
# Build the vDSO image C files and link them in.
vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o)
@@ -53,6 +63,9 @@ CPPFLAGS_vdso.lds += -P -C
VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -soname linux-vdso.so.1 --no-undefined \
-z max-page-size=4096
+$(obj)/vdso64-timens.so.dbg: $(obj)/vdso.lds $(vobjs-timens) FORCE
+ $(call if_changed,vdso)
+
$(obj)/vdso64.so.dbg: $(obj)/vdso.lds $(vobjs) FORCE
$(call if_changed,vdso)
@@ -81,12 +94,14 @@ endif
endif
$(vobjs): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
+$(vobjs-timens): KBUILD_CFLAGS := $(filter-out $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL)
#
# vDSO code runs in userspace and -pg doesn't help with profiling anyway.
#
CFLAGS_REMOVE_vdso-note.o = -pg
CFLAGS_REMOVE_vclock_gettime.o = -pg
+CFLAGS_REMOVE_vclock_gettime-timens.o = -pg
CFLAGS_REMOVE_vgetcpu.o = -pg
CFLAGS_REMOVE_vvar.o = -pg
@@ -132,6 +147,8 @@ VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -soname linux-gate.so.1
KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO
$(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
$(obj)/vdso32.so.dbg: asflags-$(CONFIG_X86_64) += -m32
+$(obj)/vdso32-timens.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
+$(obj)/vdso32-timens.so.dbg: asflags-$(CONFIG_X86_64) += -m32
KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32))
@@ -152,6 +169,10 @@ endif
endif
$(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
+$(obj)/vdso32-timens.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
+
+$(obj)/vdso32-timens.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32-timens) FORCE
+ $(call if_changed,vdso)
$(obj)/vdso32.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32) FORCE
$(call if_changed,vdso)
@@ -198,3 +219,4 @@ PHONY += vdso_install $(vdso_img_insttargets)
vdso_install: $(vdso_img_insttargets)
clean-files := vdso32.so vdso32.so.dbg vdso64* vdso-image-*.c vdsox32.so*
+clean-files += vdso32-timens.so vdso32-timens.so.dbg
diff --git a/arch/x86/entry/vdso/vclock_gettime-timens.c b/arch/x86/entry/vdso/vclock_gettime-timens.c
new file mode 100644
index 000000000000..9e92315f83db
--- /dev/null
+++ b/arch/x86/entry/vdso/vclock_gettime-timens.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define BUILD_VDSO_TIME_NS
+
+#include "vclock_gettime.c"
+
diff --git a/arch/x86/entry/vdso/vdso32/vclock_gettime-timens.c b/arch/x86/entry/vdso/vdso32/vclock_gettime-timens.c
new file mode 100644
index 000000000000..9e92315f83db
--- /dev/null
+++ b/arch/x86/entry/vdso/vdso32/vclock_gettime-timens.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define BUILD_VDSO_TIME_NS
+
+#include "vclock_gettime.c"
+
--
2.20.1
^ permalink raw reply related
* [PATCH 14/32] x86/VDSO: Build VDSO with -ffunction-sections
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
It will add a penalty for everybody as branch predictor may mispredict
the jump. Also there are instruction cache lines wasted on cmp/jmp.
Those effects of introducing time namespace are very much unwanted
having in mind how much work have been spent on micro-optimisation
vdso code.
Addressing those problems, there are two versions of VDSO's .so:
for host tasks (without any penalty) and for processes inside of time
namespace with clk_to_ns() that subtracts offsets from host's time.
Unfortunately, to allow changing VDSO VMA on a running process,
the entry points to VDSO should have the same offsets (addresses).
That's needed as i.e. application that calls setns() may have already
resolved VDSO symbols in GOT/PLT.
Compile VDSO images with -ffunction-sections so that VDSO entries can be
aligned on the same addresses with linker script magic.
Put ia32 functions those are written in assembly into corresponding
sections.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/Makefile | 2 +-
arch/x86/entry/vdso/vdso32/sigreturn.S | 2 ++
arch/x86/entry/vdso/vdso32/system_call.S | 2 +-
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 2433ed9342fd..55ba81d4415c 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -3,7 +3,7 @@
# Building vDSO images for x86.
#
-KBUILD_CFLAGS += $(DISABLE_LTO)
+KBUILD_CFLAGS += $(DISABLE_LTO) -ffunction-sections
KASAN_SANITIZE := n
UBSAN_SANITIZE := n
OBJECT_FILES_NON_STANDARD := y
diff --git a/arch/x86/entry/vdso/vdso32/sigreturn.S b/arch/x86/entry/vdso/vdso32/sigreturn.S
index c3233ee98a6b..b641ccf8d664 100644
--- a/arch/x86/entry/vdso/vdso32/sigreturn.S
+++ b/arch/x86/entry/vdso/vdso32/sigreturn.S
@@ -11,6 +11,7 @@
.globl __kernel_sigreturn
.type __kernel_sigreturn,@function
nop /* this guy is needed for .LSTARTFDEDLSI1 below (watch for HACK) */
+ .section .text.__kernel_sigreturn, "ax"
ALIGN
__kernel_sigreturn:
.LSTART_sigreturn:
@@ -21,6 +22,7 @@ __kernel_sigreturn:
nop
.size __kernel_sigreturn,.-.LSTART_sigreturn
+ .section .text.__kernel_rt_sigreturn, "ax"
.globl __kernel_rt_sigreturn
.type __kernel_rt_sigreturn,@function
ALIGN
diff --git a/arch/x86/entry/vdso/vdso32/system_call.S b/arch/x86/entry/vdso/vdso32/system_call.S
index 263d7433dea8..13ec05287f63 100644
--- a/arch/x86/entry/vdso/vdso32/system_call.S
+++ b/arch/x86/entry/vdso/vdso32/system_call.S
@@ -8,7 +8,7 @@
#include <asm/cpufeatures.h>
#include <asm/alternative-asm.h>
- .text
+ .section .text.__kernel_vsyscall, "ax"
.globl __kernel_vsyscall
.type __kernel_vsyscall,@function
ALIGN
--
2.20.1
^ permalink raw reply related
* [PATCH 15/32] x86/vdso2c: Optionally produce linker script for vdso entries
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
Two VDSO images (with/without code for adding offsets inside timens)
should be compatible by VDSO function offsets - this way kernel
can remap VDSO VMA for a task without fixupping GOT/PLT.
Add an optional parameter for vdso2c to generate .entries file from
vdso.so. As timens VDSO by nature is bigger in .text than VDSO for
tasks outside namespace, this parameter will be used to generate
.entries file from timens VDSO and include those aligns into linker
script for !timens VDSO building.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/.gitignore | 1 +
arch/x86/entry/vdso/Makefile | 7 ++++---
arch/x86/entry/vdso/vdso2c.c | 26 +++++++++++++++++++-------
arch/x86/entry/vdso/vdso2c.h | 16 +++++++++++++++-
4 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/arch/x86/entry/vdso/.gitignore b/arch/x86/entry/vdso/.gitignore
index aae8ffdd5880..9ab4fa4c7e7b 100644
--- a/arch/x86/entry/vdso/.gitignore
+++ b/arch/x86/entry/vdso/.gitignore
@@ -5,3 +5,4 @@ vdso32-sysenter-syms.lds
vdso32-int80-syms.lds
vdso-image-*.c
vdso2c
+vdso*.entries
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index 55ba81d4415c..ccb572831ea1 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -47,8 +47,8 @@ vobjs32-timens := $(foreach F,$(vobjs32-timens-y),$(obj)/$F)
$(obj)/vdso.o: $(obj)/vdso.so
-targets += vdso.lds $(vobjs-y) $(vobjs-timens-y)
-targets += vdso32/vdso32.lds $(vobjs32-y) $(vobjs32-timens-y)
+targets += vdso.lds $(vobjs-y) $(vobjs-timens-y) vdso64.entries
+targets += vdso32/vdso32.lds $(vobjs32-y) $(vobjs32-timens-y) vdso32.entries
# Build the vDSO image C files and link them in.
vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o)
@@ -73,7 +73,8 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include -I$(srctree)/include/uapi -I$(src
hostprogs-y += vdso2c
quiet_cmd_vdso2c = VDSO2C $@
- cmd_vdso2c = $(obj)/vdso2c $< $(<:%.dbg=%) $@
+ cmd_vdso2c = $(obj)/vdso2c $< $(<:%.dbg=%) $@ \
+ $(filter %.entries,$(<:%-timens.so.dbg=%.entries))
$(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so $(obj)/vdso2c FORCE
$(call if_changed,vdso2c)
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index ed66b023d4b9..72731c4cfdce 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -152,6 +152,7 @@ extern void bad_put_le(void);
#define BITSFUNC3(name, bits, suffix) name##bits##suffix
#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix)
#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, )
+#define ELF_FUNC(f, x) (BITSFUNC2(ELF, ELF_BITS, _##f)(x))
#define INT_BITS BITSFUNC2(int, ELF_BITS, _t)
@@ -169,16 +170,17 @@ extern void bad_put_le(void);
static void go(void *raw_addr, size_t raw_len,
void *stripped_addr, size_t stripped_len,
- FILE *outfile, const char *name)
+ FILE *outfile, const char *name,
+ FILE *out_entries_lds)
{
Elf64_Ehdr *hdr = (Elf64_Ehdr *)raw_addr;
if (hdr->e_ident[EI_CLASS] == ELFCLASS64) {
go64(raw_addr, raw_len, stripped_addr, stripped_len,
- outfile, name);
+ outfile, name, out_entries_lds);
} else if (hdr->e_ident[EI_CLASS] == ELFCLASS32) {
go32(raw_addr, raw_len, stripped_addr, stripped_len,
- outfile, name);
+ outfile, name, out_entries_lds);
} else {
fail("unknown ELF class\n");
}
@@ -208,12 +210,12 @@ int main(int argc, char **argv)
{
size_t raw_len, stripped_len;
void *raw_addr, *stripped_addr;
- FILE *outfile;
+ FILE *outfile, *entries_lds = NULL;
char *name, *tmp;
int namelen;
- if (argc != 4) {
- printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT\n");
+ if (argc < 4) {
+ printf("Usage: vdso2c RAW_INPUT STRIPPED_INPUT OUTPUT [OUTPUT_ENTRIES.LDS]\n");
return 1;
}
@@ -245,11 +247,21 @@ int main(int argc, char **argv)
if (!outfile)
err(1, "fopen(%s)", outfilename);
- go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name);
+ if (argc == 5) {
+ entries_lds = fopen(argv[4], "w");
+ if (!entries_lds) {
+ fclose(outfile);
+ err(1, "fopen(%s)", argv[4]);
+ }
+ }
+
+ go(raw_addr, raw_len, stripped_addr, stripped_len, outfile, name, entries_lds);
munmap(raw_addr, raw_len);
munmap(stripped_addr, stripped_len);
fclose(outfile);
+ if (entries_lds)
+ fclose(entries_lds);
return 0;
}
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index 61c8bb2e5af8..065dac6c29c8 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -7,7 +7,8 @@
static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
void *stripped_addr, size_t stripped_len,
- FILE *outfile, const char *name)
+ FILE *outfile, const char *name,
+ FILE *out_entries_lds)
{
int found_load = 0;
unsigned long load_size = -1; /* Work around bogus warning */
@@ -111,6 +112,19 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
syms[k] = GET_LE(&sym->st_value);
}
}
+
+ if (!out_entries_lds)
+ continue;
+
+ if (ELF_FUNC(ST_BIND, sym->st_info) != STB_GLOBAL)
+ continue;
+
+ if (ELF_FUNC(ST_TYPE, sym->st_info) != STT_FUNC)
+ continue;
+
+ fprintf(out_entries_lds, "\t\t. = ABSOLUTE(%#lx);\n",
+ (unsigned long)GET_LE(&sym->st_value));
+ fprintf(out_entries_lds, "\t\t*(.text.%s*)\n", name);
}
/* Validate mapping addresses. */
--
2.20.1
^ permalink raw reply related
* [PATCH 16/32] x86/vdso: Generate vdso{,32}-timens.lds
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
It will add a penalty for everybody as branch predictor may mispredict
the jump. Also there are instruction cache lines wasted on cmp/jmp.
Those effects of introducing time namespace are very much unwanted
having in mind how much work have been spent on micro-optimisation
vdso code.
Addressing those problems, there are two versions of VDSO's .so:
for host tasks (without any penalty) and for processes inside of time
namespace with clk_to_ns() that subtracts offsets from host's time.
Unfortunately, to allow changing VDSO VMA on a running process,
the entry points to VDSO should have the same offsets (addresses).
That's needed as i.e. application that calls setns() may have already
resolved VDSO symbols in GOT/PLT.
Provide two linker scripts:
- *-timens.lds for building VDSO for processes inside time namespace
(it has bigger functions and needs to build firstly)
- *.lds for host processes VDSO
(it has smaller functions and entry addresses should be adjusted
with the linker script magic to fit with entries from timens)
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/.gitignore | 1 +
arch/x86/entry/vdso/Makefile | 18 ++++++++++++++----
arch/x86/entry/vdso/vdso-timens.lds.S | 7 +++++++
arch/x86/entry/vdso/vdso32/.gitignore | 1 +
arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S | 8 ++++++++
5 files changed, 31 insertions(+), 4 deletions(-)
create mode 100644 arch/x86/entry/vdso/vdso-timens.lds.S
create mode 100644 arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S
diff --git a/arch/x86/entry/vdso/.gitignore b/arch/x86/entry/vdso/.gitignore
index 9ab4fa4c7e7b..aaddf8f2171c 100644
--- a/arch/x86/entry/vdso/.gitignore
+++ b/arch/x86/entry/vdso/.gitignore
@@ -1,4 +1,5 @@
vdso.lds
+vdso-timens.lds
vdsox32.lds
vdso32-syscall-syms.lds
vdso32-sysenter-syms.lds
diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile
index ccb572831ea1..4e1659619e7e 100644
--- a/arch/x86/entry/vdso/Makefile
+++ b/arch/x86/entry/vdso/Makefile
@@ -44,11 +44,14 @@ vobjs := $(foreach F,$(vobjs-y),$(obj)/$F)
vobjs32 := $(foreach F,$(vobjs32-y),$(obj)/$F)
vobjs-timens := $(foreach F,$(vobjs-timens-y),$(obj)/$F)
vobjs32-timens := $(foreach F,$(vobjs32-timens-y),$(obj)/$F)
+dep-vdso.lds-$(CONFIG_TIME_NS) += $(obj)/vdso-image-64-timens.c
+dep-vdso32.lds-$(CONFIG_TIME_NS) += $(obj)/vdso-image-32-timens.c
$(obj)/vdso.o: $(obj)/vdso.so
targets += vdso.lds $(vobjs-y) $(vobjs-timens-y) vdso64.entries
targets += vdso32/vdso32.lds $(vobjs32-y) $(vobjs32-timens-y) vdso32.entries
+targets += vdso-timens.lds vdso32/vdso32-timens.lds
# Build the vDSO image C files and link them in.
vdso_img_objs := $(vdso_img-y:%=vdso-image-%.o)
@@ -59,11 +62,13 @@ targets += $(vdso_img_cfiles)
targets += $(vdso_img_sodbg) $(vdso_img-y:%=vdso%.so)
CPPFLAGS_vdso.lds += -P -C
+CPPFLAGS_vdso-timens.lds := $(CPPFLAGS_vdso.lds)
VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -soname linux-vdso.so.1 --no-undefined \
-z max-page-size=4096
+VDSO_LDFLAGS_vdso-timens.lds := $(VDSO_LDFLAGS_vdso.lds)
-$(obj)/vdso64-timens.so.dbg: $(obj)/vdso.lds $(vobjs-timens) FORCE
+$(obj)/vdso64-timens.so.dbg: $(obj)/vdso-timens.lds $(vobjs-timens) FORCE
$(call if_changed,vdso)
$(obj)/vdso64.so.dbg: $(obj)/vdso.lds $(vobjs) FORCE
@@ -79,6 +84,9 @@ quiet_cmd_vdso2c = VDSO2C $@
$(obj)/vdso-image-%.c: $(obj)/vdso%.so.dbg $(obj)/vdso%.so $(obj)/vdso2c FORCE
$(call if_changed,vdso2c)
+$(obj)/vdso.lds: $(dep-vdso.lds-y) $(obj)/vdso2c FORCE
+$(obj)/vdso32/vdso32.lds: $(dep-vdso32.lds-y) $(obj)/vdso2c FORCE
+
#
# Don't omit frame pointers for ease of userspace debugging, but do
# optimize sibling calls.
@@ -142,8 +150,10 @@ $(obj)/%.so: $(obj)/%.so.dbg
$(obj)/vdsox32.so.dbg: $(obj)/vdsox32.lds $(vobjx32s) FORCE
$(call if_changed,vdso)
-CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds)
-VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -soname linux-gate.so.1
+CPPFLAGS_vdso32.lds := $(CPPFLAGS_vdso.lds)
+CPPFLAGS_vdso32-timens.lds := $(CPPFLAGS_vdso32.lds)
+VDSO_LDFLAGS_vdso32.lds := -m elf_i386 -soname linux-gate.so.1
+VDSO_LDFLAGS_vdso32-timens.lds := $(VDSO_LDFLAGS_vdso32.lds)
KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS)) -DBUILD_VDSO
$(obj)/vdso32.so.dbg: KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
@@ -172,7 +182,7 @@ endif
$(obj)/vdso32.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
$(obj)/vdso32-timens.so.dbg: KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
-$(obj)/vdso32-timens.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32-timens) FORCE
+$(obj)/vdso32-timens.so.dbg: $(obj)/vdso32/vdso32-timens.lds $(vobjs32-timens) FORCE
$(call if_changed,vdso)
$(obj)/vdso32.so.dbg: $(obj)/vdso32/vdso32.lds $(vobjs32) FORCE
diff --git a/arch/x86/entry/vdso/vdso-timens.lds.S b/arch/x86/entry/vdso/vdso-timens.lds.S
new file mode 100644
index 000000000000..687aba3bc5f0
--- /dev/null
+++ b/arch/x86/entry/vdso/vdso-timens.lds.S
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Linker script for 64-bit timens vDSO.
+ */
+
+#define UNALIGNED_ENTRIES
+#include "vdso.lds.S"
diff --git a/arch/x86/entry/vdso/vdso32/.gitignore b/arch/x86/entry/vdso/vdso32/.gitignore
index e45fba9d0ced..ce4afb6ffb62 100644
--- a/arch/x86/entry/vdso/vdso32/.gitignore
+++ b/arch/x86/entry/vdso/vdso32/.gitignore
@@ -1 +1,2 @@
vdso32.lds
+vdso32-timens.lds
diff --git a/arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S b/arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S
new file mode 100644
index 000000000000..1a3b3b1f0517
--- /dev/null
+++ b/arch/x86/entry/vdso/vdso32/vdso32-timens.lds.S
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Linker script for 32-bit timens vDSO.
+ */
+
+#define UNALIGNED_ENTRIES
+
+#include "vdso32.lds.S"
--
2.20.1
^ permalink raw reply related
* [PATCH 17/32] x86/vdso2c: Sort vdso entries by addresses for linker script
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
There are two linker scripts for vdso .so(s):
- *-timens.lds for building vdso for processes inside time namespace
(it has bigger functions and needs to build firstly)
- *.lds for host processes vdso
(it has smaller functions and entry addresses should be adjusted
with the linker script magic to fit with entries from timens)
To adjust entries on host vdso, *.lds includes *.entries.
Those are generated by vdso2c while parsing timens vdso.
Linker doesn't allow going back on some addresses, so sort entries
to timens VDSO before writing them to .entries file.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vdso2c.c | 13 +++++++++++++
arch/x86/entry/vdso/vdso2c.h | 20 +++++++++++++++++---
2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index 72731c4cfdce..4f91640398b2 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -119,6 +119,19 @@ static void fail(const char *format, ...)
va_end(ap);
}
+struct vdso_entry {
+ unsigned long addr;
+ const char *name;
+};
+
+static int entry_addr_cmp(const void *_a, const void *_b)
+{
+ const struct vdso_entry *a = _a;
+ const struct vdso_entry *b = _b;
+
+ return (a->addr < b->addr) - (a->addr > b->addr);
+}
+
/*
* Evil macros for little-endian reads and writes
*/
diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h
index 065dac6c29c8..50566dd94451 100644
--- a/arch/x86/entry/vdso/vdso2c.h
+++ b/arch/x86/entry/vdso/vdso2c.h
@@ -21,6 +21,7 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
ELF(Dyn) *dyn = 0, *dyn_end = 0;
const char *secstrings;
INT_BITS syms[NSYMS] = {};
+ struct vdso_entry *entries, *next_entry;
ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff));
@@ -88,6 +89,10 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link);
syms_nr = GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
+ entries = calloc(syms_nr, sizeof(*entries));
+ if (!entries)
+ fail("malloc()\n");
+ next_entry = entries;
/* Walk the symbol table */
for (i = 0; i < syms_nr; i++) {
unsigned int k;
@@ -122,11 +127,20 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
if (ELF_FUNC(ST_TYPE, sym->st_info) != STT_FUNC)
continue;
- fprintf(out_entries_lds, "\t\t. = ABSOLUTE(%#lx);\n",
- (unsigned long)GET_LE(&sym->st_value));
- fprintf(out_entries_lds, "\t\t*(.text.%s*)\n", name);
+ next_entry->addr = GET_LE(&sym->st_value);
+ next_entry->name = name;
+ next_entry++;
}
+ qsort(entries, next_entry - entries, sizeof(*entries), entry_addr_cmp);
+
+ while (next_entry != entries && out_entries_lds) {
+ next_entry--;
+ fprintf(out_entries_lds, "\t\t. = ABSOLUTE(%#lx);\n\t\t*(.text.%s*)\n",
+ next_entry->addr, next_entry->name);
+ }
+ free(entries);
+
/* Validate mapping addresses. */
for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) {
INT_BITS symval = syms[special_pages[i]];
--
2.20.1
^ permalink raw reply related
* [PATCH 18/32] x86/vdso.lds: Align !timens (host's) vdso.so entries
From: Dmitry Safonov @ 2019-02-06 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Adrian Reber, Andrei Vagin, Andrei Vagin,
Andy Lutomirski, Andy Tucker, Arnd Bergmann, Christian Brauner,
Cyrill Gorcunov, Dmitry Safonov, Eric W. Biederman,
H. Peter Anvin, Ingo Molnar, Jeff Dike, Oleg Nesterov,
Pavel Emelyanov, Shuah Khan, Thomas Gleixner, containers, criu,
linux-api, x86
In-Reply-To: <20190206001107.16488-1-dima@arista.com>
As it has been discussed on timens RFC, adding a new conditional branch
`if (inside_time_ns)` on VDSO for all processes is undesirable.
It will add a penalty for everybody as branch predictor may mispredict
the jump. Also there are instruction cache lines wasted on cmp/jmp.
Those effects of introducing time namespace are very much unwanted
having in mind how much work have been spent on micro-optimisation
vdso code.
Addressing those problems, there are two versions of VDSO's .so:
for host tasks (without any penalty) and for processes inside of time
namespace with clk_to_ns() that subtracts offsets from host's time.
Unfortunately, to allow changing VDSO VMA on a running process,
the entry points to VDSO should have the same offsets (addresses).
That's needed as i.e. application that calls setns() may have already
resolved VDSO symbols in GOT/PLT.
Align VDSO entries for host with addresses generated from timens VDSO
(which is bigger as it has code for adding offsets).
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vdso-layout.lds.S | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index ba216527e59f..e529ee3ec9e8 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -70,7 +70,17 @@ SECTIONS
* stuff that isn't used at runtime in between.
*/
- .text : { *(.text*) } :text =0x90909090,
+ .text : {
+#if defined(CONFIG_TIME_NS) && !defined(UNALIGNED_ENTRIES)
+#ifdef BUILD_VDSO32
+# include "vdso32.entries"
+#endif
+#ifdef BUILD_VDSO64
+# include "vdso64.entries"
+#endif
+#endif
+ *(.text*)
+ } :text =0x90909090,
.altinstructions : { *(.altinstructions) } :text
.altinstr_replacement : { *(.altinstr_replacement) } :text
--
2.20.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox