mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-23 11:04:44 +08:00
01361b665a
Naresh reported that selftests: pidfd: pidfd_wait hangs on linux next kernel on
x86_64, i386 and arm64 Juno-r2
These devices are using NFS mounted rootfs.
I have tested pidfd testcases independently and all test PASS.
The Hang or exit from test run noticed when run by run_kselftest.sh
pidfd_wait.c:208:wait_nonblock:Expected sys_waitid(P_PIDFD, pidfd,
&info, WSTOPPED, NULL) (-1) == 0 (0)
wait_nonblock: Test terminated by assertion
metadata:
git branch: master
git repo: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
git commit: e64997027d5f171148687e58b78c8b3c869a6158
git describe: next-20200922
make_kernelversion: 5.9.0-rc6
kernel-config:
http://snapshots.linaro.org/openembedded/lkft/lkft/sumo/intel-core2-32/lkft/linux-next/865/config
The reason for this is a simple race in the selftests, that I overlooked and
which is more likely to hit when there's a lot of processes running on the
system. Basically the child process hasn't SIGSTOPed itself yet but the parent
is already calling waitid() on a O_NONBLOCK pidfd. Since it doesn't find a
WSTOPPED process it returns -EAGAIN correctly.
The fix for this is to move the line where we're removing the O_NONBLOCK
property from the fd before the waitid() WSTOPPED call so we hang until the
child becomes stopped.
Fixes: cd89597bbe
("tests: add waitid() tests for non-blocking pidfds")
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Link: https://lkft.validation.linaro.org/scheduler/job/1813223
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
226 lines
5.3 KiB
C
226 lines
5.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/types.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sched.h>
|
|
#include <string.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include "pidfd.h"
|
|
#include "../kselftest_harness.h"
|
|
|
|
#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)
|
|
{
|
|
return syscall(__NR_clone3, args, sizeof(struct clone_args));
|
|
}
|
|
|
|
static int sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
|
|
struct rusage *ru)
|
|
{
|
|
return syscall(__NR_waitid, which, pid, info, options, ru);
|
|
}
|
|
|
|
TEST(wait_simple)
|
|
{
|
|
int pidfd = -1, status = 0;
|
|
pid_t parent_tid = -1;
|
|
struct clone_args args = {
|
|
.parent_tid = ptr_to_u64(&parent_tid),
|
|
.pidfd = ptr_to_u64(&pidfd),
|
|
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
|
|
.exit_signal = SIGCHLD,
|
|
};
|
|
int ret;
|
|
pid_t pid;
|
|
siginfo_t info = {
|
|
.si_signo = 0,
|
|
};
|
|
|
|
pidfd = open("/proc/self", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
|
ASSERT_NE(pid, 0);
|
|
EXPECT_EQ(close(pidfd), 0);
|
|
pidfd = -1;
|
|
|
|
pidfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
|
ASSERT_GE(pidfd, 0);
|
|
|
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
|
ASSERT_NE(pid, 0);
|
|
EXPECT_EQ(close(pidfd), 0);
|
|
pidfd = -1;
|
|
|
|
pid = sys_clone3(&args);
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0)
|
|
exit(EXIT_SUCCESS);
|
|
|
|
pid = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
|
ASSERT_GE(pid, 0);
|
|
ASSERT_EQ(WIFEXITED(info.si_status), true);
|
|
ASSERT_EQ(WEXITSTATUS(info.si_status), 0);
|
|
EXPECT_EQ(close(pidfd), 0);
|
|
|
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
|
ASSERT_EQ(info.si_code, CLD_EXITED);
|
|
ASSERT_EQ(info.si_pid, parent_tid);
|
|
}
|
|
|
|
TEST(wait_states)
|
|
{
|
|
int pidfd = -1, status = 0;
|
|
pid_t parent_tid = -1;
|
|
struct clone_args args = {
|
|
.parent_tid = ptr_to_u64(&parent_tid),
|
|
.pidfd = ptr_to_u64(&pidfd),
|
|
.flags = CLONE_PIDFD | CLONE_PARENT_SETTID,
|
|
.exit_signal = SIGCHLD,
|
|
};
|
|
int ret;
|
|
pid_t pid;
|
|
siginfo_t info = {
|
|
.si_signo = 0,
|
|
};
|
|
|
|
pid = sys_clone3(&args);
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0) {
|
|
kill(getpid(), SIGSTOP);
|
|
kill(getpid(), SIGSTOP);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
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, WCONTINUED, NULL), 0);
|
|
ASSERT_EQ(info.si_signo, SIGCHLD);
|
|
ASSERT_EQ(info.si_code, CLD_CONTINUED);
|
|
ASSERT_EQ(info.si_pid, parent_tid);
|
|
|
|
ASSERT_EQ(sys_waitid(P_PIDFD, pidfd, &info, WUNTRACED, 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, SIGKILL, 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_KILLED);
|
|
ASSERT_EQ(info.si_pid, parent_tid);
|
|
|
|
EXPECT_EQ(close(pidfd), 0);
|
|
}
|
|
|
|
TEST(wait_nonblock)
|
|
{
|
|
int pidfd, status = 0;
|
|
unsigned int flags = 0;
|
|
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,
|
|
};
|
|
|
|
/*
|
|
* Callers need to see ECHILD with non-blocking pidfds when no child
|
|
* processes exists.
|
|
*/
|
|
pidfd = sys_pidfd_open(getpid(), PIDFD_NONBLOCK);
|
|
EXPECT_GE(pidfd, 0) {
|
|
/* pidfd_open() doesn't support PIDFD_NONBLOCK. */
|
|
ASSERT_EQ(errno, EINVAL);
|
|
SKIP(return, "Skipping PIDFD_NONBLOCK test");
|
|
}
|
|
|
|
ret = sys_waitid(P_PIDFD, pidfd, &info, WEXITED, NULL);
|
|
ASSERT_LT(ret, 0);
|
|
ASSERT_EQ(errno, ECHILD);
|
|
EXPECT_EQ(close(pidfd), 0);
|
|
|
|
pid = sys_clone3(&args);
|
|
ASSERT_GE(pid, 0);
|
|
|
|
if (pid == 0) {
|
|
kill(getpid(), SIGSTOP);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
pidfd = sys_pidfd_open(pid, PIDFD_NONBLOCK);
|
|
EXPECT_GE(pidfd, 0) {
|
|
/* pidfd_open() doesn't support PIDFD_NONBLOCK. */
|
|
ASSERT_EQ(errno, EINVAL);
|
|
SKIP(return, "Skipping PIDFD_NONBLOCK test");
|
|
}
|
|
|
|
flags = fcntl(pidfd, F_GETFL, 0);
|
|
ASSERT_GT(flags, 0);
|
|
ASSERT_GT((flags & O_NONBLOCK), 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);
|
|
}
|
|
|
|
TEST_HARNESS_MAIN
|