This patch series introduces a new clone flag, CLONE_FD, which lets the caller
receive child process exit notification via a file descriptor rather than
SIGCHLD. CLONE_FD makes it possible for libraries to safely launch and manage
child processes on behalf of their caller, *without* taking over process-wide
SIGCHLD handling (either via signal handler or signalfd).
Note that signalfd for SIGCHLD does not suffice here, because that still
receives notification for all child processes, and interferes with process-wide
signal handling.
The CLONE_FD file descriptor uniquely identifies a process on the system in a
race-free way, by holding a reference to the task_struct. In the future, we
may introduce APIs that support using process file descriptors instead of PIDs.
This patch series also introduces a clone flag CLONE_AUTOREAP, which causes the
kernel to automatically reap the child process when it exits, just as it does
for processes using SIGCHLD when the parent has SIGCHLD ignored or marked as
SA_NOCLDSTOP.
Taken together, a library can launch a process with CLONE_FD, CLONE_AUTOREAP,
and no exit signal, and completely avoid affecting either process-wide signal
handling or an existing child wait loop.
Introducing CLONE_FD and CLONE_AUTOREAP required two additional bits of yak
shaving: Since clone has no more usable flags (with the three currently unused
flags unusable because old kernels ignore them without EINVAL), also introduce
a new clone4 system call with more flag bits and an extensible argument
structure. And since the magic pt_regs-based syscall argument processing for
clone's tls argument would otherwise prevent introducing a sane clone4 system
call, fix that too.
I tested the CLONE_SETTLS changes with a thread-local storage test program (two
threads independently reading and writing a __thread variable), on both 32-bit
and 64-bit, and I observed no issues there.
I tested clone4 and the new flags with several additional test programs,
launching either a process or thread (in the former case using syscall(), in
the latter case by calling clone4 via assembly and returning to C), sleeping in
parent and child to test the case of either exiting first, and then printing
the received clone4_info structure.
Changes in v2:
- Split out autoreaping into a separate CLONE_AUTOREAP. CLONE_FD no longer
implies autoreaping and no exit signal, and CLONE_AUTOREAP does not affect
ptracers or signal handling. Thanks to Oleg Nesterov for careful
investigation and discussion on v1.
- Accept O_CLOEXEC and O_NONBLOCK via a clonefd_flags parameter in clone4_args.
Stop overloading the low byte of the main clone flags, since CLONE_FD now
works with a non-zero signal.
- Return the file descriptor via an out parameter in clone4_args.
- Drop patch to export alloc_fd; CLONE_FD now uses the next available file
descriptor, even if that's 0-2, since clone4 no longer needs to avoid
ambiguity with the 0 return indicating the child process.
- Make poll on a CLONE_FD for an exited task also return POLLHUP, for
compatibility with FreeBSD's pdfork. Thanks to David Drysdale for calling
attention to pdfork.
- Fix typo in squelch_clone_flags.
- Pass arguments to _do_fork and copy_process as a structure.
- Construct the 64-bit flags in a separate variable, rather than inline in the
call to do_fork.
- Fix error return for copy_from_user faults.
- Add the new syscall to asm-generic.
- Add ack from Andy Lutomirski to patches 1 and 2.
I've included the manpages patch at the end of this series. (Note that the
manpage documents the behavior of the future glibc wrapper as well as the raw
syscall.) Here's a formatted plain-text version of the manpage for reference:
CLONE4(2) Linux Programmer's Manual CLONE4(2)
NAME
clone4 - create a child process
SYNOPSIS
/* Prototype for the glibc wrapper function */
#define _GNU_SOURCE
#include <sched.h>
int clone4(uint64_t flags,
size_t args_size,
struct clone4_args *args,
int (*fn)(void *), void *arg);
/* Prototype for the raw system call */
int clone4(unsigned flags_high, unsigned flags_low,
unsigned long args_size,
struct clone4_args *args);
struct clone4_args {
pid_t *ptid;
pid_t *ctid;
unsigned long stack_start;
unsigned long stack_size;
unsigned long tls;
int *clonefd;
unsigned clonefd_flags;
};
DESCRIPTION
clone4() creates a new process, similar to clone(2) and fork(2).
clone4() supports additional flags that clone(2) does not, and accepts
arguments via an extensible structure.
args points to a clone4_args structure, and args_size must contain the
size of that structure, as understood by the caller. If the caller
passes a shorter structure than the kernel expects, the remaining
fields will default to 0. If the caller passes a larger structure than
the kernel expects (such as one from a newer kernel), clone4() will
return EINVAL. The clone4_args structure may gain additional fields at
the end in the future, and callers must only pass a size that encom‐
passes the number of fields they understand. If the caller passes 0
for args_size, args is ignored and may be NULL.
In the clone4_args structure, ptid, ctid, stack_start, stack_size, and
tls have the same semantics as they do with clone(2) and clone2(2).
In the glibc wrapper, fn and arg have the same semantics as they do
with clone(2). As with clone(2), the underlying system call works more
like fork(2), returning 0 in the child process; the glibc wrapper sim‐
plifies thread execution by calling fn(arg) and exiting the child when
that function exits.
The 64-bit flags argument (split into the 32-bit flags_high and
flags_low arguments in the kernel interface for portability across
architectures) accepts all the same flags as clone(2), with the excep‐
tion of the obsolete CLONE_PID, CLONE_DETACHED, and CLONE_STOPPED. In
addition, flags accepts the following flags:
CLONE_AUTOREAP
When the new process exits, immediately reap it, rather than
keeping it around as a "zombie" until a call to waitpid(2) or
similar. Without this flag, the kernel will automatically reap
a process if its exit signal is set to SIGCHLD, and if the par‐
ent process has SIGCHLD set to SIG_IGN or has a SIGCHLD handler
installed with SA_NOCLDWAIT (see sigaction(2)). CLONE_AUTOREAP
allows the calling process to enable automatic reaping with an
exit signal other than SIGCHLD (including 0 to disable the exit
signal), and does not depend on the configuration of process-
wide signal handling.
CLONE_FD
Return a file descriptor associated with the new process, stor‐
ing it in location clonefd in the parent's address space. When
the new process exits, the file descriptor will become available
for reading.
Unlike using signalfd(2) for the SIGCHLD signal, the file
descriptor returned by clone4() with the CLONE_FD flag works
even with SIGCHLD unblocked in one or more threads of the parent
process, allowing the process to have different handlers for
different child processes, such as those created by a library,
without introducing race conditions around process-wide signal
handling.
clonefd_flags may contain the following additional flags for use
with CLONE_FD:
O_CLOEXEC
Set the close-on-exec flag on the new file descriptor.
See the description of the O_CLOEXEC flag in open(2) for
reasons why this may be useful.
O_NONBLOCK
Set the O_NONBLOCK flag on the new file descriptor.
Using this flag saves extra calls to fcntl(2) to achieve
the same result.
The returned file descriptor supports the following operations:
read(2) (and similar)
When the new process exits, reading from the file
descriptor produces a single clonefd_info structure:
struct clonefd_info {
uint32_t code; /* Signal code */
uint32_t status; /* Exit status or signal */
uint64_t utime; /* User CPU time */
uint64_t stime; /* System CPU time */
};
If the new process has not yet exited, read(2) either
blocks until it does, or fails with the error EAGAIN if
the file descriptor has O_NONBLOCK set.
Future kernels may extend clonefd_info by appending addi‐
tional fields to the end. Callers should read as many
bytes as they understand; unread data will be discarded,
and subsequent reads after the first will return 0 to
indicate end-of-file. Callers requesting more bytes than
the kernel provides (such as callers expecting a newer
clonefd_info structure) will receive a shorter structure
from older kernels.
poll(2), select(2), epoll(7) (and similar)
The file descriptor is readable (the select(2) readfds
argument; the poll(2) POLLIN flag) if the new process has
exited.
close(2)
When the file descriptor is no longer required it should
be closed.
C library/kernel ABI differences
As with clone(2), the raw clone4() system call corresponds more closely
to fork(2) in that execution in the child continues from the point of
the call.
Unlike clone(2), the raw system call interface for clone4() accepts
arguments in the same order on all architectures.
The raw system call accepts flags as two 32-bit arguments, flags_high
and flags_low, to simplify portability across 32-bit and 64-bit archi‐
tectures and calling conventions. The glibc wrapper accepts flags as a
single 64-bit argument for convenience.
RETURN VALUE
For the glibc wrapper, on success, clone4() returns the new process ID
to the calling process, and the new process begins running at the spec‐
ified function.
For the raw syscall, on success, clone4() returns the new process ID to
the calling process, and returns 0 in the new process.
On failure, clone4() returns -1 and sets errno accordingly.
ERRORS
clone4() can return any error from clone(2), as well as the following
additional errors:
EFAULT args is outside your accessible address space.
EINVAL flags contained an unknown flag.
EINVAL flags included CLONE_FD and clonefd_flags contained an unknown
flag.
EINVAL flags included CLONE_FD, but the kernel configuration does not
have the CONFIG_CLONEFD option enabled.
EMFILE flags included CLONE_FD, but the new file descriptor would
exceed the process limit on open file descriptors.
ENFILE flags included CLONE_FD, but the new file descriptor would
exceed the system-wide limit on open file descriptors.
ENODEV flags included CLONE_FD, but clone4() could not mount the
(internal) anonymous inode device.
CONFORMING TO
clone4() is Linux-specific and should not be used in programs intended
to be portable.
SEE ALSO
clone(2), epoll(7), poll(2), pthreads(7), read(2), select(2)
Linux 2015-03-14 CLONE4(2)
Josh Triplett and Thiago Macieira (7):
clone: Support passing tls argument via C rather than pt_regs magic
x86: Opt into HAVE_COPY_THREAD_TLS, for both 32-bit and 64-bit
Introduce a new clone4 syscall with more flag bits and extensible arguments
kernel/fork.c: Pass arguments to _do_fork and copy_process using clone4_args
clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
signal: Factor out a helper function to process task_struct exit_code
clone4: Add a CLONE_FD flag to get task exit notification via fd
arch/Kconfig | 7 ++
arch/x86/Kconfig | 1 +
arch/x86/ia32/ia32entry.S | 3 +-
arch/x86/kernel/entry_64.S | 1 +
arch/x86/kernel/process_32.c | 6 +-
arch/x86/kernel/process_64.c | 8 +--
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 2 +
include/linux/compat.h | 14 ++++
include/linux/sched.h | 22 ++++++
include/linux/syscalls.h | 6 +-
include/uapi/asm-generic/unistd.h | 4 +-
include/uapi/linux/sched.h | 55 ++++++++++++++-
init/Kconfig | 21 ++++++
kernel/Makefile | 1 +
kernel/clonefd.c | 121 ++++++++++++++++++++++++++++++++
kernel/clonefd.h | 32 +++++++++
kernel/exit.c | 4 ++
kernel/fork.c | 142 ++++++++++++++++++++++++++++++--------
kernel/signal.c | 26 ++++---
kernel/sys_ni.c | 1 +
21 files changed, 426 insertions(+), 52 deletions(-)
create mode 100644 kernel/clonefd.c
create mode 100644 kernel/clonefd.h
--
2.1.4
clone with CLONE_SETTLS accepts an argument to set the thread-local
storage area for the new thread. sys_clone declares an int argument
tls_val in the appropriate point in the argument list (based on the
various CLONE_BACKWARDS variants), but doesn't actually use or pass
along that argument. Instead, sys_clone calls do_fork, which calls
copy_process, which calls the arch-specific copy_thread, and copy_thread
pulls the corresponding syscall argument out of the pt_regs captured at
kernel entry (knowing what argument of clone that architecture passes
tls in).
Apart from being awful and inscrutable, that also only works because
only one code path into copy_thread can pass the CLONE_SETTLS flag, and
that code path comes from sys_clone with its architecture-specific
argument-passing order. This prevents introducing a new version of the
clone system call without propagating the same architecture-specific
position of the tls argument.
However, there's no reason to pull the argument out of pt_regs when
sys_clone could just pass it down via C function call arguments.
Introduce a new CONFIG_HAVE_COPY_THREAD_TLS for architectures to opt
into, and a new copy_thread_tls that accepts the tls parameter as an
additional unsigned long (syscall-argument-sized) argument.
Change sys_clone's tls argument to an unsigned long (which does
not change the ABI), and pass that down to copy_thread_tls.
Architectures that don't opt into copy_thread_tls will continue to
ignore the C argument to sys_clone in favor of the pt_regs captured at
kernel entry, and thus will be unable to introduce new versions of the
clone syscall.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
Acked-by: Andy Lutomirski <luto@kernel.org>
---
arch/Kconfig | 7 ++++++
include/linux/sched.h | 14 ++++++++++++
include/linux/syscalls.h | 6 +++---
kernel/fork.c | 55 +++++++++++++++++++++++++++++++-----------------
4 files changed, 60 insertions(+), 22 deletions(-)
@@ -484,6 +484,13 @@ config HAVE_IRQ_EXIT_ON_IRQ_STACKThissparesastackswitchandimprovescacheusageonsoftirqprocessing.+configHAVE_COPY_THREAD_TLS+bool+help+Architectureprovidescopy_thread_tlstoaccepttlsargumentvia+normalCparameterpassing,ratherthanextractingthesyscall+argumentfrompt_regs.+## ABI hall of shame#
@@ -2479,8 +2479,22 @@ extern struct mm_struct *mm_access(struct task_struct *task, unsigned int mode);/* Remove the current tasks stale references to the old mm_struct */externvoidmm_release(structtask_struct*,structmm_struct*);+#ifdef CONFIG_HAVE_COPY_THREAD_TLS+externintcopy_thread_tls(unsignedlong,unsignedlong,unsignedlong,+structtask_struct*,unsignedlong);+#elseexternintcopy_thread(unsignedlong,unsignedlong,unsignedlong,structtask_struct*);++/* Architectures that haven't opted into copy_thread_tls get the tls argument+*viapt_regs,soignorethetlsargumentpassedviaC.*/+staticinlineintcopy_thread_tls(+unsignedlongclone_flags,unsignedlongsp,unsignedlongarg,+structtask_struct*p,unsignedlongtls)+{+returncopy_thread(clone_flags,sp,arg,p);+}+#endifexternvoidflush_thread(void);externvoidexit_thread(void);
@@ -1657,7 +1660,7 @@ long do_fork(unsigned long clone_flags,}p=copy_process(clone_flags,stack_start,stack_size,-child_tidptr,NULL,trace);+child_tidptr,NULL,trace,tls);/**Dothispriorwakingupthenewthread-thethreadpointer*mightgetinvalidafterthatpoint,ifthethreadexitsquickly.
@@ -1698,20 +1701,34 @@ long do_fork(unsigned long clone_flags,returnnr;}+#ifndef CONFIG_HAVE_COPY_THREAD_TLS+/* For compatibility with architectures that call do_fork directly rather than+*usingthesyscallentrypointsbelow.*/+longdo_fork(unsignedlongclone_flags,+unsignedlongstack_start,+unsignedlongstack_size,+int__user*parent_tidptr,+int__user*child_tidptr)+{+return_do_fork(clone_flags,stack_start,stack_size,+parent_tidptr,child_tidptr,0);+}+#endif+/**Createakernelthread.*/pid_tkernel_thread(int(*fn)(void*),void*arg,unsignedlongflags){-returndo_fork(flags|CLONE_VM|CLONE_UNTRACED,(unsignedlong)fn,-(unsignedlong)arg,NULL,NULL);+return_do_fork(flags|CLONE_VM|CLONE_UNTRACED,(unsignedlong)fn,+(unsignedlong)arg,NULL,NULL,0);}#ifdef __ARCH_WANT_SYS_FORKSYSCALL_DEFINE0(fork){#ifdef CONFIG_MMU-returndo_fork(SIGCHLD,0,0,NULL,NULL);+return_do_fork(SIGCHLD,0,0,NULL,NULL,0);#else/* can not support in nommu mode */return-EINVAL;
clone() has no more usable flags available. It has three now-unused
flags (CLONE_PID, CLONE_DETACHED, and CLONE_STOPPED), but current
kernels just ignore those flags without returning an error like EINVAL,
so reusing those flags would not allow userspace to detect the
availability of the new functionality.
Introduce a new system call, clone4, which accepts a second 32-bit flags
field. clone4 also returns EINVAL for the currently unused flags in
clone, allowing their reuse.
To process these new flags, change the flags argument of _do_fork to a
u64. sys_clone and do_fork both still use "unsigned long" for flags as
they did before, truncating it to 32-bit and masking out the obsolete
flags to behave like clone currently does.
clone4 accepts its remaining arguments as a structure, and userspace
passes in the size of that structure. clone4 has well-defined semantics
that allow extending that structure in the future. New userspace
passing in a larger structure than the kernel expects will receive
EINVAL, and can use a smaller structure to work with old kernels. New
kernels accept smaller argument structures passed by userspace, and any
un-passed arguments default to 0.
clone4 handles arguments in the same order on all architectures, with no
backwards variations; to do so, it depends on the new
HAVE_COPY_THREAD_TLS.
The new system call currently accepts exactly the same flags as clone;
future commits will introduce new flags for additional functionality.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
---
arch/x86/ia32/ia32entry.S | 1 +
arch/x86/kernel/entry_64.S | 1 +
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 2 ++
include/linux/compat.h | 12 +++++++++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/sched.h | 36 ++++++++++++++++++++++---
init/Kconfig | 10 +++++++
kernel/fork.c | 56 ++++++++++++++++++++++++++++++++++++---
kernel/sys_ni.c | 1 +
10 files changed, 116 insertions(+), 8 deletions(-)
@@ -18,11 +20,8 @@#define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */-#define CLONE_DETACHED 0x00400000 /* Unused, ignored */#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */-/* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state)-andisnowavailableforre-use.*/#define CLONE_NEWUTS 0x04000000 /* New utsname namespace */#define CLONE_NEWIPC 0x08000000 /* New ipc namespace */#define CLONE_NEWUSER 0x10000000 /* New user namespace */
@@ -1701,6 +1704,15 @@ static long _do_fork(returnnr;}+/*+*Conveniencefunctionforcallerspassingunsignedlongflags,topreventold+*syscallentrypointsfromunexpectedlyreturningEINVAL.+*/+staticinlineu64squelch_clone_flags(unsignedlongclone_flags)+{+returnclone_flags&CLONE_VALID_FLAGS;+}+#ifndef CONFIG_HAVE_COPY_THREAD_TLS/* For compatibility with architectures that call do_fork directly rather than*usingthesyscallentrypointsbelow.*/
@@ -1710,7 +1722,8 @@ long do_fork(unsigned long clone_flags,int__user*parent_tidptr,int__user*child_tidptr){-return_do_fork(clone_flags,stack_start,stack_size,+return_do_fork(squelch_clone_flags(clone_flags),+stack_start,stack_size,parent_tidptr,child_tidptr,0);}#endif
Rather than continuing to add arguments to _do_fork and copy_process for
future clone4 extensions, with corresponding churn in every caller, pass
the arguments using the clone4_args structure instead. This allows
clone4 to avoid unpacking the arguments, and allows other callers to use
C99 structure initializers to only initialize the arguments they care
about. Future extensions to clone4_args will thus not need to touch
clone4, fork, vfork, or other callers of _do_fork.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
---
kernel/fork.c | 77 +++++++++++++++++++++++++++++++----------------------------
1 file changed, 41 insertions(+), 36 deletions(-)
@@ -1662,8 +1654,7 @@ static long _do_fork(trace=0;}-p=copy_process(clone_flags,stack_start,stack_size,-child_tidptr,NULL,trace,tls);+p=copy_process(clone_flags,args,NULL,trace);/**Dothispriorwakingupthenewthread-thethreadpointer*mightgetinvalidafterthatpoint,ifthethreadexitsquickly.
@@ -1678,7 +1669,7 @@ static long _do_fork(nr=pid_vnr(pid);if(clone_flags&CLONE_PARENT_SETTID)-put_user(nr,parent_tidptr);+put_user(nr,args->ptid);if(clone_flags&CLONE_VFORK){p->vfork_done=&vfork;
@@ -1722,9 +1713,13 @@ long do_fork(unsigned long clone_flags,int__user*parent_tidptr,int__user*child_tidptr){-return_do_fork(squelch_clone_flags(clone_flags),-stack_start,stack_size,-parent_tidptr,child_tidptr,0);+structclone4_argskargs={+.ptid=parent_tidptr,+.ctid=child_tidptr,+.stack_start=stack_start,+.stack_start=stack_size,+};+return_do_fork(squelch_clone_flags(clone_flags),&kargs);}#endif
@@ -1733,15 +1728,19 @@ long do_fork(unsigned long clone_flags,*/pid_tkernel_thread(int(*fn)(void*),void*arg,unsignedlongflags){-return_do_fork(flags|CLONE_VM|CLONE_UNTRACED,(unsignedlong)fn,-(unsignedlong)arg,NULL,NULL,0);+structclone4_argskargs={+.stack_start=(unsignedlong)fn,+.stack_size=(unsignedlong)arg,+};+return_do_fork(flags|CLONE_VM|CLONE_UNTRACED,&kargs);}#ifdef __ARCH_WANT_SYS_FORKSYSCALL_DEFINE0(fork){#ifdef CONFIG_MMU-return_do_fork(SIGCHLD,0,0,NULL,NULL,0);+structclone4_argskargs={};+return_do_fork(SIGCHLD,&kargs);#else/* can not support in nommu mode */return-EINVAL;
If a process launches a child process with the notification signal set
to SIGCHLD (e.g. with fork()), and then the parent process either
ignores SIGCHLD or sets a handler with SA_NOCLDWAIT, the child process
will get automatically reaped without waiting for the parent to wait on
it.
However, there's currently no way to get the same autoreaping behavior
if the signal is not set to SIGCHLD, including in particular if the
signal is set to 0 to disable notification. Furthermore, the code
launching the child process may not own process-wide signal handling for
the parent process.
Add a CLONE_AUTOREAP flag to request this behavior unconditionally,
regardless of the notification signal or the state of the parent
process's signal handling when the process exits.
This is particularly useful for libraries that want to launch unattended
child processes without interfering with the calling process's signal
handling or wait loop.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
---
include/linux/sched.h | 2 ++
include/uapi/linux/sched.h | 7 ++++++-
kernel/fork.c | 2 ++
kernel/signal.c | 2 ++
4 files changed, 12 insertions(+), 1 deletion(-)
@@ -1372,6 +1372,8 @@ struct task_struct {unsignedmemcg_kmem_skip_account:1;#endif+unsignedautoreap:1;/* Do not become a zombie on exit */+unsignedlongatomic_flags;/* Flags needing atomic access. */structrestart_blockrestart_block;
do_notify_parent includes the code to convert the exit_code field of
struct task_struct to the code and status fields that accompany SIGCHLD.
Factor that out into a new helper function task_exit_code_status, to
allow other methods of task exit notification to share that code.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
---
include/linux/sched.h | 1 +
kernel/signal.c | 24 +++++++++++++++---------
2 files changed, 16 insertions(+), 9 deletions(-)
When passed CLONE_FD, clone4 hands the caller a file descriptor
referring to the new process. When the new process exits, the file
descriptor becomes readable, producing a structure containing the exit
status, exit code, and user/system times. The file descriptor also
works in epoll, poll, and select.
This allows libraries to safely launch and manage child processes on
behalf of a caller, without taking over or interfering with process-wide
signal handling. Without this, such a library would need to take over
or cooperate with the entire process's SIGCHLD handling, either via a
signal handler or a signalfd.
Signed-off-by: Josh Triplett <josh@joshtriplett.org>
Signed-off-by: Thiago Macieira <redacted>
---
include/linux/compat.h | 2 +
include/linux/sched.h | 5 ++
include/uapi/linux/sched.h | 16 +++++-
init/Kconfig | 11 +++++
kernel/Makefile | 1 +
kernel/clonefd.c | 121 +++++++++++++++++++++++++++++++++++++++++++++
kernel/clonefd.h | 32 ++++++++++++
kernel/exit.c | 4 ++
kernel/fork.c | 22 +++++++--
9 files changed, 209 insertions(+), 5 deletions(-)
create mode 100644 kernel/clonefd.c
create mode 100644 kernel/clonefd.h
@@ -1374,6 +1374,11 @@ struct task_struct {unsignedautoreap:1;/* Do not become a zombie on exit */+#ifdef CONFIG_CLONEFD+unsignedclonefd:1;/* Notify clonefd_wqh on exit */+wait_queue_head_tclonefd_wqh;+#endif+unsignedlongatomic_flags;/* Flags needing atomic access. */structrestart_blockrestart_block;
@@ -0,0 +1,121 @@+/*+*SupportfunctionsforCLONE_FD+*+*Copyright(c)2015IntelCorporation+*Originalauthors:JoshTriplett<josh@joshtriplett.org>+*ThiagoMacieira<thiago@macieira.org>+*/+#include<linux/anon_inodes.h>+#include<linux/file.h>+#include<linux/fs.h>+#include<linux/poll.h>+#include<linux/slab.h>+#include"clonefd.h"++staticintclonefd_release(structinode*inode,structfile*file)+{+put_task_struct(file->private_data);+return0;+}++staticunsignedintclonefd_poll(structfile*file,poll_table*wait)+{+structtask_struct*p=file->private_data;+poll_wait(file,&p->clonefd_wqh,wait);+returnp->exit_state?(POLLIN|POLLRDNORM|POLLHUP):0;+}++staticssize_tclonefd_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos)+{+structtask_struct*p=file->private_data;+intret=0;++/* EOF after first read */+if(*ppos)+return0;++if(file->f_flags&O_NONBLOCK)+ret=-EAGAIN;+else+ret=wait_event_interruptible(p->clonefd_wqh,p->exit_state);++if(p->exit_state){+structclonefd_infoinfo={};+cputime_tutime,stime;+task_exit_code_status(p->exit_code,&info.code,&info.status);+info.code&=~__SI_MASK;+task_cputime(p,&utime,&stime);+info.utime=cputime_to_clock_t(utime+p->signal->utime);+info.stime=cputime_to_clock_t(stime+p->signal->stime);+ret=simple_read_from_buffer(buf,count,ppos,&info,sizeof(info));+}+returnret;+}++staticstructfile_operationsclonefd_fops={+.release=clonefd_release,+.poll=clonefd_poll,+.read=clonefd_read,+.llseek=no_llseek,+};++/* Do process exit notification for clonefd. */+voidclonefd_do_notify(structtask_struct*p)+{+if(p->clonefd)+wake_up_all(&p->clonefd_wqh);+}++/* Handle the CLONE_FD case for copy_process. */+intclonefd_do_clone(u64clone_flags,structtask_struct*p,+structclone4_args*args,structclonefd_setup*setup)+{+intflags;+structfile*file;+intfd;++p->clonefd=!!(clone_flags&CLONE_FD);+if(!p->clonefd)+return0;++if(args->clonefd_flags&~(O_CLOEXEC|O_NONBLOCK))+return-EINVAL;++init_waitqueue_head(&p->clonefd_wqh);++get_task_struct(p);+flags=O_RDONLY|FMODE_ATOMIC_POS|args->clonefd_flags;+file=anon_inode_getfile("[process]",&clonefd_fops,p,flags);+if(IS_ERR(file)){+put_task_struct(p);+returnPTR_ERR(file);+}++fd=get_unused_fd_flags(flags);+if(fd<0){+fput(file);+returnfd;+}++setup->fd=fd;+setup->file=file;+return0;+}++/* Clean up clonefd information after a partially complete clone */+voidclonefd_cleanup_failed_clone(structclonefd_setup*setup)+{+if(setup->file){+put_unused_fd(setup->fd);+fput(setup->file);+}+}++/* Finish setting up the clonefd */+voidclonefd_install_fd(structclone4_args*args,structclonefd_setup*setup)+{+if(setup->file){+fd_install(setup->fd,setup->file);+put_user(setup->fd,args->clonefd);+}+}
@@ -615,6 +617,8 @@ static void exit_notify(struct task_struct *tsk, int group_dead)if(tsk->exit_state==EXIT_DEAD)list_add(&tsk->ptrace_entry,&dead);+clonefd_do_notify(tsk);+/* mt-exec, de_thread() is waiting for group leader */if(unlikely(tsk->signal->notify_count<0))wake_up_process(tsk->signal->group_exit_task);
@@ -0,0 +1,345 @@+.\" Based on clone.2:+.\" Copyright (c) 1992 Drew Eckhardt <drew@cs.colorado.edu>, March 28, 1992+.\" and Copyright (c) Michael Kerrisk, 2001, 2002, 2005, 2013+.\"+.\" %%%LICENSE_START(GPL_NOVERSION_ONELINE)+.\" May be distributed under the GNU General Public License.+.\" %%%LICENSE_END+.THCLONE422015-03-14"Linux""Linux Programmer's Manual"+.SHNAME+clone4 \- create a child process+.SHSYNOPSIS+.nf+/* Prototype for the glibc wrapper function */++.B#define_GNU_SOURCE+.B#include<sched.h>++.BI"int clone4(uint64_t "flags,+.BI" size_t "args_size,+.BI" struct clone4_args *"args,+.BI" int (*""fn"")(void *), void *"arg);++/* Prototype for the raw system call */++.BI"int clone4(unsigned "flags_high", unsigned "flags_low,+.BI" unsigned long "args_size,+.BI" struct clone4_args *"args);++struct clone4_args {+ pid_t *ptid;+ pid_t *ctid;+ unsigned long stack_start;+ unsigned long stack_size;+ unsigned long tls;+ int *clonefd;+ unsigned clonefd_flags;+};++.SHDESCRIPTION+.BRclone4()+creates a new process, similar to+.BRclone(2)+and+.BRfork(2).+.BRclone4()+supports additional flags that+.BRclone(2)+does not, and accepts arguments via an extensible structure.++.Iargs+points to a+.Iclone4_args+structure, and+.Iargs_size+must contain the size of that structure, as understood by the caller. If the+caller passes a shorter structure than the kernel expects, the remaining fields+will default to 0. If the caller passes a larger structure than the kernel+expects (such as one from a newer kernel),+.BRclone4()+will return+.BREINVAL.+The+.Iclone4_args+structure may gain additional fields at the end in the future, and callers must+only pass a size that encompasses the number of fields they understand. If the+caller passes 0 for+.IRargs_size,+.Iargs+is ignored and may be NULL.++In the+.Iclone4_args+structure,+.IRptid,+.IRctid,+.IRstack_start,+.IRstack_size,+and+.Itls+have the same semantics as they do with+.BRclone(2)+and+.BRclone2(2).++In the glibc wrapper,+.Ifn+and+.Iarg+have the same semantics as they do with+.BRclone(2).+As with+.BRclone(2),+the underlying system call works more like+.BRfork(2),+returning 0 in the child process; the glibc wrapper simplifies thread execution+by calling+.IRfn(arg)+and exiting the child when that function exits.++The 64-bit+.Iflags+argument (split into the 32-bit+.Iflags_high+and+.Iflags_low+arguments in the kernel interface for portability across architectures)+accepts all the same flags as+.BRclone(2),+with the exception of the obsolete+.BRCLONE_PID,+.BRCLONE_DETACHED,+and+.BRCLONE_STOPPED.+In addition,+.Iflags+accepts the following flags:++.TP+.BCLONE_AUTOREAP+When the new process exits, immediately reap it, rather than keeping it around+as a "zombie" until a call to+.BRwaitpid(2)+or similar. Without this flag, the kernel will automatically reap a process if+its exit signal is set to+.BRSIGCHLD,+and if the parent process has+.BSIGCHLD+set to+.BSIG_IGN+or has a+.BSIGCHLD+handler installed with+.BSA_NOCLDWAIT+(see+.BRsigaction(2)).+.BCLONE_AUTOREAP+allows the calling process to enable automatic reaping with an exit signal+other than+.BSIGCHLD+(including 0 to disable the exit signal), and does not depend on the+configuration of process-wide signal handling.++.TP+.BCLONE_FD+Return a file descriptor associated with the new process, storing it in+location+.Iclonefd+in the parent's address space. When the new process exits, the file descriptor+will become available for reading.++Unlike using+.BRsignalfd(2)+for the+.BSIGCHLD+signal,+the file descriptor returned by+.BRclone4()+with the+.BCLONE_FD+flag works even with+.BSIGCHLD+unblocked in one or more threads of the parent process, allowing the process to+have different handlers for different child processes, such as those created by+a library, without introducing race conditions around process-wide signal+handling.++.Iclonefd_flags+may contain the following additional flags for use with+.BRCLONE_FD:++.RS+.TP+.BO_CLOEXEC+Set the close-on-exec flag on the new file descriptor. See the description of+the+.BO_CLOEXEC+flag in+.BRopen(2)+for reasons why this may be useful.++.TP+.BO_NONBLOCK+Set the+.BO_NONBLOCK+flag on the new file descriptor. Using this flag saves extra calls to+.BRfcntl(2)+to achieve the same result.+.RE++.IP+The returned file descriptor supports the following operations:+.RS+.TP+.BRread"(2) (and similar)"+When the new process exits, reading from the file descriptor produces+a single+.Iclonefd_info+structure:+.nf++struct clonefd_info {+ uint32_t code; /* Signal code */+ uint32_t status; /* Exit status or signal */+ uint64_t utime; /* User CPU time */+ uint64_t stime; /* System CPU time */+};++.fi+.IP+If the new process has not yet exited,+.BRread(2)+either blocks until it does, or fails with the error+.BEAGAIN+if the file descriptor has+.BO_NONBLOCK+set.+.IP+Future kernels may extend+.Iclonefd_info+by appending additional fields to the end. Callers should read as many bytes+as they understand; unread data will be discarded, and subsequent reads after+the first will return 0 to indicate end-of-file. Callers requesting more bytes+than the kernel provides (such as callers expecting a newer+.Iclonefd_info+structure) will receive a shorter structure from older kernels.+.TP+.BRpoll"(2), "select"(2), "epoll"(7) (and similar)"+The file descriptor is readable+(the+.BRselect(2)+.Ireadfds+argument; the+.BRpoll(2)+.BPOLLIN+flag)+if the new process has exited.+.TP+.BRclose(2)+When the file descriptor is no longer required it should be closed.+.RE++.SSClibrary/kernelABIdifferences+As with+.BRclone(2),+the raw+.BRclone4()+system call corresponds more closely to+.BRfork(2)+in that execution in the child continues from the point of the call.++Unlike+.BRclone(2),+the raw system call interface for+.BRclone4()+accepts arguments in the same order on all architectures.++The raw system call accepts+.Iflags+as two 32-bit arguments,+.Iflags_high+and+.IRflags_low,+to simplify portability across 32-bit and 64-bit architectures and calling+conventions. The glibc wrapper accepts+.Iflags+as a single 64-bit argument for convenience.++.SHRETURNVALUE+For the glibc wrapper, on success,+.BRclone4()+returns the new process ID to the calling process, and the new process begins+running at the specified function.++For the raw syscall, on success,+.BRclone4()+returns the new process ID to the calling process, and returns 0 in the new+process.++On failure,+.BRclone4()+returns \-1 and sets+.Ierrno+accordingly.++.SHERRORS+.BRclone4()+can return any error from+.BRclone(2),+as well as the following additional errors:+.TP+.BEFAULT+.Iargs+is outside your accessible address space.+.TP+.BEINVAL+.Iflags+contained an unknown flag.+.TP+.BEINVAL+.Iflags+included+.BCLONE_FD+and+.Iclonefd_flags+contained an unknown flag.+.TP+.BEINVAL+.Iflags+included+.BRCLONE_FD,+but the kernel configuration does not have the+.BCONFIG_CLONEFD+option enabled.+.TP+.BEMFILE+.Iflags+included+.BRCLONE_FD,+but the new file descriptor would exceed the process limit on open file descriptors.+.TP+.BENFILE+.Iflags+included+.BRCLONE_FD,+but the new file descriptor would exceed the system-wide limit on open file descriptors.+.TP+.BENODEV+.Iflags+included+.BRCLONE_FD,+but+.BRclone4()+could not mount the (internal) anonymous inode device.++.SHCONFORMINGTO+.BRclone4()+is Linux-specific and should not be used in programs intended to be portable.++.SHSEEALSO+.BRclone(2),+.BRepoll(7),+.BRpoll(2),+.BRpthreads(7),+.BRread(2),+.BRselect(2)
On Sun, Mar 15, 2015 at 12:59 AM, Josh Triplett [off-list ref] wrote:
This patch series introduces a new clone flag, CLONE_FD, which lets the caller
receive child process exit notification via a file descriptor rather than
SIGCHLD. CLONE_FD makes it possible for libraries to safely launch and manage
child processes on behalf of their caller, *without* taking over process-wide
SIGCHLD handling (either via signal handler or signalfd).
Note that signalfd for SIGCHLD does not suffice here, because that still
receives notification for all child processes, and interferes with process-wide
signal handling.
The CLONE_FD file descriptor uniquely identifies a process on the system in a
race-free way, by holding a reference to the task_struct. In the future, we
may introduce APIs that support using process file descriptors instead of PIDs.
This patch series also introduces a clone flag CLONE_AUTOREAP, which causes the
kernel to automatically reap the child process when it exits, just as it does
for processes using SIGCHLD when the parent has SIGCHLD ignored or marked as
SA_NOCLDSTOP.
Taken together, a library can launch a process with CLONE_FD, CLONE_AUTOREAP,
and no exit signal, and completely avoid affecting either process-wide signal
handling or an existing child wait loop.
Introducing CLONE_FD and CLONE_AUTOREAP required two additional bits of yak
shaving: Since clone has no more usable flags (with the three currently unused
flags unusable because old kernels ignore them without EINVAL), also introduce
a new clone4 system call with more flag bits and an extensible argument
structure. And since the magic pt_regs-based syscall argument processing for
clone's tls argument would otherwise prevent introducing a sane clone4 system
call, fix that too.
I tested the CLONE_SETTLS changes with a thread-local storage test program (two
threads independently reading and writing a __thread variable), on both 32-bit
and 64-bit, and I observed no issues there.
I tested clone4 and the new flags with several additional test programs,
launching either a process or thread (in the former case using syscall(), in
the latter case by calling clone4 via assembly and returning to C), sleeping in
parent and child to test the case of either exiting first, and then printing
the received clone4_info structure.
Changes in v2:
- Split out autoreaping into a separate CLONE_AUTOREAP. CLONE_FD no longer
implies autoreaping and no exit signal, and CLONE_AUTOREAP does not affect
ptracers or signal handling. Thanks to Oleg Nesterov for careful
investigation and discussion on v1.
- Accept O_CLOEXEC and O_NONBLOCK via a clonefd_flags parameter in clone4_args.
Stop overloading the low byte of the main clone flags, since CLONE_FD now
works with a non-zero signal.
- Return the file descriptor via an out parameter in clone4_args.
- Drop patch to export alloc_fd; CLONE_FD now uses the next available file
descriptor, even if that's 0-2, since clone4 no longer needs to avoid
ambiguity with the 0 return indicating the child process.
- Make poll on a CLONE_FD for an exited task also return POLLHUP, for
compatibility with FreeBSD's pdfork. Thanks to David Drysdale for calling
attention to pdfork.
I think POLLHUP should be mentioned in the manpage (now it only
mentions POLLIN).
- Fix typo in squelch_clone_flags.
- Pass arguments to _do_fork and copy_process as a structure.
- Construct the 64-bit flags in a separate variable, rather than inline in the
call to do_fork.
- Fix error return for copy_from_user faults.
- Add the new syscall to asm-generic.
- Add ack from Andy Lutomirski to patches 1 and 2.
I've included the manpages patch at the end of this series. (Note that the
manpage documents the behavior of the future glibc wrapper as well as the raw
syscall.) Here's a formatted plain-text version of the manpage for reference:
CLONE4(2) Linux Programmer's Manual CLONE4(2)
NAME
clone4 - create a child process
SYNOPSIS
/* Prototype for the glibc wrapper function */
#define _GNU_SOURCE
#include <sched.h>
int clone4(uint64_t flags,
size_t args_size,
struct clone4_args *args,
int (*fn)(void *), void *arg);
/* Prototype for the raw system call */
int clone4(unsigned flags_high, unsigned flags_low,
unsigned long args_size,
struct clone4_args *args);
struct clone4_args {
pid_t *ptid;
pid_t *ctid;
unsigned long stack_start;
unsigned long stack_size;
unsigned long tls;
int *clonefd;
unsigned clonefd_flags;
};
DESCRIPTION
clone4() creates a new process, similar to clone(2) and fork(2).
clone4() supports additional flags that clone(2) does not, and accepts
arguments via an extensible structure.
args points to a clone4_args structure, and args_size must contain the
size of that structure, as understood by the caller. If the caller
passes a shorter structure than the kernel expects, the remaining
fields will default to 0. If the caller passes a larger structure than
the kernel expects (such as one from a newer kernel), clone4() will
return EINVAL. The clone4_args structure may gain additional fields at
the end in the future, and callers must only pass a size that encom‐
passes the number of fields they understand. If the caller passes 0
for args_size, args is ignored and may be NULL.
In the clone4_args structure, ptid, ctid, stack_start, stack_size, and
tls have the same semantics as they do with clone(2) and clone2(2).
In the glibc wrapper, fn and arg have the same semantics as they do
with clone(2). As with clone(2), the underlying system call works more
like fork(2), returning 0 in the child process; the glibc wrapper sim‐
plifies thread execution by calling fn(arg) and exiting the child when
that function exits.
The 64-bit flags argument (split into the 32-bit flags_high and
flags_low arguments in the kernel interface for portability across
architectures) accepts all the same flags as clone(2), with the excep‐
tion of the obsolete CLONE_PID, CLONE_DETACHED, and CLONE_STOPPED. In
addition, flags accepts the following flags:
CLONE_AUTOREAP
When the new process exits, immediately reap it, rather than
keeping it around as a "zombie" until a call to waitpid(2) or
similar. Without this flag, the kernel will automatically reap
a process if its exit signal is set to SIGCHLD, and if the par‐
ent process has SIGCHLD set to SIG_IGN or has a SIGCHLD handler
installed with SA_NOCLDWAIT (see sigaction(2)). CLONE_AUTOREAP
allows the calling process to enable automatic reaping with an
exit signal other than SIGCHLD (including 0 to disable the exit
signal), and does not depend on the configuration of process-
wide signal handling.
CLONE_FD
Return a file descriptor associated with the new process, stor‐
ing it in location clonefd in the parent's address space. When
the new process exits, the file descriptor will become available
for reading.
Unlike using signalfd(2) for the SIGCHLD signal, the file
descriptor returned by clone4() with the CLONE_FD flag works
even with SIGCHLD unblocked in one or more threads of the parent
process, allowing the process to have different handlers for
different child processes, such as those created by a library,
without introducing race conditions around process-wide signal
handling.
clonefd_flags may contain the following additional flags for use
with CLONE_FD:
O_CLOEXEC
Set the close-on-exec flag on the new file descriptor.
See the description of the O_CLOEXEC flag in open(2) for
reasons why this may be useful.
This begs the question: what happens when all CLONE_FD fds for a
process are closed? Will the parent get SIGCHLD instead, will it
auto-reap, or will it be un-wait-able (I assume not this...)
O_NONBLOCK
Set the O_NONBLOCK flag on the new file descriptor.
Using this flag saves extra calls to fcntl(2) to achieve
the same result.
The returned file descriptor supports the following operations:
read(2) (and similar)
When the new process exits, reading from the file
descriptor produces a single clonefd_info structure:
struct clonefd_info {
uint32_t code; /* Signal code */
uint32_t status; /* Exit status or signal */
uint64_t utime; /* User CPU time */
uint64_t stime; /* System CPU time */
};
If the new process has not yet exited, read(2) either
blocks until it does, or fails with the error EAGAIN if
the file descriptor has O_NONBLOCK set.
Future kernels may extend clonefd_info by appending addi‐
tional fields to the end. Callers should read as many
bytes as they understand; unread data will be discarded,
and subsequent reads after the first will return 0 to
indicate end-of-file. Callers requesting more bytes than
the kernel provides (such as callers expecting a newer
clonefd_info structure) will receive a shorter structure
from older kernels.
poll(2), select(2), epoll(7) (and similar)
The file descriptor is readable (the select(2) readfds
argument; the poll(2) POLLIN flag) if the new process has
exited.
close(2)
When the file descriptor is no longer required it should
be closed.
C library/kernel ABI differences
As with clone(2), the raw clone4() system call corresponds more closely
to fork(2) in that execution in the child continues from the point of
the call.
Unlike clone(2), the raw system call interface for clone4() accepts
arguments in the same order on all architectures.
The raw system call accepts flags as two 32-bit arguments, flags_high
and flags_low, to simplify portability across 32-bit and 64-bit archi‐
tectures and calling conventions. The glibc wrapper accepts flags as a
single 64-bit argument for convenience.
RETURN VALUE
For the glibc wrapper, on success, clone4() returns the new process ID
to the calling process, and the new process begins running at the spec‐
ified function.
For the raw syscall, on success, clone4() returns the new process ID to
the calling process, and returns 0 in the new process.
On failure, clone4() returns -1 and sets errno accordingly.
ERRORS
clone4() can return any error from clone(2), as well as the following
additional errors:
EFAULT args is outside your accessible address space.
EINVAL flags contained an unknown flag.
EINVAL flags included CLONE_FD and clonefd_flags contained an unknown
flag.
EINVAL flags included CLONE_FD, but the kernel configuration does not
have the CONFIG_CLONEFD option enabled.
EMFILE flags included CLONE_FD, but the new file descriptor would
exceed the process limit on open file descriptors.
ENFILE flags included CLONE_FD, but the new file descriptor would
exceed the system-wide limit on open file descriptors.
ENODEV flags included CLONE_FD, but clone4() could not mount the
(internal) anonymous inode device.
CONFORMING TO
clone4() is Linux-specific and should not be used in programs intended
to be portable.
SEE ALSO
clone(2), epoll(7), poll(2), pthreads(7), read(2), select(2)
Linux 2015-03-14 CLONE4(2)
Josh Triplett and Thiago Macieira (7):
clone: Support passing tls argument via C rather than pt_regs magic
x86: Opt into HAVE_COPY_THREAD_TLS, for both 32-bit and 64-bit
Introduce a new clone4 syscall with more flag bits and extensible arguments
kernel/fork.c: Pass arguments to _do_fork and copy_process using clone4_args
clone4: Add a CLONE_AUTOREAP flag to automatically reap the child process
signal: Factor out a helper function to process task_struct exit_code
clone4: Add a CLONE_FD flag to get task exit notification via fd
arch/Kconfig | 7 ++
arch/x86/Kconfig | 1 +
arch/x86/ia32/ia32entry.S | 3 +-
arch/x86/kernel/entry_64.S | 1 +
arch/x86/kernel/process_32.c | 6 +-
arch/x86/kernel/process_64.c | 8 +--
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 2 +
include/linux/compat.h | 14 ++++
include/linux/sched.h | 22 ++++++
include/linux/syscalls.h | 6 +-
include/uapi/asm-generic/unistd.h | 4 +-
include/uapi/linux/sched.h | 55 ++++++++++++++-
init/Kconfig | 21 ++++++
kernel/Makefile | 1 +
kernel/clonefd.c | 121 ++++++++++++++++++++++++++++++++
kernel/clonefd.h | 32 +++++++++
kernel/exit.c | 4 ++
kernel/fork.c | 142 ++++++++++++++++++++++++++++++--------
kernel/signal.c | 26 ++++---
kernel/sys_ni.c | 1 +
21 files changed, 426 insertions(+), 52 deletions(-)
create mode 100644 kernel/clonefd.c
create mode 100644 kernel/clonefd.h
--
2.1.4
Looks promising!
-Kees
--
Kees Cook
Chrome OS Security
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
@@ -1374,6 +1374,11 @@ struct task_struct {unsignedautoreap:1;/* Do not become a zombie on exit */+#ifdef CONFIG_CLONEFD+unsignedclonefd:1;/* Notify clonefd_wqh on exit */+wait_queue_head_tclonefd_wqh;+#endif+unsignedlongatomic_flags;/* Flags needing atomic access. */structrestart_blockrestart_block;
Idle thought: are there any concerns about the occupancy
impact of adding a wait_queue_head to every task_struct,
whether it has a clonefd or not?
I guess we could reduce the size somewhat by just
storing a struct file *clonefd_file in the task, and then have
a separate structure (with the wqh and a task_struct*) referenced
by file->private_data. Not sure whether the added complication
would be worthwhile, though.
@@ -0,0 +1,121 @@+/*+*SupportfunctionsforCLONE_FD+*+*Copyright(c)2015IntelCorporation+*Originalauthors:JoshTriplett<josh@joshtriplett.org>+*ThiagoMacieira<thiago@macieira.org>+*/+#include<linux/anon_inodes.h>+#include<linux/file.h>+#include<linux/fs.h>+#include<linux/poll.h>+#include<linux/slab.h>+#include"clonefd.h"++staticintclonefd_release(structinode*inode,structfile*file)+{+put_task_struct(file->private_data);+return0;+}++staticunsignedintclonefd_poll(structfile*file,poll_table*wait)+{+structtask_struct*p=file->private_data;+poll_wait(file,&p->clonefd_wqh,wait);+returnp->exit_state?(POLLIN|POLLRDNORM|POLLHUP):0;+}++staticssize_tclonefd_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos)+{+structtask_struct*p=file->private_data;+intret=0;++/* EOF after first read */+if(*ppos)+return0;++if(file->f_flags&O_NONBLOCK)+ret=-EAGAIN;+else+ret=wait_event_interruptible(p->clonefd_wqh,p->exit_state);++if(p->exit_state){+structclonefd_infoinfo={};+cputime_tutime,stime;+task_exit_code_status(p->exit_code,&info.code,&info.status);+info.code&=~__SI_MASK;+task_cputime(p,&utime,&stime);+info.utime=cputime_to_clock_t(utime+p->signal->utime);+info.stime=cputime_to_clock_t(stime+p->signal->stime);+ret=simple_read_from_buffer(buf,count,ppos,&info,sizeof(info));+}+returnret;+}++staticstructfile_operationsclonefd_fops={+.release=clonefd_release,+.poll=clonefd_poll,+.read=clonefd_read,+.llseek=no_llseek,+};
It might be nice to include a show_fdinfo() implementation that shows
(say) the pid that the clonefd refers to. E.g. something like:
static void clonefd_show_fdinfo(struct seq_file *m, struct file *file)
{
struct task_struct *p = file->private_data;
seq_printf(m, "tid:\t%d\n", task_tgid_vnr(p));
}
+
+/* Do process exit notification for clonefd. */
+void clonefd_do_notify(struct task_struct *p)
+{
+ if (p->clonefd)
+ wake_up_all(&p->clonefd_wqh);
+}
+
+/* Handle the CLONE_FD case for copy_process. */
+int clonefd_do_clone(u64 clone_flags, struct task_struct *p,
+ struct clone4_args *args, struct clonefd_setup *setup)
+{
+ int flags;
+ struct file *file;
+ int fd;
+
+ p->clonefd = !!(clone_flags & CLONE_FD);
+ if (!p->clonefd)
+ return 0;
+
+ if (args->clonefd_flags & ~(O_CLOEXEC | O_NONBLOCK))
+ return -EINVAL;
+
Maybe also check for (args->clonefd == NULL) in advance, and
return -EINVAL or -EFAULT?
@@ -1374,6 +1374,11 @@ struct task_struct {unsignedautoreap:1;/* Do not become a zombie on exit */+#ifdef CONFIG_CLONEFD+unsignedclonefd:1;/* Notify clonefd_wqh on exit */+wait_queue_head_tclonefd_wqh;+#endif+unsignedlongatomic_flags;/* Flags needing atomic access. */structrestart_blockrestart_block;
Idle thought: are there any concerns about the occupancy
impact of adding a wait_queue_head to every task_struct,
whether it has a clonefd or not?
I guess we could reduce the size somewhat by just
storing a struct file *clonefd_file in the task, and then have
a separate structure (with the wqh and a task_struct*) referenced
by file->private_data. Not sure whether the added complication
would be worthwhile, though.
My original patches did exactly that (minus the reference back to the
task_struct). However, there are a couple of problems with that
approach. First, it assumes that a task_struct has only a single file
referencing it, but in the future I'd like to support obtaining a
clonefd for an existing task. Second, the task_struct really shouldn't
have a reference to the actual struct file, when it only needs the
wait_queue_head_t.
Also, AFAICT a wait_queue_head_t is normally (in the absence of kernel
lock debugging options) the size of two pointers. Adding an indirection
and an extra allocation to change that to the size of one pointer seems
iffy, especially when looking at the rest of what's directly in
task_struct that's far larger.
quoted
--- /dev/null+++ b/kernel/clonefd.c
@@ -0,0 +1,121 @@+/*+*SupportfunctionsforCLONE_FD+*+*Copyright(c)2015IntelCorporation+*Originalauthors:JoshTriplett<josh@joshtriplett.org>+*ThiagoMacieira<thiago@macieira.org>+*/+#include<linux/anon_inodes.h>+#include<linux/file.h>+#include<linux/fs.h>+#include<linux/poll.h>+#include<linux/slab.h>+#include"clonefd.h"++staticintclonefd_release(structinode*inode,structfile*file)+{+put_task_struct(file->private_data);+return0;+}++staticunsignedintclonefd_poll(structfile*file,poll_table*wait)+{+structtask_struct*p=file->private_data;+poll_wait(file,&p->clonefd_wqh,wait);+returnp->exit_state?(POLLIN|POLLRDNORM|POLLHUP):0;+}++staticssize_tclonefd_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos)+{+structtask_struct*p=file->private_data;+intret=0;++/* EOF after first read */+if(*ppos)+return0;++if(file->f_flags&O_NONBLOCK)+ret=-EAGAIN;+else+ret=wait_event_interruptible(p->clonefd_wqh,p->exit_state);++if(p->exit_state){+structclonefd_infoinfo={};+cputime_tutime,stime;+task_exit_code_status(p->exit_code,&info.code,&info.status);+info.code&=~__SI_MASK;+task_cputime(p,&utime,&stime);+info.utime=cputime_to_clock_t(utime+p->signal->utime);+info.stime=cputime_to_clock_t(stime+p->signal->stime);+ret=simple_read_from_buffer(buf,count,ppos,&info,sizeof(info));+}+returnret;+}++staticstructfile_operationsclonefd_fops={+.release=clonefd_release,+.poll=clonefd_poll,+.read=clonefd_read,+.llseek=no_llseek,+};
It might be nice to include a show_fdinfo() implementation that shows
(say) the pid that the clonefd refers to. E.g. something like:
static void clonefd_show_fdinfo(struct seq_file *m, struct file *file)
{
struct task_struct *p = file->private_data;
seq_printf(m, "tid:\t%d\n", task_tgid_vnr(p));
}
I thought about that, but that would add a couple of additional ifdefs
(CONFIG_PROC_FS), for an informational file of minimal value. More
importantly, I don't want to add that until after adding an ioctl or
similar to programmatically obtain the pid from a clonefd; otherwise,
someone might try to use fdinfo as the "API" to do so, which would be
all kinds of awful.
So I'd prefer to add fdinfo in a future extension of clonefd, rather
than in the initial patch series.
quoted
+
+/* Do process exit notification for clonefd. */
+void clonefd_do_notify(struct task_struct *p)
+{
+ if (p->clonefd)
+ wake_up_all(&p->clonefd_wqh);
+}
+
+/* Handle the CLONE_FD case for copy_process. */
+int clonefd_do_clone(u64 clone_flags, struct task_struct *p,
+ struct clone4_args *args, struct clonefd_setup *setup)
+{
+ int flags;
+ struct file *file;
+ int fd;
+
+ p->clonefd = !!(clone_flags & CLONE_FD);
+ if (!p->clonefd)
+ return 0;
+
+ if (args->clonefd_flags & ~(O_CLOEXEC | O_NONBLOCK))
+ return -EINVAL;
+
Maybe also check for (args->clonefd == NULL) in advance, and
return -EINVAL or -EFAULT?
That wouldn't be consistent with how clone treats its various other
out argument pointers.
- Josh Triplett
From: Jonathan Corbet <corbet@lwn.net> Date: 2015-03-31 20:08:07
So I finally got around to having a look at this, and one thing caught my
eye:
read(2) (and similar)
When the new process exits, reading from the file
descriptor produces a single clonefd_info structure:
struct clonefd_info {
uint32_t code; /* Signal code */
uint32_t status; /* Exit status or signal */
uint64_t utime; /* User CPU time */
uint64_t stime; /* System CPU time */
};
This would appear to assume that a clonefd_info structure is the only
thing that will ever be read from this descriptor. It seems to me that
there is the potential for, someday, wanting to be able to read and write
other things as well. Should this structure be marked with type and
length fields so that other structures could be added in the future?
(I suppose we could just use ioctl() for any other functionality in the
future, though...:)
jon
On Tue, Mar 31, 2015 at 10:08:07PM +0200, Jonathan Corbet wrote:
So I finally got around to having a look at this, and one thing caught my
eye:
quoted
read(2) (and similar)
When the new process exits, reading from the file
descriptor produces a single clonefd_info structure:
struct clonefd_info {
uint32_t code; /* Signal code */
uint32_t status; /* Exit status or signal */
uint64_t utime; /* User CPU time */
uint64_t stime; /* System CPU time */
};
This would appear to assume that a clonefd_info structure is the only
thing that will ever be read from this descriptor. It seems to me that
there is the potential for, someday, wanting to be able to read and write
other things as well. Should this structure be marked with type and
length fields so that other structures could be added in the future?
I don't think it makes sense for a caller to get an arbitrary structure
on read(), and have to figure out what they got and ignore something
they don't understand. Instead, I think it makes more sense for the
caller to say "Hey, here's a flag saying I understand the new thing, go
ahead and give me the new thing". So, for instance, if you want to
receive SIGSTOP/SIGCONT messages for child processes through this
descriptor, we could add a flag for that.
- Josh Triplett
From: Jonathan Corbet <corbet@lwn.net> Date: 2015-04-01 07:24:20
On Tue, 31 Mar 2015 15:02:24 -0700
josh@joshtriplett.org wrote:
quoted
This would appear to assume that a clonefd_info structure is the only
thing that will ever be read from this descriptor. It seems to me that
there is the potential for, someday, wanting to be able to read and write
other things as well. Should this structure be marked with type and
length fields so that other structures could be added in the future?
I don't think it makes sense for a caller to get an arbitrary structure
on read(), and have to figure out what they got and ignore something
they don't understand. Instead, I think it makes more sense for the
caller to say "Hey, here's a flag saying I understand the new thing, go
ahead and give me the new thing". So, for instance, if you want to
receive SIGSTOP/SIGCONT messages for child processes through this
descriptor, we could add a flag for that.
The flag is fine, but, once we have set that flag saying we want those
messages, how do we know which type of structure we've gotten? That's
the piece of the puzzle I'm missing, sorry if I'm being overly slow.
Thanks,
jon