From: Samir Mulani <samir@linux.ibm.com>
To: ltp@lists.linux.it
Cc: Samir Mulani <samir@linux.ibm.com>
Subject: [LTP] [PATCH 1/2] cgroup: Add cgroup_burst01 test
Date: Mon, 28 Sep 2026 07:17:34 +0200 [thread overview]
Message-ID: <20260928051740.591819-2-samir@linux.ibm.com> (raw)
In-Reply-To: <20260928051740.591819-1-samir@linux.ibm.com>
I added cpu_max_burst01 to test the cpu.max.burst interface in
cgroupv2. This interface controls the burst budget for CFS bandwidth
throttling. When a cgroup uses less than its quota in a period, the
kernel saves up to (quota + burst) microseconds; a later busy period
can use that saved credit to run beyond the normal quota.
Test scenarios:
TC1 - cpu.max.burst defaults to 0; writing a non-zero value and
then clearing it back to 0 works correctly.
TC2 - Writing burst = quota/2 and burst = quota both read back the
correct value.
TC3 - A worker underruns to build up burst credit, then overloads
to consume it; cpu.stat nr_bursts and burst_usec counters
must increase. Also checks that burst can be updated while
tasks are running.
TC4 - The burst value is preserved after quota is removed
(cpu.max set to "max").
TC5 - The burst value is preserved after the period is changed.
Requires CONFIG_CFS_BANDWIDTH=y and cgroupv2 cpu controller.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
runtest/controllers | 1 +
.../kernel/controllers/cgroup/.gitignore | 1 +
.../controllers/cgroup/cpu_max_burst01.c | 650 ++++++++++++++++++
3 files changed, 652 insertions(+)
create mode 100644 testcases/kernel/controllers/cgroup/cpu_max_burst01.c
diff --git a/runtest/controllers b/runtest/controllers
index 93c52c439..aa93ea5af 100644
--- a/runtest/controllers
+++ b/runtest/controllers
@@ -3,6 +3,7 @@ cgroup_core01 cgroup_core01
cgroup_core02 cgroup_core02
cgroup_core03 cgroup_core03
cgroup cgroup_regression_test.sh
+cpu_max_burst01 cpu_max_burst01
memcg_regression memcg_regression_test.sh
memcg_test_3 memcg_test_3
memcg_failcnt memcg_failcnt.sh
diff --git a/testcases/kernel/controllers/cgroup/.gitignore b/testcases/kernel/controllers/cgroup/.gitignore
index 9f1d1ada9..2506388fd 100644
--- a/testcases/kernel/controllers/cgroup/.gitignore
+++ b/testcases/kernel/controllers/cgroup/.gitignore
@@ -3,3 +3,4 @@
/cgroup_core01
/cgroup_core02
/cgroup_core03
+/cpu_max_burst01
diff --git a/testcases/kernel/controllers/cgroup/cpu_max_burst01.c b/testcases/kernel/controllers/cgroup/cpu_max_burst01.c
new file mode 100644
index 000000000..afb8befa6
--- /dev/null
+++ b/testcases/kernel/controllers/cgroup/cpu_max_burst01.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 Samir Mulani, IBM Corporation.
+ */
+
+/*\
+ * Functional tests for the cgroupv2 cpu.max.burst interface.
+ *
+ * cpu.max.burst controls the burst budget for CFS bandwidth throttling.
+ * When a cgroup uses less than its quota in a period, the kernel retains
+ * up to (quota + burst) microseconds in the runtime pool. A later busy
+ * period may draw from that retained credit, temporarily running beyond
+ * the nominal quota rate.
+ *
+ * Kernel accounting (kernel/sched/fair.c __refill_cfs_bandwidth_runtime):
+ *
+ * At every period boundary:
+ * runtime += quota
+ * surplus = runtime_snap - runtime
+ * if (surplus > 0):
+ * burst_time += surplus
+ * nr_burst++
+ * runtime = min(runtime, quota + burst)
+ * runtime_snap = runtime
+ *
+ * Test scenarios:
+ * TC 1 - Default value and write-zero: cpu.max.burst defaults to 0;
+ * setting burst to non-zero then back to 0 is accepted.
+ * TC 2 - Write/readback, burst == quota boundary accepted, and
+ * burst > quota rejected with EINVAL.
+ * TC 3 - Functional burst accounting: a worker underruns to fill the
+ * pool, then overloads to trigger burst accounting; nr_bursts
+ * and burst_usec in cpu.stat must both increase.
+ * TC 4 - Burst preserved after quota cleared (cpu.max = "max").
+ * TC 5 - Burst preserved after period change.
+ *
+ * Requires: CONFIG_CFS_BANDWIDTH=y, cgroup v2 cpu controller.
+ */
+
+#define _GNU_SOURCE
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "tst_test.h"
+#include "tst_safe_clocks.h"
+#include "tst_safe_file_at.h"
+
+#define PERIOD_US 100000ULL
+#define QUOTA_US 20000ULL
+#define BURST_US 10000ULL
+
+static struct tst_cg_group *cg_child;
+
+static int cg_dirfd(const struct tst_cg_group *cg)
+{
+ int fd = tst_cg_group_unified_dir_fd(cg);
+
+ if (fd < 0)
+ tst_brk(TBROK, "tst_cg_group_unified_dir_fd returned %d", fd);
+ return fd;
+}
+
+static unsigned long long cg_read_ull(const struct tst_cg_group *cg,
+ const char *fname)
+{
+ char buf[64];
+ char *endp;
+ unsigned long long val;
+
+ SAFE_FILE_READAT(cg_dirfd(cg), fname, buf, sizeof(buf) - 1);
+
+ val = strtoull(buf, &endp, 10);
+ if (endp == buf)
+ tst_brk(TBROK, "strtoull failed on '%s' from %s", buf, fname);
+ return val;
+}
+
+static void cg_write_ull(const struct tst_cg_group *cg,
+ const char *fname, unsigned long long val)
+{
+ SAFE_FILE_PRINTFAT(cg_dirfd(cg), fname, "%llu", val);
+}
+
+static void cg_write_str_safe(const struct tst_cg_group *cg,
+ const char *fname, const char *str)
+{
+ SAFE_FILE_PRINTFAT(cg_dirfd(cg), fname, "%s", str);
+}
+
+/*
+ * cg_write_str() - attempt a write that may legitimately fail.
+ * Returns 0 on success, errno on failure.
+ */
+static int cg_write_str(const struct tst_cg_group *cg,
+ const char *fname, const char *str)
+{
+ int fd, saved = 0;
+ ssize_t n;
+
+ fd = SAFE_OPENAT(cg_dirfd(cg), fname, O_WRONLY);
+ n = write(fd, str, strlen(str));
+ if (n < 0)
+ saved = errno;
+ SAFE_CLOSE(fd);
+ return saved;
+}
+
+static void set_cpu_max(const struct tst_cg_group *cg,
+ unsigned long long quota_us,
+ unsigned long long period_us)
+{
+ char buf[64];
+
+ if (quota_us == ULLONG_MAX)
+ snprintf(buf, sizeof(buf), "max %llu", period_us);
+ else
+ snprintf(buf, sizeof(buf), "%llu %llu", quota_us, period_us);
+
+ cg_write_str_safe(cg, "cpu.max", buf);
+}
+
+static unsigned long long read_burst(const struct tst_cg_group *cg)
+{
+ return cg_read_ull(cg, "cpu.max.burst");
+}
+
+static void write_burst(const struct tst_cg_group *cg,
+ unsigned long long burst_us)
+{
+ cg_write_ull(cg, "cpu.max.burst", burst_us);
+}
+
+/*
+ * try_write_burst() - attempt a burst write that may legitimately fail.
+ * Returns 0 on success, errno on failure.
+ */
+static int try_write_burst(const struct tst_cg_group *cg,
+ unsigned long long burst_us)
+{
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%llu", burst_us);
+ return cg_write_str(cg, "cpu.max.burst", buf);
+}
+
+struct burst_stat {
+ unsigned long long nr_bursts;
+ unsigned long long burst_usec;
+};
+
+static int read_burst_stat(const struct tst_cg_group *cg,
+ struct burst_stat *s)
+{
+ char buf[4096];
+ char *line, *save;
+ ssize_t n;
+ int fd;
+ int got_bursts = 0, got_usec = 0;
+
+ memset(s, 0, sizeof(*s));
+
+ fd = SAFE_OPENAT(cg_dirfd(cg), "cpu.stat", O_RDONLY);
+ n = SAFE_READ(0, fd, buf, sizeof(buf) - 1);
+ SAFE_CLOSE(fd);
+
+ if (n <= 0)
+ return -1;
+ buf[n] = '\0';
+
+ for (line = strtok_r(buf, "\n", &save); line;
+ line = strtok_r(NULL, "\n", &save)) {
+ if (sscanf(line, "nr_bursts %llu", &s->nr_bursts) == 1)
+ got_bursts = 1;
+ if (sscanf(line, "burst_usec %llu", &s->burst_usec) == 1)
+ got_usec = 1;
+ }
+
+ return (got_bursts && got_usec) ? 0 : -1;
+}
+
+enum worker_cmd {
+ CMD_UNDERRUN,
+ CMD_OVERLOAD,
+ CMD_EXIT,
+};
+
+enum worker_ack { ACK_DONE };
+
+static int cmd_pipe[2] = { -1, -1 };
+static int ack_pipe[2] = { -1, -1 };
+static pid_t worker_pid = -1;
+
+static long long monotonic_us(void)
+{
+ struct timespec ts;
+
+ SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, &ts);
+ return (long long)ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL;
+}
+
+static long long process_cpu_us(void)
+{
+ struct timespec ts;
+
+ SAFE_CLOCK_GETTIME(CLOCK_PROCESS_CPUTIME_ID, &ts);
+ return (long long)ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL;
+}
+
+static void burn_cpu_for(long long dur_us)
+{
+ long long start = process_cpu_us();
+ volatile unsigned long v = 1;
+
+ while (process_cpu_us() - start < dur_us)
+ v = v * 1103515245UL + 12345UL;
+}
+
+/*
+ * worker_main() - command loop run by the forked child inside the cgroup.
+ *
+ * CMD_UNDERRUN:
+ * Runs 5 periods. In each period the worker burns 1 ms of CPU time then
+ * sleeps for the remainder of the period via clock_nanosleep(CLOCK_MONOTONIC).
+ * clock_nanosleep() genuinely blocks — the process goes off-CPU and consumes
+ * no quota while sleeping — so the cgroup uses ~1 ms out of 20 ms quota each
+ * period. After 2 periods the runtime pool reaches the (quota + burst = 30 ms)
+ * ceiling. nr_bursts stays 0 during this phase because the pool is never
+ * fully drained.
+ *
+ * CMD_OVERLOAD:
+ * Burns 4 x QUOTA_US = 80 ms of process CPU time to drain the pool
+ * across multiple period boundaries and trigger burst accounting.
+ */
+static void worker_main(void)
+{
+ enum worker_cmd cmd;
+ enum worker_ack ack = ACK_DONE;
+ ssize_t n;
+
+ SAFE_CLOSE(cmd_pipe[1]);
+ SAFE_CLOSE(ack_pipe[0]);
+
+ for (;;) {
+ n = SAFE_READ(1, cmd_pipe[0], &cmd, sizeof(cmd));
+ if (n != (ssize_t)sizeof(cmd))
+ break;
+
+ switch (cmd) {
+ case CMD_UNDERRUN:
+ {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ struct timespec deadline;
+ long long now_us;
+
+ /*
+ * Burn 1 ms of CPU quota, then
+ * genuinely sleep for the rest of the
+ * period. clock_nanosleep() blocks the
+ * process off-CPU so no quota is used
+ * while waiting.
+ */
+ burn_cpu_for(1000LL);
+
+ now_us = monotonic_us();
+ deadline.tv_sec =
+ (now_us + (long long)PERIOD_US)
+ / 1000000LL;
+ deadline.tv_nsec =
+ ((now_us + (long long)PERIOD_US)
+ % 1000000LL) * 1000LL;
+ while (clock_nanosleep(CLOCK_MONOTONIC,
+ TIMER_ABSTIME, &deadline,
+ NULL) == EINTR)
+ ;
+ }
+ }
+ break;
+
+ case CMD_OVERLOAD:
+ burn_cpu_for(4LL * (long long)QUOTA_US);
+ break;
+
+ case CMD_EXIT:
+ SAFE_CLOSE(cmd_pipe[0]);
+ SAFE_CLOSE(ack_pipe[1]);
+ exit(0);
+
+ default:
+ break;
+ }
+
+ SAFE_WRITE(1, ack_pipe[1], &ack, sizeof(ack));
+ }
+
+ exit(1);
+}
+
+static void worker_start(const struct tst_cg_group *cg)
+{
+ SAFE_PIPE(cmd_pipe);
+ SAFE_PIPE(ack_pipe);
+
+ worker_pid = SAFE_FORK();
+ if (!worker_pid) {
+ SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid());
+ worker_main();
+ exit(1);
+ }
+
+ SAFE_CLOSE(cmd_pipe[0]);
+ SAFE_CLOSE(ack_pipe[1]);
+}
+
+static void worker_send(enum worker_cmd cmd)
+{
+ enum worker_ack ack;
+
+ SAFE_WRITE(1, cmd_pipe[1], &cmd, sizeof(cmd));
+ if (cmd != CMD_EXIT)
+ SAFE_READ(1, ack_pipe[0], &ack, sizeof(ack));
+}
+
+static void worker_stop(void)
+{
+ if (worker_pid < 0)
+ return;
+
+ worker_send(CMD_EXIT);
+ SAFE_WAITPID(worker_pid, NULL, 0);
+ worker_pid = -1;
+
+ if (cmd_pipe[1] >= 0) {
+ SAFE_CLOSE(cmd_pipe[1]);
+ cmd_pipe[1] = -1;
+ }
+ if (ack_pipe[0] >= 0) {
+ SAFE_CLOSE(ack_pipe[0]);
+ ack_pipe[0] = -1;
+ }
+}
+
+/*
+ * tc1_default_and_zero - default value is 0; clearing back to 0 works.
+ *
+ * A freshly created cgroup must read 0 from cpu.max.burst. After writing
+ * a non-zero value, writing 0 must restore it to 0.
+ */
+static void tc1_default_and_zero(void)
+{
+ unsigned long long burst;
+
+ cg_child = tst_cg_group_mk(tst_cg, "burst_tc1");
+
+ burst = read_burst(cg_child);
+ if (burst != 0)
+ tst_res(TFAIL, "%s: expected default 0, got %llu", __func__, burst);
+ else
+ tst_res(TPASS, "%s: cpu.max.burst defaults to 0", __func__);
+
+ set_cpu_max(cg_child, QUOTA_US, PERIOD_US);
+ write_burst(cg_child, BURST_US);
+ write_burst(cg_child, 0);
+
+ burst = read_burst(cg_child);
+ if (burst == 0)
+ tst_res(TPASS, "%s: burst cleared back to 0", __func__);
+ else
+ tst_res(TFAIL, "%s: expected 0 after clear, got %llu us", __func__, burst);
+
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+/*
+ * tc2_write_readback_and_boundary - valid readback, burst == quota accepted,
+ * burst > quota rejected.
+ *
+ * Verifies write/readback correctness for burst = quota/2, confirms
+ * burst == quota is the upper valid boundary, and confirms burst > quota
+ * is rejected with EINVAL without corrupting the stored value.
+ */
+static void tc2_write_readback_and_boundary(void)
+{
+ const unsigned long long bad_burst = QUOTA_US + 1;
+ unsigned long long burst;
+ int err;
+
+ cg_child = tst_cg_group_mk(tst_cg, "burst_tc2");
+
+ set_cpu_max(cg_child, QUOTA_US, PERIOD_US);
+ write_burst(cg_child, BURST_US);
+
+ burst = read_burst(cg_child);
+ if (burst == BURST_US)
+ tst_res(TPASS, "tc2_write_readback: cpu.max.burst write/readback %llu us", burst);
+ else
+ tst_res(TFAIL, "tc2_write_readback: expected %llu us, got %llu us",
+ BURST_US, burst);
+
+ write_burst(cg_child, QUOTA_US);
+
+ burst = read_burst(cg_child);
+ if (burst == QUOTA_US)
+ tst_res(TPASS, "tc2_burst_equals_quota: burst == quota (%llu us) accepted", burst);
+ else
+ tst_res(TFAIL, "tc2_burst_equals_quota: expected %llu us, got %llu us",
+ QUOTA_US, burst);
+
+ /*
+ * Restore a known pre-seed value before the rejection test so that
+ * silent corruption of the stored burst is detectable.
+ */
+ write_burst(cg_child, BURST_US);
+
+ err = try_write_burst(cg_child, bad_burst);
+ burst = read_burst(cg_child);
+
+ if (!err) {
+ tst_res(TFAIL,
+ "tc2_burst_exceeds_quota: burst=%llu (quota+1) write succeeded, expected EINVAL",
+ bad_burst);
+ } else {
+ if (err != EINVAL) {
+ tst_res(TWARN,
+ "tc2_burst_exceeds_quota: got errno=%d (%s), expected EINVAL",
+ err, tst_strerrno(err));
+ }
+
+ if (burst != BURST_US) {
+ tst_res(TFAIL,
+ "tc2_burst_exceeds_quota: burst corrupted after rejected write (expected %llu us, got %llu us)",
+ BURST_US, burst);
+ } else {
+ tst_res(TPASS,
+ "tc2_burst_exceeds_quota: burst=%llu rejected (errno=%d), stored value unchanged (%llu us)",
+ bad_burst, err, burst);
+ }
+ }
+
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+/*
+ * tc3_functional_burst_accounting - verify nr_bursts and burst_usec increase.
+ *
+ * Configuration: quota=20ms burst=10ms period=100ms
+ * Pool ceiling: quota + burst = 30ms
+ *
+ * Phase A (underrun) - worker burns ~1 ms CPU per period over 5 periods:
+ * CPU usage ~1 ms/period << 20 ms quota. After 2 periods the pool reaches
+ * the 30 ms ceiling. nr_bursts stays 0: the pool fills but is never drained
+ * below runtime_snap, so the surplus at refill is never positive.
+ *
+ * Phase B (overload) - worker burns 4 x QUOTA_US = 80 ms process CPU time:
+ * Under 20 ms quota this spans >= 4 wall-clock periods.
+ * Period B1: pool=30ms, task consumes 30ms -> pool->0, throttled.
+ * Period B2 refill:
+ * runtime += 20ms -> 20ms
+ * surplus = snap(30) - 20 = +10ms > 0
+ * nr_burst++ , burst_time += 10ms
+ * runtime = min(20, 30) = 20ms
+ *
+ * The 'before' snapshot is taken after setup but BEFORE the worker starts,
+ * so it captures the zero baseline. Any burst activity from the underrun
+ * phase (there should be none) is already counted in 'before'. After the
+ * overload phase both counters must be strictly greater than 'before'.
+ */
+static void tc3_functional_burst_accounting(void)
+{
+ struct burst_stat before, after;
+ int stat_ok;
+
+ cg_child = tst_cg_group_mk(tst_cg, "burst_tc3");
+
+ set_cpu_max(cg_child, QUOTA_US, PERIOD_US);
+ write_burst(cg_child, BURST_US);
+
+ /*
+ * Snapshot BEFORE the worker starts. cpu.stat counters start at 0
+ * on a freshly created cgroup; capturing here gives a clean baseline
+ * that is unaffected by any scheduling activity inside the worker.
+ */
+ stat_ok = read_burst_stat(cg_child, &before);
+ if (stat_ok < 0) {
+ tst_res(TCONF,
+ "%s: nr_bursts/burst_usec absent from cpu.stat -- kernel may lack CONFIG_CFS_BANDWIDTH",
+ __func__);
+ cg_child = tst_cg_group_rm(cg_child);
+ return;
+ }
+
+ worker_start(cg_child);
+ worker_send(CMD_UNDERRUN);
+ worker_send(CMD_OVERLOAD);
+ worker_stop();
+
+ read_burst_stat(cg_child, &after);
+
+ tst_res(TINFO, "%s: nr_bursts %llu->%llu burst_usec %llu->%llu",
+ __func__,
+ before.nr_bursts, after.nr_bursts,
+ before.burst_usec, after.burst_usec);
+
+ if (after.nr_bursts > before.nr_bursts &&
+ after.burst_usec > before.burst_usec) {
+ tst_res(TPASS,
+ "%s: nr_bursts +%llu burst_usec +%llu us",
+ __func__,
+ after.nr_bursts - before.nr_bursts,
+ after.burst_usec - before.burst_usec);
+ } else {
+ tst_res(TFAIL,
+ "%s: burst accounting did not increase (nr_bursts %llu->%llu burst_usec %llu->%llu)",
+ __func__,
+ before.nr_bursts, after.nr_bursts,
+ before.burst_usec, after.burst_usec);
+ }
+
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+/*
+ * tc4_preserved_after_quota_clear - burst value survives quota removal.
+ *
+ * Setting cpu.max to "max <period>" (no quota) must not reset the stored
+ * burst value.
+ */
+static void tc4_preserved_after_quota_clear(void)
+{
+ unsigned long long burst_after;
+
+ cg_child = tst_cg_group_mk(tst_cg, "burst_tc4");
+
+ set_cpu_max(cg_child, QUOTA_US, PERIOD_US);
+ write_burst(cg_child, BURST_US);
+ set_cpu_max(cg_child, ULLONG_MAX, PERIOD_US);
+
+ burst_after = read_burst(cg_child);
+ if (burst_after == BURST_US)
+ tst_res(TPASS, "%s: burst preserved (%llu us) after quota cleared",
+ __func__, burst_after);
+ else
+ tst_res(TFAIL, "%s: expected %llu us after quota clear, got %llu us",
+ __func__, BURST_US, burst_after);
+
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+/*
+ * tc5_preserved_after_period_change - burst value survives period update.
+ *
+ * Updating only the period in cpu.max must not silently clear the burst value.
+ */
+static void tc5_preserved_after_period_change(void)
+{
+ const unsigned long long new_period = 200000ULL;
+ unsigned long long burst_after;
+
+ cg_child = tst_cg_group_mk(tst_cg, "burst_tc5");
+
+ set_cpu_max(cg_child, QUOTA_US, PERIOD_US);
+ write_burst(cg_child, BURST_US);
+ set_cpu_max(cg_child, QUOTA_US, new_period);
+
+ burst_after = read_burst(cg_child);
+ if (burst_after == BURST_US)
+ tst_res(TPASS, "%s: burst preserved (%llu us) after period changed to %llu us",
+ __func__, burst_after, new_period);
+ else
+ tst_res(TFAIL, "%s: expected %llu us after period change, got %llu us",
+ __func__, BURST_US, burst_after);
+
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+static void (*const tc_funcs[])(void) = {
+ tc1_default_and_zero,
+ tc2_write_readback_and_boundary,
+ tc3_functional_burst_accounting,
+ tc4_preserved_after_quota_clear,
+ tc5_preserved_after_period_change,
+};
+
+static void run_test(unsigned int n)
+{
+ tc_funcs[n]();
+}
+
+static void setup(void)
+{
+ int dirfd;
+
+ dirfd = tst_cg_group_unified_dir_fd(tst_cg);
+ if (dirfd < 0)
+ tst_brk(TBROK, "tst_cg_group_unified_dir_fd failed");
+
+ if (faccessat(dirfd, "cpu.max.burst", F_OK, 0) != 0)
+ tst_brk(TCONF,
+ "cpu.max.burst not present -- kernel requires CONFIG_CFS_BANDWIDTH=y");
+
+ if (faccessat(dirfd, "cpu.stat", F_OK, 0) != 0)
+ tst_brk(TCONF, "cpu.stat not present");
+
+ tst_res(TINFO, "quota=%llu us burst=%llu us period=%llu us",
+ QUOTA_US, BURST_US, PERIOD_US);
+}
+
+static void cleanup(void)
+{
+ if (worker_pid >= 0)
+ worker_stop();
+
+ if (cmd_pipe[1] >= 0) {
+ SAFE_CLOSE(cmd_pipe[1]);
+ cmd_pipe[1] = -1;
+ }
+ if (ack_pipe[0] >= 0) {
+ SAFE_CLOSE(ack_pipe[0]);
+ ack_pipe[0] = -1;
+ }
+
+ if (cg_child)
+ cg_child = tst_cg_group_rm(cg_child);
+}
+
+static struct tst_test test = {
+ .tcnt = ARRAY_SIZE(tc_funcs),
+ .test = run_test,
+ .setup = setup,
+ .cleanup = cleanup,
+ .needs_root = 1,
+ .forks_child = 1,
+ .timeout = 60,
+ .taint_check = TST_TAINT_W | TST_TAINT_D,
+ .needs_kconfigs = (const char *[]) {
+ "CONFIG_CFS_BANDWIDTH",
+ NULL
+ },
+ .needs_cgroup_ver = TST_CG_V2,
+ .needs_cgroup_ctrls = (const char *const []) { "cpu", NULL },
+};
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
next prev parent reply other threads:[~2026-09-28 5:19 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 5:17 [LTP] [PATCH 0/2] cgroup: Add tests for cgroupv2 cpu.max.burst Samir Mulani
2026-09-28 5:17 ` Samir Mulani [this message]
2026-09-29 7:14 ` [LTP] cgroup: Add cgroup_burst01 test linuxtestproject.agent
2026-09-28 5:17 ` [LTP] [PATCH 2/2] cgroup: Add invalid burst test Samir Mulani
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=20260928051740.591819-2-samir@linux.ibm.com \
--to=samir@linux.ibm.com \
--cc=ltp@lists.linux.it \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.