diff options
author | Linus Torvalds | 2020-10-14 14:39:20 -0700 |
---|---|---|
committer | Linus Torvalds | 2020-10-14 14:39:20 -0700 |
commit | 4da9af0014b51c8b015ed8c622440ef28912efe6 (patch) | |
tree | ceef59250d98685f0a3ac91af5e4e5d9cb27a470 /kernel/exit.c | |
parent | 612e7a4c1645f09449355cf08b6fd3de80b4f8cc (diff) | |
parent | 01361b665a26ef0c087e53f14cf310d1cfe0cf98 (diff) |
Merge tag 'threads-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux
Pull pidfd updates from Christian Brauner:
"This introduces a new extension to the pidfd_open() syscall. Users can
now raise the new PIDFD_NONBLOCK flag to support non-blocking pidfd
file descriptors. This has been requested for uses in async process
management libraries such as async-pidfd in Rust.
Ever since the introduction of pidfds and more advanced async io
various programming languages such as Rust have grown support for
async event libraries. These libraries are created to help build
epoll-based event loops around file descriptors. A common pattern is
to automatically make all file descriptors they manage to O_NONBLOCK.
For such libraries the EAGAIN error code is treated specially. When a
function is called that returns EAGAIN the function isn't called again
until the event loop indicates the the file descriptor is ready.
Supporting EAGAIN when waiting on pidfds makes such libraries just
work with little effort.
This introduces a new flag PIDFD_NONBLOCK that is equivalent to
O_NONBLOCK. This follows the same patterns we have for other (anon
inode) file descriptors such as EFD_NONBLOCK, IN_NONBLOCK,
SFD_NONBLOCK, TFD_NONBLOCK and the same for close-on-exec flags.
Passing a non-blocking pidfd to waitid() currently has no effect, i.e.
is not supported. There are users which would like to use waitid() on
pidfds that are O_NONBLOCK and mix it with pidfds that are blocking
and both pass them to waitid().
The expected behavior is to have waitid() return -EAGAIN for
non-blocking pidfds and to block for blocking pidfds without needing
to perform any additional checks for flags set on the pidfd before
passing it to waitid(). Non-blocking pidfds will return EAGAIN from
waitid() when no child process is ready yet. Returning -EAGAIN for
non-blocking pidfds makes it easier for event loops that handle EAGAIN
specially.
It also makes the API more consistent and uniform. In essence,
waitid() is treated like a read on a non-blocking pidfd or a recvmsg()
on a non-blocking socket.
With the addition of support for non-blocking pidfds we support the
same functionality that sockets do. For sockets() recvmsg() supports
MSG_DONTWAIT for pidfds waitid() supports WNOHANG. Both flags are
per-call options. In contrast non-blocking pidfds and non-blocking
sockets are a setting on an open file description affecting all
threads in the calling process as well as other processes that hold
file descriptors referring to the same open file description. Both
behaviors, per call and per open file description, have genuine
use-cases.
The interaction with the WNOHANG flag is documented as follows:
- If a non-blocking pidfd is passed and WNOHANG is not raised we
simply raise the WNOHANG flag internally. When do_wait() returns
indicating that there are eligible child processes but none have
exited yet we set EAGAIN. If no child process exists we continue
returning ECHILD.
- If a non-blocking pidfd is passed and WNOHANG is raised waitid()
will continue returning 0, i.e. it will not set EAGAIN. This ensure
backwards compatibility with applications passing WNOHANG
explicitly with pidfds"
* tag 'threads-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux:
tests: remove O_NONBLOCK before waiting for WSTOPPED
tests: add waitid() tests for non-blocking pidfds
tests: port pidfd_wait to kselftest harness
pidfd: support PIDFD_NONBLOCK in pidfd_open()
exit: support non-blocking pidfds
Diffstat (limited to 'kernel/exit.c')
-rw-r--r-- | kernel/exit.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 733e80f334e7..1f51c27bae59 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1474,7 +1474,7 @@ end: return retval; } -static struct pid *pidfd_get_pid(unsigned int fd) +static struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags) { struct fd f; struct pid *pid; @@ -1484,8 +1484,10 @@ static struct pid *pidfd_get_pid(unsigned int fd) return ERR_PTR(-EBADF); pid = pidfd_pid(f.file); - if (!IS_ERR(pid)) + if (!IS_ERR(pid)) { get_pid(pid); + *flags = f.file->f_flags; + } fdput(f); return pid; @@ -1498,6 +1500,7 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, struct pid *pid = NULL; enum pid_type type; long ret; + unsigned int f_flags = 0; if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED| __WNOTHREAD|__WCLONE|__WALL)) @@ -1531,9 +1534,10 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, if (upid < 0) return -EINVAL; - pid = pidfd_get_pid(upid); + pid = pidfd_get_pid(upid, &f_flags); if (IS_ERR(pid)) return PTR_ERR(pid); + break; default: return -EINVAL; @@ -1544,7 +1548,12 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, wo.wo_flags = options; wo.wo_info = infop; wo.wo_rusage = ru; + if (f_flags & O_NONBLOCK) + wo.wo_flags |= WNOHANG; + ret = do_wait(&wo); + if (!ret && !(options & WNOHANG) && (f_flags & O_NONBLOCK)) + ret = -EAGAIN; put_pid(pid); return ret; |