From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
To: Juri Lelli <juri.lelli@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Vincent Guittot <vincent.guittot@linaro.org>
Cc: linux-kernel@vger.kernel.org,
"Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>,
"Dietmar Eggemann" <dietmar.eggemann@arm.com>,
"Steven Rostedt" <rostedt@goodmis.org>,
"Ben Segall" <bsegall@google.com>, "Mel Gorman" <mgorman@suse.de>,
"Valentin Schneider" <vschneid@redhat.com>,
"K Prateek Nayak" <kprateek.nayak@amd.com>,
"Luca Abeni" <luca.abeni@santannapisa.it>,
"Mathieu Poirier" <mathieu.poirier@linaro.org>,
"Waiman Long" <longman@redhat.com>,
"Ridong Chen" <ridong.chen@linux.dev>,
"Tejun Heo" <tj@kernel.org>,
"Johannes Weiner" <hannes@cmpxchg.org>,
"Michal Koutný" <mkoutny@suse.com>,
"Shuah Khan" <shuah@kernel.org>,
"Sebastian Andrzej Siewior" <bigeasy@linutronix.de>,
"Clark Williams" <clrkwllms@kernel.org>,
cgroups@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-rt-devel@lists.linux.dev
Subject: [PATCH v2 10/21] selftests/sched: Test SCHED_DEADLINE bandwidth across a root domain rebuild
Date: Wed, 4 Nov 2026 16:11:56 -0500 [thread overview]
Message-ID: <20261104211207.1392343-11-mathieu.desnoyers@efficios.com> (raw)
In-Reply-To: <20261104211207.1392343-1-mathieu.desnoyers@efficios.com>
Add deadline_rebuild_test, for the previous patch. In each case a task
of 400ms every second runs 380ms, then leaves SCHED_DEADLINE, its 0-lag
time some 570ms ahead; past that time, the one-CPU partition root P must
admit one task of 600ms every second and refuse a second with EBUSY:
- dies_before_rebuild: the task, in P, exits, and a second partition
root is made meanwhile, which rebuilds the accounting of every root
domain;
- switched_out_before_rebuild: the same, the task switched to
SCHED_OTHER and asleep in P;
- moved_after_switch_out: the task, outside P, switches to SCHED_OTHER,
moves into P and dies there.
A kernel that removes the bandwidth from P's root domain when a rebuild
dropped it, or when P never had it, admits both. Skipped without root,
the cgroup v2 cpuset controller, deadline admission control (that is,
with sched_rt_runtime_us at -1) or 3 CPUs.
In 8 VMs of 8 vCPUs each, three runs in each, v7.4-rc1 with the four
deadline fixes of sched/urgent fails the three cases, and so does it
with the previous patches but the last; with it, the three pass; every
time. v7.3 fails them as well (2 VMs, three runs in each).
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
---
tools/testing/selftests/sched/.gitignore | 1 +
tools/testing/selftests/sched/Makefile | 2 +-
.../selftests/sched/deadline_rebuild_test.c | 462 ++++++++++++++++++
3 files changed, 464 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/sched/deadline_rebuild_test.c
diff --git a/tools/testing/selftests/sched/.gitignore b/tools/testing/selftests/sched/.gitignore
index d97f40c21ab5..fc7ab1ddbbe7 100644
--- a/tools/testing/selftests/sched/.gitignore
+++ b/tools/testing/selftests/sched/.gitignore
@@ -1,2 +1,3 @@
cs_prctl_test
deadline_switch_test
+deadline_rebuild_test
diff --git a/tools/testing/selftests/sched/Makefile b/tools/testing/selftests/sched/Makefile
index ae9421eabf15..728e046b463c 100644
--- a/tools/testing/selftests/sched/Makefile
+++ b/tools/testing/selftests/sched/Makefile
@@ -10,6 +10,6 @@ LDLIBS += -lpthread
TEST_GEN_FILES := cs_prctl_test
TEST_PROGS := cs_prctl_test
-TEST_GEN_PROGS := deadline_switch_test
+TEST_GEN_PROGS := deadline_switch_test deadline_rebuild_test
include ../lib.mk
diff --git a/tools/testing/selftests/sched/deadline_rebuild_test.c b/tools/testing/selftests/sched/deadline_rebuild_test.c
new file mode 100644
index 000000000000..d1f948f5de77
--- /dev/null
+++ b/tools/testing/selftests/sched/deadline_rebuild_test.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The bandwidth of a SCHED_DEADLINE task that leaves the policy, or dies,
+ * before its 0-lag time stays allocated in its root domain until the
+ * inactive timer removes it. It must be removed once, from the root
+ * domain that keeps it:
+ *
+ * - dies: a task of a one-CPU partition root P dies, and a second
+ * partition root Q is made meanwhile, which rebuilds the accounting
+ * of every root domain;
+ * - switched out: the same, the task switched to SCHED_OTHER;
+ * - moved: a task outside P switched to SCHED_OTHER, then moved into P
+ * before its 0-lag time.
+ *
+ * Then P, past the timer, must admit one task of 600ms every second and
+ * refuse a second one (EBUSY). A kernel that removes the bandwidth from
+ * P's root domain when the rebuild dropped it, or when P never held it,
+ * leaves it negative: it admits both.
+ *
+ * Needs root, the cgroup v2 cpuset controller, deadline admission
+ * control and 3 CPUs.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysinfo.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "../kselftest.h"
+
+#ifndef SCHED_DEADLINE
+#define SCHED_DEADLINE 6
+#endif
+
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_SEC 1000000000ULL
+
+struct dl_attr {
+ uint32_t size;
+ uint32_t sched_policy;
+ uint64_t sched_flags;
+ int32_t sched_nice;
+ uint32_t sched_priority;
+ uint64_t sched_runtime;
+ uint64_t sched_deadline;
+ uint64_t sched_period;
+};
+
+static char root[PATH_MAX], cg_p[PATH_MAX], cg_q[PATH_MAX];
+static int ncpus;
+
+static int set_deadline(pid_t pid, uint64_t runtime)
+{
+ struct dl_attr attr = {
+ .size = sizeof(attr),
+ .sched_policy = SCHED_DEADLINE,
+ .sched_runtime = runtime,
+ .sched_deadline = NSEC_PER_SEC,
+ .sched_period = NSEC_PER_SEC,
+ };
+
+ return syscall(__NR_sched_setattr, pid, &attr, 0) ? -errno : 0;
+}
+
+static int set_other(pid_t pid)
+{
+ struct sched_param sp = { .sched_priority = 0 };
+
+ return sched_setscheduler(pid, SCHED_OTHER, &sp) ? -errno : 0;
+}
+
+/* These clocks cannot fail: both are always supported, the pointer is valid. */
+static uint64_t now_ns(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+}
+
+static void sleep_ns(uint64_t ns)
+{
+ struct timespec ts = { .tv_sec = ns / NSEC_PER_SEC, .tv_nsec = ns % NSEC_PER_SEC };
+
+ while (nanosleep(&ts, &ts) && errno == EINTR)
+ ;
+}
+
+static void reap(pid_t pid)
+{
+ int status;
+
+ if (pid <= 0)
+ return;
+ if (kill(pid, SIGKILL) && errno != ESRCH)
+ ksft_print_msg("kill %d: %s\n", pid, strerror(errno));
+ set_other(pid); /* gone already: nothing to switch */
+ while (waitpid(pid, &status, 0) != pid) {
+ if (errno == EINTR)
+ continue;
+ ksft_print_msg("waitpid %d: %s\n", pid, strerror(errno));
+ break;
+ }
+}
+
+static int write_file(const char *dir, const char *name, const char *val)
+{
+ char path[PATH_MAX];
+ int fd, err = 0;
+ ssize_t n;
+
+ if (snprintf(path, sizeof(path), "%s/%s", dir, name) >= (int)sizeof(path))
+ return -ENAMETOOLONG;
+ fd = open(path, O_WRONLY);
+ if (fd < 0)
+ return -errno;
+ n = write(fd, val, strlen(val));
+ if (n < 0)
+ err = -errno;
+ else if (n != (ssize_t)strlen(val))
+ err = -EIO;
+ if (close(fd) && !err)
+ err = -errno;
+ return err;
+}
+
+static int read_file(const char *path, char *buf, size_t len)
+{
+ int fd = open(path, O_RDONLY);
+ ssize_t n;
+
+ if (fd < 0)
+ return -1;
+ do {
+ n = read(fd, buf, len - 1);
+ } while (n < 0 && errno == EINTR);
+ close(fd);
+ if (n < 0)
+ return -1;
+ buf[n] = 0;
+ return 0;
+}
+
+static int enter(const char *cg, pid_t pid)
+{
+ char buf[16];
+
+ snprintf(buf, sizeof(buf), "%d", pid);
+ return write_file(cg, "cgroup.procs", buf);
+}
+
+/* A partition root of the one CPU @cpu, or -1. */
+static int make_partition(const char *cg, int cpu)
+{
+ char path[PATH_MAX], buf[32];
+
+ if (mkdir(cg, 0755) && errno != EEXIST)
+ return -1;
+ snprintf(buf, sizeof(buf), "%d", cpu);
+ if (write_file(cg, "cpuset.cpus", buf) || write_file(cg, "cpuset.cpus.partition", "root"))
+ return -1;
+ snprintf(path, sizeof(path), "%s/cpuset.cpus.partition", cg);
+ if (read_file(path, buf, sizeof(buf)) || strcmp(buf, "root\n"))
+ return -1;
+ return 0;
+}
+
+static void remove_partition(const char *cg)
+{
+ int err = write_file(cg, "cpuset.cpus.partition", "member");
+ uint64_t end;
+
+ if (err && err != -ENOENT)
+ ksft_print_msg("%s back to a member: %s\n", cg, strerror(-err));
+ /* A cgroup whose last task just died takes a moment to empty. */
+ end = now_ns() + NSEC_PER_SEC;
+ while (rmdir(cg)) {
+ if (errno == ENOENT)
+ return;
+ if (errno != EBUSY || now_ns() > end) {
+ ksft_print_msg("rmdir %s: %s\n", cg, strerror(errno));
+ return;
+ }
+ sleep_ns(10 * NSEC_PER_MSEC);
+ }
+}
+
+/* Both clocks are always supported and the pointer is valid: no error. */
+static uint64_t cpu_time_ns(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+ return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
+}
+
+enum depart { DIES, SWITCHED_OUT };
+
+/*
+ * In @cg if given, made SCHED_DEADLINE at 400ms every second, the child
+ * runs 380ms of it, then dies or switches to SCHED_OTHER and sleeps:
+ * its 0-lag time is then some 570ms ahead. The pipe @fd gets a byte once
+ * it is out of the policy; for one that dies, its exit status tells. The
+ * caller keeps the read end alone.
+ */
+static pid_t departing(const char *cg, enum depart how, int fd[2])
+{
+ pid_t pid = fork();
+
+ if (pid < 0)
+ ksft_print_msg("fork: %s\n", strerror(errno));
+ if (!pid) {
+ uint64_t t0;
+
+ close(fd[0]);
+ if (cg && enter(cg, getpid()))
+ _exit(2);
+ if (set_deadline(0, 400 * NSEC_PER_MSEC))
+ _exit(3);
+ t0 = cpu_time_ns();
+ while (cpu_time_ns() - t0 < 380 * NSEC_PER_MSEC)
+ ;
+ if (how == DIES)
+ _exit(0);
+ if (set_other(0) || write(fd[1], "x", 1) != 1)
+ _exit(4);
+ for (;;)
+ pause();
+ }
+ close(fd[1]);
+ fd[1] = -1;
+ return pid;
+}
+
+/* Wait until the child is out of SCHED_DEADLINE, dead or switched out. */
+static int departed(pid_t *pid, enum depart how, int fd)
+{
+ int status;
+ char c;
+
+ if (*pid < 0)
+ return -1;
+ /* No byte: the child failed, and its exit status tells. */
+ if (how == SWITCHED_OUT) {
+ ssize_t n;
+
+ do {
+ n = read(fd, &c, 1);
+ } while (n < 0 && errno == EINTR);
+ if (n == 1)
+ return 0;
+ }
+ if (waitpid(*pid, &status, 0) != *pid)
+ return -1;
+ *pid = -1;
+ return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+}
+
+/*
+ * A child that enters @cg and sleeps there, or -1. It moves itself, so
+ * that it last ran on a CPU of @cg, and says so once it is there.
+ */
+static pid_t sleeper(const char *cg)
+{
+ int fd[2];
+ pid_t pid;
+ char c;
+
+ if (pipe(fd))
+ return -1;
+ pid = fork();
+ if (pid < 0)
+ ksft_print_msg("fork: %s\n", strerror(errno));
+ if (!pid) {
+ close(fd[0]);
+ if (enter(cg, getpid()) || write(fd[1], "x", 1) != 1)
+ _exit(1);
+ for (;;)
+ pause();
+ }
+ close(fd[1]);
+ if (pid > 0 && read(fd[0], &c, 1) != 1) {
+ reap(pid);
+ pid = -1;
+ }
+ close(fd[0]);
+ return pid;
+}
+
+/* P, past every timer, admits one task of 600ms every second, not two. */
+static int check_p(void)
+{
+ pid_t a = sleeper(cg_p), b = sleeper(cg_p);
+ int ret = KSFT_FAIL, e1, e2;
+
+ if (a < 0 || b < 0) {
+ ksft_print_msg("no task could enter P\n");
+ goto out;
+ }
+ e1 = set_deadline(a, 600 * NSEC_PER_MSEC);
+ e2 = set_deadline(b, 600 * NSEC_PER_MSEC);
+ if (e1)
+ ksft_print_msg("a first task refused in P: %s\n", strerror(-e1));
+ else if (!e2)
+ ksft_print_msg("1.2 CPUs of deadline tasks admitted in a partition of one CPU\n");
+ else if (e2 != -EBUSY)
+ ksft_print_msg("the second task refused with %s, not EBUSY\n", strerror(-e2));
+ else
+ ret = KSFT_PASS;
+out:
+ reap(a);
+ reap(b);
+ return ret;
+}
+
+/* A task of P leaves, and Q is made before its 0-lag time. */
+static int test_rebuild(enum depart how)
+{
+ int ret = KSFT_FAIL, fd[2], err;
+ pid_t pid = -1;
+
+ if (pipe(fd))
+ return KSFT_FAIL;
+ if (make_partition(cg_p, ncpus - 1)) {
+ ksft_print_msg("no partition root on CPU %d\n", ncpus - 1);
+ ret = KSFT_SKIP;
+ goto out;
+ }
+ pid = departing(cg_p, how, fd);
+ err = departed(&pid, how, fd[0]);
+ if (err) {
+ ksft_print_msg("the deadline task failed (%d)\n", err);
+ goto out;
+ }
+ if (make_partition(cg_q, ncpus - 2)) {
+ ksft_print_msg("no partition root on CPU %d\n", ncpus - 2);
+ ret = KSFT_SKIP;
+ goto out;
+ }
+ /* Past the 0-lag time; one switched out sleeps on in P. */
+ sleep_ns(1500 * NSEC_PER_MSEC);
+ ret = check_p();
+out:
+ reap(pid);
+ remove_partition(cg_q);
+ remove_partition(cg_p);
+ close(fd[0]);
+ if (fd[1] >= 0)
+ close(fd[1]);
+ return ret;
+}
+
+/* A task outside P switches out, then moves into P before its 0-lag time. */
+static int test_move(void)
+{
+ int ret = KSFT_FAIL, fd[2], err;
+ pid_t pid = -1;
+
+ if (pipe(fd))
+ return KSFT_FAIL;
+ if (make_partition(cg_p, ncpus - 1)) {
+ ksft_print_msg("no partition root on CPU %d\n", ncpus - 1);
+ ret = KSFT_SKIP;
+ goto out;
+ }
+ pid = departing(NULL, SWITCHED_OUT, fd);
+ err = departed(&pid, SWITCHED_OUT, fd[0]);
+ if (err) {
+ ksft_print_msg("the deadline task failed (%d)\n", err);
+ goto out;
+ }
+ if (enter(cg_p, pid)) {
+ ksft_print_msg("the task cannot move into P\n");
+ goto out;
+ }
+ /* It wakes up, and dies, on P's CPU, before its 0-lag time. */
+ reap(pid);
+ pid = -1;
+ sleep_ns(1500 * NSEC_PER_MSEC);
+ ret = check_p();
+out:
+ reap(pid);
+ remove_partition(cg_p);
+ close(fd[0]);
+ if (fd[1] >= 0)
+ close(fd[1]);
+ return ret;
+}
+
+static int find_cgroup2(void)
+{
+ char line[PATH_MAX + 64], dev[64], dir[PATH_MAX], type[64];
+ FILE *f = fopen("/proc/self/mounts", "r");
+ int found = 0;
+
+ while (f && !found && fgets(line, sizeof(line), f)) {
+ if (sscanf(line, "%63s %4095s %63s", dev, dir, type) == 3 &&
+ !strcmp(type, "cgroup2")) {
+ snprintf(root, sizeof(root), "%s", dir);
+ found = 1;
+ }
+ }
+ if (f)
+ fclose(f);
+ return found ? 0 : -1;
+}
+
+static void report(const char *name, int ret)
+{
+ if (ret == KSFT_PASS)
+ ksft_test_result_pass("%s\n", name);
+ else if (ret == KSFT_SKIP)
+ ksft_test_result_skip("%s\n", name);
+ else
+ ksft_test_result_fail("%s\n", name);
+}
+
+int main(void)
+{
+ char buf[256], path[PATH_MAX + 32];
+ int added;
+
+ ksft_print_header();
+ if (geteuid())
+ ksft_exit_skip("needs root\n");
+ ncpus = get_nprocs();
+ if (ncpus < 3)
+ ksft_exit_skip("needs 3 CPUs\n");
+ if (read_file("/proc/sys/kernel/sched_rt_runtime_us", buf, sizeof(buf)) || atol(buf) < 0)
+ ksft_exit_skip("no deadline admission control\n");
+ if (find_cgroup2())
+ ksft_exit_skip("cgroup v2 is not mounted\n");
+ /* The cpuset controller for the root's children while the test runs. */
+ snprintf(path, sizeof(path), "%s/cgroup.subtree_control", root);
+ if (read_file(path, buf, sizeof(buf)))
+ ksft_exit_skip("no cgroup.subtree_control\n");
+ added = !strstr(buf, "cpuset");
+ if (added && write_file(root, "cgroup.subtree_control", "+cpuset"))
+ ksft_exit_skip("no cpuset controller\n");
+ if (snprintf(cg_p, sizeof(cg_p), "%s/deadline_rebuild_p", root) >= (int)sizeof(cg_p) ||
+ snprintf(cg_q, sizeof(cg_q), "%s/deadline_rebuild_q", root) >= (int)sizeof(cg_q))
+ ksft_exit_skip("cgroup v2 mount path too long\n");
+
+ ksft_set_plan(3);
+ report("dies_before_rebuild", test_rebuild(DIES));
+ report("switched_out_before_rebuild", test_rebuild(SWITCHED_OUT));
+ report("moved_after_switch_out", test_move());
+ if (added && write_file(root, "cgroup.subtree_control", "-cpuset"))
+ ksft_print_msg("the cpuset controller stays enabled\n");
+ ksft_finished();
+}
--
2.47.3
next prev parent reply other threads:[~2026-11-04 21:12 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 21:11 [PATCH v2 00/21] sched/deadline: bandwidth accounting fixes and selftests Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 01/21] sched: Reject SCHED_FLAG_KEEP_PARAMS before reserving deadline bandwidth Mathieu Desnoyers
2026-11-05 7:14 ` Juri Lelli
2026-11-04 21:11 ` [PATCH v2 02/21] sched: Refuse SCHED_FLAG_KEEP_PARAMS that would change the policy Mathieu Desnoyers
2026-11-05 7:15 ` Juri Lelli
2026-11-04 21:11 ` [PATCH v2 03/21] sched/deadline: Do not admit bandwidth for parameters that are kept Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 04/21] sched/deadline: Do not divide by zero active CPUs of a root domain Mathieu Desnoyers
2026-11-04 21:46 ` sashiko-bot
2026-11-04 21:11 ` [PATCH v2 05/21] sched/deadline: Do not BUG() on a deadline task with no active CPU allowed Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 06/21] sched/deadline: Do not lose a throttled task switched out and back Mathieu Desnoyers
2026-11-04 22:09 ` sashiko-bot
2026-11-04 21:11 ` [PATCH v2 07/21] sched/deadline: Do not release a departing sleeper's bandwidth twice Mathieu Desnoyers
2026-11-04 22:15 ` sashiko-bot
2026-11-04 21:11 ` [PATCH v2 08/21] selftests/sched: Add a test of SCHED_DEADLINE policy round trips Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 09/21] sched/deadline: Keep departing bandwidth across a root domain rebuild Mathieu Desnoyers
2026-11-04 22:44 ` sashiko-bot
2026-11-04 21:11 ` Mathieu Desnoyers [this message]
2026-11-04 21:11 ` [PATCH v2 11/21] sched/deadline: Do not take a waking task's bandwidth off a runqueue it left Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 12/21] sched/debug: Show the deadline bandwidth of each runqueue Mathieu Desnoyers
2026-11-04 21:11 ` [PATCH v2 13/21] sched/deadline: Rebuild the root domains' accounting without cpusets too Mathieu Desnoyers
2026-11-04 23:18 ` sashiko-bot
2026-11-04 21:12 ` [PATCH v2 14/21] selftests/sched: Add a test of SCHED_DEADLINE bandwidth accounting Mathieu Desnoyers
2026-11-04 21:12 ` [PATCH v2 15/21] sched/deadline: Record the root domain holding a task's bandwidth Mathieu Desnoyers
2026-11-04 23:31 ` sashiko-bot
2026-11-04 21:12 ` [PATCH v2 16/21] sched/deadline: Move a task's bandwidth only where room was made Mathieu Desnoyers
2026-11-04 21:12 ` [PATCH v2 17/21] cgroup/cpuset: Give back the bandwidth an attach did not move Mathieu Desnoyers
2026-11-04 23:56 ` sashiko-bot
2026-11-04 21:12 ` [PATCH v2 18/21] sched/deadline: Credit a rebuild to a CPU that can hold the bandwidth Mathieu Desnoyers
2026-11-05 0:18 ` sashiko-bot
2026-11-04 21:12 ` [PATCH v2 19/21] sched: Clear cpuset_locked where the policy check retries Mathieu Desnoyers
2026-11-04 21:12 ` [PATCH v2 20/21] sched/deadline: Cancel the replenishment timer with the parameters Mathieu Desnoyers
2026-11-05 0:25 ` sashiko-bot
2026-11-04 21:12 ` [PATCH v2 21/21] sched/deadline: Count the bandwidth a boosted task is given Mathieu Desnoyers
2026-11-05 0:36 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104211207.1392343-11-mathieu.desnoyers@efficios.com \
--to=mathieu.desnoyers@efficios.com \
--cc=bigeasy@linutronix.de \
--cc=bsegall@google.com \
--cc=cgroups@vger.kernel.org \
--cc=clrkwllms@kernel.org \
--cc=dietmar.eggemann@arm.com \
--cc=hannes@cmpxchg.org \
--cc=juri.lelli@redhat.com \
--cc=kprateek.nayak@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=longman@redhat.com \
--cc=luca.abeni@santannapisa.it \
--cc=mathieu.poirier@linaro.org \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=mkoutny@suse.com \
--cc=peterz@infradead.org \
--cc=ridong.chen@linux.dev \
--cc=rostedt@goodmis.org \
--cc=shuah@kernel.org \
--cc=tj@kernel.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®