History

exit: support non-blocking pidfds

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 implementation should be straightforward: - 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. A concrete use-case that was brought on-list was Josh's async pidfd library. 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.

Christian Brauner committed 6 years ago

Commit ba7d25f1 parent d012a711 file · +12 −3Browse files

Unified · Split

kernel/exit.c+12−3
@@ -1474,7 +1474,7 @@ end: · 1473 unmodified lines
1474 return retval;1474 return retval;
1475}1475}
1476 1476
1477static struct pid *pidfd_get_pid(unsigned int fd)1477static struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
1478{1478{
1479 struct fd f;1479 struct fd f;
1480 struct pid *pid;1480 struct pid *pid;
@@ -1484,8 +1484,10 @@ static struct pid *pidfd_get_pid(unsigned int fd) · 3 unmodified lines
1484 return ERR_PTR(-EBADF);1484 return ERR_PTR(-EBADF);
1485 1485
1486 pid = pidfd_pid(f.file);1486 pid = pidfd_pid(f.file);
1487 if (!IS_ERR(pid))1487 if (!IS_ERR(pid)) {
1488 get_pid(pid);1488 get_pid(pid);
1489 *flags = f.file->f_flags;
1490 }
1489 1491
1490 fdput(f);1492 fdput(f);
1491 return pid;1493 return pid;
@@ -1498,6 +1500,7 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, · 6 unmodified lines
1498 struct pid *pid = NULL;1500 struct pid *pid = NULL;
1499 enum pid_type type;1501 enum pid_type type;
1500 long ret;1502 long ret;
1503 unsigned int f_flags = 0;
1501 1504
1502 if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED|1505 if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED|
1503 __WNOTHREAD|__WCLONE|__WALL))1506 __WNOTHREAD|__WCLONE|__WALL))
@@ -1531,9 +1534,10 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, · 27 unmodified lines
1531 if (upid < 0)1534 if (upid < 0)
1532 return -EINVAL;1535 return -EINVAL;
1533 1536
1534 pid = pidfd_get_pid(upid);1537 pid = pidfd_get_pid(upid, &f_flags);
1535 if (IS_ERR(pid))1538 if (IS_ERR(pid))
1536 return PTR_ERR(pid);1539 return PTR_ERR(pid);
1540
1537 break;1541 break;
1538 default:1542 default:
1539 return -EINVAL;1543 return -EINVAL;
@@ -1544,7 +1548,12 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, · 4 unmodified lines
1544 wo.wo_flags = options;1548 wo.wo_flags = options;
1545 wo.wo_info = infop;1549 wo.wo_info = infop;
1546 wo.wo_rusage = ru;1550 wo.wo_rusage = ru;
1551 if (f_flags & O_NONBLOCK)
1552 wo.wo_flags |= WNOHANG;
1553
1547 ret = do_wait(&wo);1554 ret = do_wait(&wo);
1555 if (!ret && !(options & WNOHANG) && (f_flags & O_NONBLOCK))
1556 ret = -EAGAIN;
1548 1557
1549 put_pid(pid);1558 put_pid(pid);
1550 return ret;1559 return ret;