Re: [PATCH 1/1] tests: add pidfd poll tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jul 24, 2019 at 3:38 AM Christian Brauner <christian@xxxxxxxxxx> wrote:
>
> On Tue, Jul 23, 2019 at 10:39:07AM -0700, Suren Baghdasaryan wrote:
> > This adds testing for polling on pidfd of a process being killed. Test runs
> > 10000 iterations by default to stress test pidfd polling functionality.
> > It accepts an optional command-line parameter to override the number or
> > iterations to run.
> > Specifically, it tests for:
> > - pidfd_open on a child process succeeds
> > - pidfd_send_signal on a child process succeeds
> > - polling on pidfd succeeds and returns exactly one event
> > - returned event is POLLIN
> > - event is received within 3 secs of the process being killed
> >
> > 10000 iterations was chosen because of the race condition being tested
> > which is not consistently reproducible but usually is revealed after less
> > than 2000 iterations.
> > Reveals race fixed by commit b191d6491be6 ("pidfd: fix a poll race when setting exit_state")
>
> Thanks for upstreaming the stress-tester. A few comments below.
>

Thanks for the comments. All make sense. I'll send an update later
today or tomorrow.

> >
> > Signed-off-by: Suren Baghdasaryan <surenb@xxxxxxxxxx>
> > ---
> >  tools/testing/selftests/pidfd/.gitignore      |   1 +
> >  tools/testing/selftests/pidfd/Makefile        |   2 +-
> >  .../testing/selftests/pidfd/pidfd_poll_test.c | 137 ++++++++++++++++++
> >  3 files changed, 139 insertions(+), 1 deletion(-)
> >  create mode 100644 tools/testing/selftests/pidfd/pidfd_poll_test.c
> >
> > diff --git a/tools/testing/selftests/pidfd/.gitignore b/tools/testing/selftests/pidfd/.gitignore
> > index 16d84d117bc0..a67896347d34 100644
> > --- a/tools/testing/selftests/pidfd/.gitignore
> > +++ b/tools/testing/selftests/pidfd/.gitignore
> > @@ -1,2 +1,3 @@
> >  pidfd_open_test
> > +pidfd_poll_test
> >  pidfd_test
> > diff --git a/tools/testing/selftests/pidfd/Makefile b/tools/testing/selftests/pidfd/Makefile
> > index 720b2d884b3c..ed58b7108d18 100644
> > --- a/tools/testing/selftests/pidfd/Makefile
> > +++ b/tools/testing/selftests/pidfd/Makefile
> > @@ -1,7 +1,7 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  CFLAGS += -g -I../../../../usr/include/ -lpthread
> >
> > -TEST_GEN_PROGS := pidfd_test pidfd_open_test
> > +TEST_GEN_PROGS := pidfd_test pidfd_open_test pidfd_poll_test
> >
> >  include ../lib.mk
> >
> > diff --git a/tools/testing/selftests/pidfd/pidfd_poll_test.c b/tools/testing/selftests/pidfd/pidfd_poll_test.c
> > new file mode 100644
> > index 000000000000..f2934aa070ae
> > --- /dev/null
> > +++ b/tools/testing/selftests/pidfd/pidfd_poll_test.c
> > @@ -0,0 +1,137 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#define _GNU_SOURCE
> > +#include <errno.h>
> > +#include <linux/types.h>
> > +#include <linux/wait.h>
> > +#include <poll.h>
> > +#include <signal.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <syscall.h>
> > +#include <sys/wait.h>
> > +#include <unistd.h>
> > +
> > +#include "pidfd.h"
> > +#include "../kselftest.h"
> > +
> > +#define __NR_pidfd_send_signal 424
> > +#define __NR_pidfd_open 434
>
> That won't work on Alpha where this number is unfortunately different so
> I'm not sure which syscall you'd hit there (/me checks
> 424     common  tgkill                          sys_tgkill
> )
>
> The better option is to just place
>
> #ifndef __NR_pidfd_send_signal
> #define __NR_pidfd_send_signal -1
> #endif
>
> #ifndef __NR_pidfd_open
> #define __NR_pidfd_open -1
> #endif
>
> into the pidfd.h header.
>
> > +
> > +static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
> > +{
> > +     return syscall(__NR_pidfd_open, pid, flags);
> > +}
> > +
> > +static inline int sys_pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
> > +                                     unsigned int flags)
> > +{
> > +     return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
> > +}
>
> Those can go into pidfd.h too.
>
> > +
> > +static bool timeout;
> > +
> > +static void handle_alarm(int sig)
> > +{
> > +     timeout = true;
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > +     int ret = 1;
> > +     int pidfd = -1;
> > +     struct pollfd fds;
> > +     int iter, nevents;
> > +     int nr_iterations = 10000;
> > +
> > +     fds.events = POLLIN;
>
> if (argc > 2)
>         ksft_exit_fail_msg("Unexpected command line argument");
>
> > +     if (argc > 1) {
>
>         if (argc == 2)
>
> > +             nr_iterations = atoi(argv[1]);
> > +             if (!nr_iterations) {
>
> You should probably also check for negative values.
>
> > +                     ksft_test_result_fail("invalid input parameter %s\n",
> > +                             argv[1]);
> > +                     return ksft_exit_fail();
> > +             }
> > +     }
> > +
> > +     ksft_print_msg("running pidfd poll test for %d iterations\n",
> > +             nr_iterations);
> > +
> > +     for (iter = 0; iter < nr_iterations; iter++) {
> > +             int child_pid = fork();
> > +
> > +             if (!child_pid) {
> > +                     /* Child process just sleeps for a min */
> > +                     sleep(60);
> > +                     exit(0);
>
> Just for kicks, please use exit(EXIT_SUCCESS)
>
> > +             }
>
>
> You should switch the checkes, i.e.
>
>         if (child_pid < 0)
>                 /* do stuff */
>
>         if (child_pid == 0)
>                 /* do other stuff */
>
> > +
> > +             /* Parent kills the child and waits for its death */
> > +             if (child_pid < 0) {
> > +                     if (errno == EAGAIN) {
> > +                             iter--;
> > +                             continue;
> > +                     }
> > +                     ksft_print_msg("%s - failed to fork a child process\n",
> > +                             strerror(errno));
>
> Uhm, shouldn't you exit with an error here?, i.e.
>
>                 ksft_exit_fail_msg(...
>
> > +             }
> > +             pidfd = sys_pidfd_open(child_pid, 0);
> > +             if (pidfd < 0) {
> > +                     ksft_print_msg("%s - pidfd_open failed\n",
> > +                             strerror(errno));
> > +                     goto on_error;
>
> I think you honestly can just call ksft_exit_fail_msg() everywhere. The
> fds will be cleaned up on process exit anyway and it's pretty
> short-lived.
>
> > +             }
> > +             /* Setup 3 sec alarm - plenty of time */
> > +             if (signal(SIGALRM, handle_alarm) == SIG_ERR) {
> > +                     ksft_print_msg("%s - signal failed\n",
> > +                             strerror(errno));
> > +                     goto on_error;
> > +             }
> > +             alarm(3);
> > +             /* Send SIGKILL to the child */
> > +             if (sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0)) {
> > +                     ksft_print_msg("%s - pidfd_send_signal failed\n",
> > +                             strerror(errno));
> > +                     goto on_error;
> > +             }
> > +             /* Wait for the death notification */
> > +             fds.fd = pidfd;
> > +             nevents = poll(&fds, 1, -1);
> > +             if (nevents < 0) {
> > +                     ksft_print_msg("%s - poll failed\n",
> > +                             strerror(errno));
> > +                     goto on_error;
> > +             }
> > +             if (nevents != 1) {
> > +                     ksft_print_msg("unexpected poll result: %d\n",
> > +                             nevents);
> > +                     goto on_error;
> > +             }
> > +             if (!(fds.revents & POLLIN)) {
> > +                     ksft_print_msg(
> > +                             "unexpected event type received: 0x%x\n",
> > +                             fds.revents);
> > +                     goto on_error;
> > +             }
> > +             if (timeout) {
> > +                     ksft_print_msg("death notification wait timeout\n");
> > +                     goto on_error;
> > +             }
> > +             close(pidfd);
> > +     }
> > +     ret = 0;
> > +
> > +on_error:
> > +     if (pidfd)
> > +             close(pidfd);
> > +
> > +     if (ret) {
> > +             ksft_test_result_fail("failed after %d retries\n", iter);
> > +             return ksft_exit_fail();
> > +     }
> > +
> > +     ksft_test_result_pass("pidfd poll test: pass\n");
> > +     return ksft_exit_pass();
> > +}
> > --
> > 2.22.0.657.g960e92d24f-goog
> >



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux