[LTP] [PATCH 1/2] cgroup: Add cgroup_burst01 test
Samir Mulani
samir@linux.ibm.com
Mon Sep 28 07:17:34 CEST 2026
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
More information about the ltp
mailing list