threads-v5.10
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCX4a4sAAKCRCRxhvAZXjc ohdRAP9fclgrRkTl3o4cgaK0PUMt8BZ5QCg/SPQrVT58AQlfSwEAsNtWAeo6U2z1 FLGuCoPBEW1Zghkj1lMbIhj5zyVaEQ8= =Z7Q0 -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
4da9af0014
|
@ -0,0 +1,12 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
|
|
||||||
|
#ifndef _UAPI_LINUX_PIDFD_H
|
||||||
|
#define _UAPI_LINUX_PIDFD_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/fcntl.h>
|
||||||
|
|
||||||
|
/* Flags for pidfd_open(). */
|
||||||
|
#define PIDFD_NONBLOCK O_NONBLOCK
|
||||||
|
|
||||||
|
#endif /* _UAPI_LINUX_PIDFD_H */
|
|
@ -1474,7 +1474,7 @@ end:
|
||||||
return retval;
|
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 fd f;
|
||||||
struct pid *pid;
|
struct pid *pid;
|
||||||
|
@ -1484,8 +1484,10 @@ static struct pid *pidfd_get_pid(unsigned int fd)
|
||||||
return ERR_PTR(-EBADF);
|
return ERR_PTR(-EBADF);
|
||||||
|
|
||||||
pid = pidfd_pid(f.file);
|
pid = pidfd_pid(f.file);
|
||||||
if (!IS_ERR(pid))
|
if (!IS_ERR(pid)) {
|
||||||
get_pid(pid);
|
get_pid(pid);
|
||||||
|
*flags = f.file->f_flags;
|
||||||
|
}
|
||||||
|
|
||||||
fdput(f);
|
fdput(f);
|
||||||
return pid;
|
return pid;
|
||||||
|
@ -1498,6 +1500,7 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||||
struct pid *pid = NULL;
|
struct pid *pid = NULL;
|
||||||
enum pid_type type;
|
enum pid_type type;
|
||||||
long ret;
|
long ret;
|
||||||
|
unsigned int f_flags = 0;
|
||||||
|
|
||||||
if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED|
|
if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED|
|
||||||
__WNOTHREAD|__WCLONE|__WALL))
|
__WNOTHREAD|__WCLONE|__WALL))
|
||||||
|
@ -1531,9 +1534,10 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||||
if (upid < 0)
|
if (upid < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pid = pidfd_get_pid(upid);
|
pid = pidfd_get_pid(upid, &f_flags);
|
||||||
if (IS_ERR(pid))
|
if (IS_ERR(pid))
|
||||||
return PTR_ERR(pid);
|
return PTR_ERR(pid);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
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_flags = options;
|
||||||
wo.wo_info = infop;
|
wo.wo_info = infop;
|
||||||
wo.wo_rusage = ru;
|
wo.wo_rusage = ru;
|
||||||
|
if (f_flags & O_NONBLOCK)
|
||||||
|
wo.wo_flags |= WNOHANG;
|
||||||
|
|
||||||
ret = do_wait(&wo);
|
ret = do_wait(&wo);
|
||||||
|
if (!ret && !(options & WNOHANG) && (f_flags & O_NONBLOCK))
|
||||||
|
ret = -EAGAIN;
|
||||||
|
|
||||||
put_pid(pid);
|
put_pid(pid);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
12
kernel/pid.c
12
kernel/pid.c
|
@ -43,6 +43,7 @@
|
||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <net/sock.h>
|
#include <net/sock.h>
|
||||||
|
#include <uapi/linux/pidfd.h>
|
||||||
|
|
||||||
struct pid init_struct_pid = {
|
struct pid init_struct_pid = {
|
||||||
.count = REFCOUNT_INIT(1),
|
.count = REFCOUNT_INIT(1),
|
||||||
|
@ -522,7 +523,8 @@ struct pid *find_ge_pid(int nr, struct pid_namespace *ns)
|
||||||
/**
|
/**
|
||||||
* pidfd_create() - Create a new pid file descriptor.
|
* pidfd_create() - Create a new pid file descriptor.
|
||||||
*
|
*
|
||||||
* @pid: struct pid that the pidfd will reference
|
* @pid: struct pid that the pidfd will reference
|
||||||
|
* @flags: flags to pass
|
||||||
*
|
*
|
||||||
* This creates a new pid file descriptor with the O_CLOEXEC flag set.
|
* This creates a new pid file descriptor with the O_CLOEXEC flag set.
|
||||||
*
|
*
|
||||||
|
@ -532,12 +534,12 @@ struct pid *find_ge_pid(int nr, struct pid_namespace *ns)
|
||||||
* Return: On success, a cloexec pidfd is returned.
|
* Return: On success, a cloexec pidfd is returned.
|
||||||
* On error, a negative errno number will be returned.
|
* On error, a negative errno number will be returned.
|
||||||
*/
|
*/
|
||||||
static int pidfd_create(struct pid *pid)
|
static int pidfd_create(struct pid *pid, unsigned int flags)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = anon_inode_getfd("[pidfd]", &pidfd_fops, get_pid(pid),
|
fd = anon_inode_getfd("[pidfd]", &pidfd_fops, get_pid(pid),
|
||||||
O_RDWR | O_CLOEXEC);
|
flags | O_RDWR | O_CLOEXEC);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
put_pid(pid);
|
put_pid(pid);
|
||||||
|
|
||||||
|
@ -565,7 +567,7 @@ SYSCALL_DEFINE2(pidfd_open, pid_t, pid, unsigned int, flags)
|
||||||
int fd;
|
int fd;
|
||||||
struct pid *p;
|
struct pid *p;
|
||||||
|
|
||||||
if (flags)
|
if (flags & ~PIDFD_NONBLOCK)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (pid <= 0)
|
if (pid <= 0)
|
||||||
|
@ -576,7 +578,7 @@ SYSCALL_DEFINE2(pidfd_open, pid_t, pid, unsigned int, flags)
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
if (pid_has_task(p, PIDTYPE_TGID))
|
if (pid_has_task(p, PIDTYPE_TGID))
|
||||||
fd = pidfd_create(p);
|
fd = pidfd_create(p, flags);
|
||||||
else
|
else
|
||||||
fd = -EINVAL;
|
fd = -EINVAL;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@
|
||||||
#define __NR_pidfd_getfd -1
|
#define __NR_pidfd_getfd -1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef PIDFD_NONBLOCK
|
||||||
|
#define PIDFD_NONBLOCK O_NONBLOCK
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
|
* The kernel reserves 300 pids via RESERVED_PIDS in kernel/pid.c
|
||||||
* That means, when it wraps around any pid < 300 will be skipped.
|
* That means, when it wraps around any pid < 300 will be skipped.
|
||||||
|
|
|
@ -17,10 +17,15 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "pidfd.h"
|
#include "pidfd.h"
|
||||||
#include "../kselftest.h"
|
#include "../kselftest_harness.h"
|
||||||
|
|
||||||
#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
|
#define ptr_to_u64(ptr) ((__u64)((uintptr_t)(ptr)))
|
||||||
|
|
||||||
|
/* Attempt to de-conflict with the selftests tree. */
|
||||||
|
#ifndef SKIP
|
||||||
|
#define SKIP(s, ...) XFAIL(s, ##__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
static pid_t sys_clone3(struct clone_args *args)
|
static pid_t sys_clone3(struct clone_args *args)
|
||||||
{
|
{
|
||||||
return syscall(__NR_clone3, args, sizeof(struct clone_args));
|
return syscall(__NR_clone3, args, sizeof(struct clone_args));
|
||||||
|
@ -32,9 +37,8 @@ static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
|
||||||
return syscall(__NR_waitid, which, pid, info, options, ru);
|
return syscall(__NR_waitid, which, pid, info, options, ru);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_pidfd_wait_simple(void)
|
TEST(wait_simple)
|
||||||
{
|
{
|
||||||
const char *test_name = "pidfd wait simple";
|
|
||||||
int pidfd = -1, status = 0;
|
int pidfd = -1, status = 0;
|
||||||
pid_t parent_tid = -1;
|
pid_t parent_tid = -1;
|
||||||
struct clone_args args = {
|
struct clone_args args = {
|
||||||
|
@ -50,76 +54,40 @@ static int test_pidfd_wait_simple(void)
|
||||||
};
|
};
|
||||||
|
|
||||||
pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
|
pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
|
||||||
if (pidfd < 0)
|
ASSERT_GE(pidfd, 0);
|
||||||
ksft_exit_fail_msg("%s test: failed to open /proc/self %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
|
|
||||||
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
||||||
if (pid == 0)
|
ASSERT_NE(pid, 0);
|
||||||
ksft_exit_fail_msg(
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
"%s test: succeeded to wait on invalid pidfd %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
close(pidfd);
|
|
||||||
pidfd = -1;
|
pidfd = -1;
|
||||||
|
|
||||||
pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||||
if (pidfd == 0)
|
ASSERT_GE(pidfd, 0);
|
||||||
ksft_exit_fail_msg("%s test: failed to open /dev/null %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
|
|
||||||
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
||||||
if (pid == 0)
|
ASSERT_NE(pid, 0);
|
||||||
ksft_exit_fail_msg(
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
"%s test: succeeded to wait on invalid pidfd %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
close(pidfd);
|
|
||||||
pidfd = -1;
|
pidfd = -1;
|
||||||
|
|
||||||
pid = sys_clone3(&args);
|
pid = sys_clone3(&args);
|
||||||
if (pid < 0)
|
ASSERT_GE(pid, 0);
|
||||||
ksft_exit_fail_msg("%s test: failed to create new process %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
|
|
||||||
if (pid == 0)
|
if (pid == 0)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
||||||
if (pid < 0)
|
ASSERT_GE(pid, 0);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(WIFEXITED(info.si_status), true);
|
||||||
"%s test: failed to wait on process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(WEXITSTATUS(info.si_status), 0);
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
|
|
||||||
if (!WIFEXITED(info.si_status) || WEXITSTATUS(info.si_status))
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(info.si_code, CLD_EXITED);
|
||||||
"%s test: unexpected status received after waiting on process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
close(pidfd);
|
|
||||||
|
|
||||||
if (info.si_signo != SIGCHLD)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_signo, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_code != CLD_EXITED)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_code, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_pid != parent_tid)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_pid, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
ksft_test_result_pass("%s test: Passed\n", test_name);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int test_pidfd_wait_states(void)
|
TEST(wait_states)
|
||||||
{
|
{
|
||||||
const char *test_name = "pidfd wait states";
|
|
||||||
int pidfd = -1, status = 0;
|
int pidfd = -1, status = 0;
|
||||||
pid_t parent_tid = -1;
|
pid_t parent_tid = -1;
|
||||||
struct clone_args args = {
|
struct clone_args args = {
|
||||||
|
@ -135,9 +103,7 @@ static int test_pidfd_wait_states(void)
|
||||||
};
|
};
|
||||||
|
|
||||||
pid = sys_clone3(&args);
|
pid = sys_clone3(&args);
|
||||||
if (pid < 0)
|
ASSERT_GE(pid, 0);
|
||||||
ksft_exit_fail_msg("%s test: failed to create new process %s\n",
|
|
||||||
test_name, strerror(errno));
|
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
kill(getpid(), SIGSTOP);
|
kill(getpid(), SIGSTOP);
|
||||||
|
@ -145,127 +111,115 @@ static int test_pidfd_wait_states(void)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL);
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);
|
||||||
if (ret < 0)
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(info.si_code, CLD_STOPPED);
|
||||||
"%s test: failed to wait on WSTOPPED process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_signo != SIGCHLD)
|
ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_signo, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_code != CLD_STOPPED)
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL), 0);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(info.si_code, CLD_CONTINUED);
|
||||||
test_name, info.si_code, parent_tid, pidfd,
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_pid != parent_tid)
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL), 0);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(info.si_code, CLD_STOPPED);
|
||||||
test_name, info.si_pid, parent_tid, pidfd,
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
ret = sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
|
ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0), 0);
|
||||||
if (ret < 0)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: failed to send signal to process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
ret = sys_waitid(P_PIDFD, pidfd, &info, WCONTINUED, NULL);
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);
|
||||||
if (ret < 0)
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(info.si_code, CLD_KILLED);
|
||||||
"%s test: failed to wait WCONTINUED on process with pid %d and pidfd %d: %s\n",
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_signo != SIGCHLD)
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
ksft_exit_fail_msg(
|
}
|
||||||
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_signo, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_code != CLD_CONTINUED)
|
TEST(wait_nonblock)
|
||||||
ksft_exit_fail_msg(
|
{
|
||||||
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
int pidfd, status = 0;
|
||||||
test_name, info.si_code, parent_tid, pidfd,
|
unsigned int flags = 0;
|
||||||
strerror(errno));
|
pid_t parent_tid = -1;
|
||||||
|
struct clone_args args = {
|
||||||
|
.parent_tid = ptr_to_u64(&parent_tid),
|
||||||
|
.flags = CLONE_PARENT_SETTID,
|
||||||
|
.exit_signal = SIGCHLD,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
pid_t pid;
|
||||||
|
siginfo_t info = {
|
||||||
|
.si_signo = 0,
|
||||||
|
};
|
||||||
|
|
||||||
if (info.si_pid != parent_tid)
|
/*
|
||||||
ksft_exit_fail_msg(
|
* Callers need to see ECHILD with non-blocking pidfds when no child
|
||||||
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
* processes exists.
|
||||||
test_name, info.si_pid, parent_tid, pidfd,
|
*/
|
||||||
strerror(errno));
|
pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK);
|
||||||
|
EXPECT_GE(pidfd, 0) {
|
||||||
ret = sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, NULL);
|
/* pidfd_open() doesn't support PIDFD_NONBLOCK. */
|
||||||
if (ret < 0)
|
ASSERT_EQ(errno, EINVAL);
|
||||||
ksft_exit_fail_msg(
|
SKIP(return, "Skipping PIDFD_NONBLOCK test");
|
||||||
"%s test: failed to wait on WUNTRACED process with pid %d and pidfd %d: %s\n",
|
}
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_signo != SIGCHLD)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_signo, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_code != CLD_STOPPED)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_code, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_pid != parent_tid)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_pid, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
ret = sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
|
|
||||||
if (ret < 0)
|
|
||||||
ksft_exit_fail_msg(
|
|
||||||
"%s test: failed to send SIGKILL to process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
||||||
if (ret < 0)
|
ASSERT_LT(ret, 0);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_EQ(errno, ECHILD);
|
||||||
"%s test: failed to wait on WEXITED process with pid %d and pidfd %d: %s\n",
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
test_name, parent_tid, pidfd, strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_signo != SIGCHLD)
|
pid = sys_clone3(&args);
|
||||||
ksft_exit_fail_msg(
|
ASSERT_GE(pid, 0);
|
||||||
"%s test: unexpected si_signo value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
|
||||||
test_name, info.si_signo, parent_tid, pidfd,
|
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_code != CLD_KILLED)
|
if (pid == 0) {
|
||||||
ksft_exit_fail_msg(
|
kill(getpid(), SIGSTOP);
|
||||||
"%s test: unexpected si_code value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
exit(EXIT_SUCCESS);
|
||||||
test_name, info.si_code, parent_tid, pidfd,
|
}
|
||||||
strerror(errno));
|
|
||||||
|
|
||||||
if (info.si_pid != parent_tid)
|
pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK);
|
||||||
ksft_exit_fail_msg(
|
EXPECT_GE(pidfd, 0) {
|
||||||
"%s test: unexpected si_pid value %d received after waiting on process with pid %d and pidfd %d: %s\n",
|
/* pidfd_open() doesn't support PIDFD_NONBLOCK. */
|
||||||
test_name, info.si_pid, parent_tid, pidfd,
|
ASSERT_EQ(errno, EINVAL);
|
||||||
strerror(errno));
|
SKIP(return, "Skipping PIDFD_NONBLOCK test");
|
||||||
|
}
|
||||||
|
|
||||||
close(pidfd);
|
flags = fcntl(pidfd, F_GETFL, 0);
|
||||||
|
ASSERT_GT(flags, 0);
|
||||||
|
ASSERT_GT((flags & O_NONBLOCK), 0);
|
||||||
|
|
||||||
ksft_test_result_pass("%s test: Passed\n", test_name);
|
/*
|
||||||
return 0;
|
* Callers need to see EAGAIN/EWOULDBLOCK with non-blocking pidfd when
|
||||||
|
* child processes exist but none have exited.
|
||||||
|
*/
|
||||||
|
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
||||||
|
ASSERT_LT(ret, 0);
|
||||||
|
ASSERT_EQ(errno, EAGAIN);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callers need to continue seeing 0 with non-blocking pidfd and
|
||||||
|
* WNOHANG raised explicitly when child processes exist but none have
|
||||||
|
* exited.
|
||||||
|
*/
|
||||||
|
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED | WNOHANG, NULL);
|
||||||
|
ASSERT_EQ(ret, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(fcntl(pidfd, F_SETFL, (flags & ~O_NONBLOCK)), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WSTOPPED, NULL), 0);
|
||||||
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
|
ASSERT_EQ(info.si_code, CLD_STOPPED);
|
||||||
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_pidfd_send_signal(pidfd, SIGCONT, NULL, 0), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL), 0);
|
||||||
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
||||||
|
ASSERT_EQ(info.si_code, CLD_EXITED);
|
||||||
|
ASSERT_EQ(info.si_pid, parent_tid);
|
||||||
|
|
||||||
|
EXPECT_EQ(close(pidfd), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
TEST_HARNESS_MAIN
|
||||||
{
|
|
||||||
ksft_print_header();
|
|
||||||
ksft_set_plan(2);
|
|
||||||
|
|
||||||
test_pidfd_wait_simple();
|
|
||||||
test_pidfd_wait_states();
|
|
||||||
|
|
||||||
return ksft_exit_pass();
|
|
||||||
}
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче