]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf test: Add workload-ctl option
authorJames Clark <james.clark@linaro.org>
Tue, 9 Jun 2026 14:40:07 +0000 (15:40 +0100)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 10 Jun 2026 21:55:48 +0000 (18:55 -0300)
Add a --workload-ctl=fifo:ctl-fifo[,ack-fifo] option for 'perf test
-w'. When set, run_workload() opens the named FIFO, writes enable before
invoking the builtin workload, writes disable before returning, and
waits for ack responses when an ack FIFO is provided to ensure that the
workload doesn't run until the events are enabled.

This can be used to limit the scope of the recording to only the
workload execution and avoid recording Perf setup and teardown code if
Perf record is started with events disabled (-D 1).

Assisted-by: Codex:GPT-5.5
Signed-off-by: James Clark <james.clark@linaro.org>
Cc: Amir Ayupov <aaupov@meta.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Mike Leach <mike.leach@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paschalis Mpeis <Paschalis.Mpeis@arm.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Suzuki Poulouse <suzuki.poulose@arm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-test.txt
tools/perf/tests/builtin-test.c

index 32da0d1fa86ab411434bb77ec47e5e76161613fd..2f4a91f5b9dc6466707c6f4ff5c0f8686a49051a 100644 (file)
@@ -69,3 +69,15 @@ OPTIONS
 
 --list-workloads::
        List the available workloads to use with -w/--workload.
+
+--record-ctl=fifo:ctl-fifo[,ack-fifo]::
+       This option is used to communicate with a perf record session in order
+       to control the recording scope to only the workload and avoid recording
+       setup and teardown code. When specifying this option, the same FIFO path
+       must be specified in the record session via:
+
+         perf record -D -1 --control=fifo:ctl-fifo[,ack-fifo] ...
+
+       Perf test sends 'enable' and 'disable' commands through ctl-fifo to
+       control event recording. If 'ack-fifo' is provided, the workload runner
+       waits for an 'ack' response after each command.
index b64fc2204f223170f03f37709b1b8c6b01b33d45..86ea427eb0aa8f500db9fd044c3a2a3b156f0ea8 100644 (file)
@@ -56,6 +56,7 @@ static unsigned int runs_per_test = 1;
 static unsigned int failure_snippet_lines = 10;
 const char *dso_to_test;
 const char *test_objdump_path = "objdump";
+static const char *workload_control;
 
 /*
  * List of architecture specific tests. Not a weak symbol as the array length is
@@ -168,6 +169,11 @@ static struct test_workload *workloads[] = {
 #endif
 };
 
+struct workload_control {
+       int ctl_fd;
+       int ack_fd;
+};
+
 #define workloads__for_each(workload) \
        for (unsigned i = 0; i < ARRAY_SIZE(workloads) && ({ workload = workloads[i]; 1; }); i++)
 
@@ -1387,13 +1393,185 @@ static int workloads__fprintf_list(FILE *fp)
        return printed;
 }
 
+static int perf_control_open_fifo(struct workload_control *ctl, const char *str)
+{
+       char *s, *p;
+       int ret;
+
+       if (strncmp(str, "fifo:", 5))
+               return -EINVAL;
+
+       str += 5;
+       if (!*str || *str == ',')
+               return -EINVAL;
+
+       s = strdup(str);
+       if (!s)
+               return -ENOMEM;
+
+       p = strchr(s, ',');
+       if (p)
+               *p = '\0';
+
+       ctl->ctl_fd = open(s, O_WRONLY | O_CLOEXEC);
+       if (ctl->ctl_fd < 0) {
+               ret = -errno;
+               pr_err("Failed to open workload control FIFO '%s': %m\n", s);
+               free(s);
+               return ret;
+       }
+
+       if (p && *++p) {
+               ctl->ack_fd = open(p, O_RDONLY | O_CLOEXEC);
+               if (ctl->ack_fd < 0) {
+                       ret = -errno;
+                       pr_err("Failed to open workload control ack FIFO '%s': %m\n", p);
+                       close(ctl->ctl_fd);
+                       ctl->ctl_fd = -1;
+                       free(s);
+                       return ret;
+               }
+       }
+
+       free(s);
+       return 0;
+}
+
+static int perf_control_open(struct workload_control *ctl)
+{
+       int ret;
+
+       if (!workload_control)
+               return 0;
+
+       ret = perf_control_open_fifo(ctl, workload_control);
+
+       if (ret == -EINVAL) {
+               pr_err("Unsupported workload control spec '%s', expected fifo:ctl-fifo[,ack-fifo]\n",
+                       workload_control);
+       }
+
+       return ret;
+}
+
+static void perf_control_close(struct workload_control *ctl)
+{
+       if (ctl->ctl_fd >= 0) {
+               close(ctl->ctl_fd);
+               ctl->ctl_fd = -1;
+       }
+       if (ctl->ack_fd >= 0) {
+               close(ctl->ack_fd);
+               ctl->ack_fd = -1;
+       }
+}
+
+static int perf_control_write_cmd(int fd, const char *cmd)
+{
+       size_t len = strlen(cmd);
+       ssize_t ret;
+
+       while (len) {
+               ret = write(fd, cmd, len);
+               if (ret < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       pr_err("Failed to write perf control command: %m\n");
+                       return -1;
+               }
+
+               if (!ret) {
+                       pr_err("Failed to write perf control command: short write\n");
+                       return -1;
+               }
+
+               cmd += ret;
+               len -= ret;
+       }
+
+       return 0;
+}
+
+static int perf_control_read_ack(int fd)
+{
+       char buf[16];
+       ssize_t ret;
+
+       do {
+               ret = read(fd, buf, sizeof(buf) - 1);
+       } while (ret < 0 && errno == EINTR);
+
+       if (ret < 0) {
+               pr_err("Failed to read perf control ack: %m\n");
+               return -1;
+       }
+
+       if (!ret) {
+               pr_err("Unexpected EOF while reading perf control ack\n");
+               return -1;
+       }
+
+       buf[ret] = '\0';
+       for (ssize_t i = 0; i < ret; i++) {
+               if (buf[i] == '\n' || buf[i] == '\0') {
+                       buf[i] = '\0';
+                       break;
+               }
+       }
+
+       if (strcmp(buf, "ack")) {
+               pr_err("Unexpected perf control ack: %s\n", buf);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int perf_control_send(struct workload_control *ctl, const char *cmd)
+{
+       if (ctl->ctl_fd < 0)
+               return 0;
+
+       if (perf_control_write_cmd(ctl->ctl_fd, cmd))
+               return -1;
+
+       if (ctl->ack_fd >= 0 && perf_control_read_ack(ctl->ack_fd))
+               return -1;
+
+       return 0;
+}
+
 static int run_workload(const char *work, int argc, const char **argv)
 {
        struct test_workload *twl;
 
        workloads__for_each(twl) {
-               if (!strcmp(twl->name, work))
-                       return twl->func(argc, argv);
+               struct workload_control ctl = {
+                       .ctl_fd = -1,
+                       .ack_fd = -1,
+               };
+               int control_ret, ret;
+
+               if (strcmp(twl->name, work))
+                       continue;
+
+               ret = perf_control_open(&ctl);
+               if (ret)
+                       return ret;
+
+               if (perf_control_send(&ctl, "enable\n")) {
+                       perf_control_close(&ctl);
+                       return -1;
+               }
+
+               ret = twl->func(argc, argv);
+
+               control_ret = perf_control_send(&ctl, "disable\n");
+               perf_control_close(&ctl);
+               if (control_ret)
+                       return -1;
+
+               return ret;
        }
 
        pr_info("No workload found: %s\n", work);
@@ -1486,6 +1664,8 @@ int cmd_test(int argc, const char **argv)
        OPT_UINTEGER('r', "runs-per-test", &runs_per_test,
                     "Run each test the given number of times, default 1"),
        OPT_STRING('w', "workload", &workload, "work", "workload to run for testing, use '--list-workloads' to list the available ones."),
+       OPT_STRING(0, "record-ctl", &workload_control, "fifo:ctl-fifo[,ack-fifo]",
+                  "Write enable to the fifo just before running the workload and disable after, with optional ack from ack-fifo"),
        OPT_BOOLEAN(0, "list-workloads", &list_workloads, "List the available builtin workloads to use with -w/--workload"),
        OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"),
        OPT_STRING(0, "objdump", &test_objdump_path, "path",