Re: [PATCH 2/2] perf test: Test FASYNC with watermark wakeups.

From: Kyle Huey
Date: Thu Feb 22 2024 - 12:55:57 EST


On Wed, Feb 21, 2024 at 10:36 AM Ian Rogers <irogers@xxxxxxxxxx> wrote:
>
> On Wed, Feb 21, 2024 at 9:52 AM Kyle Huey <me@xxxxxxxxxxxx> wrote:
> >
> > The test uses PERF_RECORD_SWITCH records to fill the ring buffer and
> > trigger the watermark wakeup, which in turn should trigger an IO
> > signal.
> >
> > Signed-off-by: Kyle Huey <khuey@xxxxxxxxxxxx>
> > ---
> > tools/perf/tests/Build | 1 +
> > tools/perf/tests/builtin-test.c | 1 +
> > tools/perf/tests/tests.h | 1 +
> > tools/perf/tests/watermark_signal.c | 123 ++++++++++++++++++++++++++++
> > 4 files changed, 126 insertions(+)
> > create mode 100644 tools/perf/tests/watermark_signal.c
> >
> > diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
> > index 53ba9c3e20e0..de43eb60b280 100644
> > --- a/tools/perf/tests/Build
> > +++ b/tools/perf/tests/Build
> > @@ -67,6 +67,7 @@ perf-y += sigtrap.o
> > perf-y += event_groups.o
> > perf-y += symbols.o
> > perf-y += util.o
> > +perf-y += watermark_signal.o
> >
> > ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc))
> > perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
> > diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> > index 4a5973f9bb9b..715c01a2172a 100644
> > --- a/tools/perf/tests/builtin-test.c
> > +++ b/tools/perf/tests/builtin-test.c
> > @@ -124,6 +124,7 @@ static struct test_suite *generic_tests[] = {
> > &suite__event_groups,
> > &suite__symbols,
> > &suite__util,
> > + &suite__watermark_signal,
> > NULL,
> > };
> >
> > diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> > index dad3d7414142..7ef4e0d0a77b 100644
> > --- a/tools/perf/tests/tests.h
> > +++ b/tools/perf/tests/tests.h
> > @@ -146,6 +146,7 @@ DECLARE_SUITE(sigtrap);
> > DECLARE_SUITE(event_groups);
> > DECLARE_SUITE(symbols);
> > DECLARE_SUITE(util);
> > +DECLARE_SUITE(watermark_signal);
> >
> > /*
> > * PowerPC and S390 do not support creation of instruction breakpoints using the
> > diff --git a/tools/perf/tests/watermark_signal.c b/tools/perf/tests/watermark_signal.c
> > new file mode 100644
> > index 000000000000..ae4abedc4b7c
> > --- /dev/null
> > +++ b/tools/perf/tests/watermark_signal.c
> > @@ -0,0 +1,123 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#include <stddef.h>
> > +#include <signal.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <sys/ioctl.h>
> > +#include <sys/mman.h>
> > +#include <sys/wait.h>
> > +#include <unistd.h>
> > +#include <errno.h>
> > +#include <fcntl.h>
> > +
> > +#include "tests.h"
> > +#include "debug.h"
> > +#include "event.h"
> > +#include "../perf-sys.h"
> > +#include "cloexec.h"
> > +#include <internal/lib.h> // page_size
> > +
> > +static int sigio_count;
> > +
> > +static void handle_sigio(int sig __always_unused)
> > +{
> > + ++sigio_count;
> > +}
> > +
> > +static void do_child(void)
> > +{
> > + for (int i = 0; i < 20; ++i)
> > + sleep(1);
> > +
> > + exit(0);
> > +}
> > +
> > +static int test__watermark_signal(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
> > +{
> > + struct perf_event_attr attr;
> > + struct perf_event_mmap_page *p = NULL;
> > + sighandler_t previous_sigio = SIG_ERR;
> > + pid_t child = -1;
> > + int fd = -1;
> > + int ret = TEST_FAIL;
> > +
> > + previous_sigio = signal(SIGIO, handle_sigio);
> > + if (previous_sigio == SIG_ERR) {
> > + pr_debug("failed setting SIGIO handler\n");
> > + goto cleanup;
> > + }
> > +
> > + memset(&attr, 0, sizeof(attr));
> > + attr.size = sizeof(attr);
> > + attr.type = PERF_TYPE_SOFTWARE;
> > + attr.config = PERF_COUNT_SW_DUMMY;
> > + attr.sample_period = 1;
> > + attr.disabled = 1;
> > + attr.watermark = 1;
> > + attr.context_switch = 1;
> > + attr.wakeup_watermark = 1;
> > +
> > + child = fork();
> > + if (child == 0)
> > + do_child();
> > + else if (child < 0) {
> > + pr_debug("failed fork() %d\n", errno);
> > + goto cleanup;
> > + }
> > +
> > + fd = sys_perf_event_open(&attr, child, -1, -1,
> > + perf_event_open_cloexec_flag());
> > + if (fd < 0) {
> > + pr_debug("failed opening event %llx\n", attr.config);
> > + goto cleanup;
> > + }
> > +
> > + if (fcntl(fd, F_SETFL, FASYNC)) {
> > + pr_debug("failed F_SETFL FASYNC %d\n", errno);
> > + goto cleanup;
> > + }
>
> Thanks for the work! The perf tool and perf test should run on older
> kernels ideally without failure. I'm assuming this would fail on an
> older kernel. Could we make the return value skip in that case?

Ah, hmm, I wasn't aware of that. This would fail on an older kernel,
yes. It's not possible to distinguish between an older kernel and a
kernel where this fix broke (at least not without hardcoding in an
expected good kernel version, which seems undesirable), so that would
mean the test would always return ok or skip, not ok or fail. Is that
ok?

> > +
> > + if (fcntl(fd, F_SETOWN, getpid())) {
> > + pr_debug("failed F_SETOWN getpid() %d\n", errno);
> > + goto cleanup;
> > + }
> > +
> > + if (fcntl(fd, F_SETSIG, SIGIO)) {
> > + pr_debug("failed F_SETSIG SIGIO %d\n", errno);
> > + goto cleanup;
> > + }
> > +
> > + p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> > + if (p == NULL) {
> > + pr_debug("failed to mmap\n");
> > + goto cleanup;
> > + }
> > +
> > + if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)) {
> > + pr_debug("failed PERF_EVENT_IOC_ENABLE %d\n", errno);
> > + goto cleanup;
> > + }
> > +
> > + sleep(30);
>
> This is kind of a painful wait, could it be less? No sleeps would be best :-)

We could synchronize with the child using SIGSTOP instead of sleeping
here. Not sure about getting rid of the tiny sleeps in the child. I
have observed that sched_yield() doesn't reliably trigger context
switches (which isn't surprising). I'll experiment with blocking on a
pipe or something.

- Kyle

> Thanks,
> Ian
>
> > +
> > + ret = (sigio_count == 1) ? TEST_OK : TEST_FAIL;
> > +
> > +cleanup:
> > + if (p != NULL)
> > + munmap(p, 2 * page_size);
> > +
> > + if (fd >= 0)
> > + close(fd);
> > +
> > + if (child > 0) {
> > + kill(child, SIGTERM);
> > + waitpid(child, NULL, 0);
> > + }
> > +
> > + if (previous_sigio != SIG_ERR)
> > + signal(SIGIO, previous_sigio);
> > +
> > + return ret;
> > +}
> > +
> > +DEFINE_SUITE("Watermark signal handler", watermark_signal);
> > --
> > 2.34.1
> >