* Re: [LTP] sched/psi: Add psi_invalid_trigger01 test
2026-10-24 2:30 ` [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test Samir Mulani
@ 2026-10-23 17:55 ` linuxtestproject.agent
0 siblings, 0 replies; 6+ messages in thread
From: linuxtestproject.agent @ 2026-10-23 17:55 UTC (permalink / raw)
To: Samir Mulani; +Cc: ltp
Hi Samir,
On Sat, 24 Oct 2026, Samir Mulani wrote:
> sched/psi: Add psi_invalid_trigger01 test
--- [PATCH 1/4] ---
> + .needs_kconfigs = (const char *const[]) {
> + "CONFIG_PSI=y",
Medium: PSI statistics exist in Linux 4.20-5.1, but trigger writes were
introduced in 5.2, so CONFIG_PSI and the setup read do not establish
monitor support. Add .min_kver = "5.2" or equivalent runtime detection
to avoid failing on supported kernels without this interface.
> + TEST(write(fd, tc->input, len));
> +
> + if (TST_RET == (long)len)
> + tst_res(TPASS, "%s: trigger accepted", tc->desc);
Low: Use TST_EXP_VAL(write(fd, tc->input, len), (long)len,
"%s", tc->desc) instead of the manual return-value reporting block.
> + .needs_root = 1,
> + .caps = (struct tst_cap []) {
> + TST_CAP(TST_CAP_REQ, CAP_SYS_RESOURCE),
Low: Document the root/CAP_SYS_RESOURCE requirement for custom trigger
windows in the high-level test description, not only the commit message.
> diff --git a/testcases/kernel/sched/psi/Makefile b/testcases/kernel/sched/psi/Makefile
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
Low: Add the required copyright line after the SPDX tag.
--- [PATCH 2/4] ---
> +static struct tst_test test = {
> + .tcnt = ARRAY_SIZE(tests),
> + .test = run,
> + .setup = setup,
Medium: This test also needs a monitor-availability gate for PSI-enabled
4.20-5.1 kernels, where setup succeeds but trigger writes are unsupported.
Use .min_kver = "5.2" or equivalent runtime detection.
> + TST_EXP_FAIL2(WRITE_TRIG(fd, TRIG_B), EBUSY,
> + "second write to the same fd");
Medium: This checks the behavior introduced by kernel fix a06247c6804f
("psi: Fix uaf issue when psi trigger is destroyed while being polled").
Add a linux-git regression tag for that fix and explain the relationship
in the commit message; do not skip kernels merely because they lack
the fix.
> +static void (*const tests[])(void) = {
> + first_write_succeeds,
> + second_write_ebusy,
> + dup_fd_shares_trigger,
Low: Use the required struct tcase table and common .test handler,
parameterizing trigger strings, descriptor sharing and expected results
instead of dispatching separate per-case functions.
> + .needs_root = 1,
Low: Explain the writable PSI interface and custom-window privilege
requirements in the high-level test description.
--- [PATCH 3/4] ---
> +#define PSI_LINE "%7s avg10=%lf avg60=%lf avg300=%lf total=%llu"
> [...]
> + SAFE_FCLOSE(fp);
> +
> + if (ret != 10) {
Medium: Supplying total=-1 and an unexpected third line still produces
seven passing results: %llu accepts the sign and the parser ignores
everything after the tenth conversion. Validate unsigned decimal totals,
consume each expected line completely, and reject extra records.
> + if (n < ARRAY_SIZE(paths))
> + verify_format(n);
> + else if (n < 2 * ARRAY_SIZE(paths))
> + verify_monotonic(n - ARRAY_SIZE(paths));
> + else
> + verify_cpu_full_zero();
Low: Replace the index-range dispatch with a struct tcase table describing
each path and check, using the common .test callback.
--- [PATCH 4/4] ---
> + tst_clock_gettime(CLOCK_MONOTONIC, &start);
> +
> + if (pwrite(fd, io_buf, IO_SIZE, 0) != IO_SIZE)
> + tst_brk(TBROK | TERRNO, "pwrite() failed");
> +
> + tst_clock_gettime(CLOCK_MONOTONIC, &end);
> + *io_wait_us += tst_timespec_diff_us(end, start);
High: This measures syscall wall time, including CPU work and preemption,
not time blocked on I/O. An O_DIRECT shmem reproduction measured 1995579us
with zero voluntary context switches, yet the test would require 498894us
of I/O pressure; LTP_DEV_FS_TYPE=tmpfs reaches this no-storage path.
Measure actual blocked-I/O time independently or use a controlled
blocking-I/O workload before deriving the expected PSI delta.
> +static char io_buf[IO_SIZE] __attribute__((aligned(4096)));
Medium: A fixed 4KiB alignment is insufficient on supported 64KiB-page
systems with 64KiB-sector Btrfs, which falls back to buffered writes for
this misalignment and invalidates the workload. Allocate a buffer using
the runtime DIO alignment or a suitable runtime page alignment; opening
the file successfully does not validate buffer alignment.
> + for (i = 0; i < 2; i++) {
> + if (!SAFE_FORK()) {
> + join_cgroup();
> + pin_to_cpu(pin_cpu);
> +
> + tst_timer_start(CLOCK_MONOTONIC);
Medium: If the parent is delayed between forks for two seconds, the first
worker can finish before the second joins, producing serial workloads
and a false failure on an otherwise idle CPU. Use checkpoints to wait
until both workers have joined and pinned themselves, then release both
before starting their workload timers.
> + if (pwrite(fd, io_buf, IO_SIZE, 0) != IO_SIZE)
> + tst_brk(TBROK | TERRNO, "pwrite() failed");
Low: Use SAFE_PWRITE(SAFE_WRITE_ALL, fd, io_buf, IO_SIZE, 0) for this
workload operation; the subject under test is PSI accounting, not pwrite.
> +static void (*const tests[])(void) = {
> + verify_cpu,
> + verify_memory,
> + verify_io,
Low: Use a struct tcase table and common .test handler rather than
dispatching separate per-case functions.
> + .needs_root = 1,
Low: Document the cgroup/controller and test-filesystem setup as the
reason for requiring root in the high-level description.
> diff --git a/testcases/kernel/sched/psi/.gitignore b/testcases/kernel/sched/psi/.gitignore
> /psi_proc_format01
> +/psi_accounting01
Low: Keep the entries sorted; /psi_accounting01 belongs first.
Verdict - Needs revision
---
Note:
The agent can sometimes produce false positives although often its
findings are genuine. If you find issues with the review, please
comment this email or ignore the suggestions.
Regards,
LTP AI Reviewer
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 6+ messages in thread
* [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface
@ 2026-10-24 2:30 Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test Samir Mulani
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Samir Mulani @ 2026-10-24 2:30 UTC (permalink / raw)
To: ltp; +Cc: Samir Mulani
This patch series adds four new test cases for the Linux kernel's
Pressure Stall Information (PSI) interface (/proc/pressure/{cpu,memory,io}).
Currently, LTP lacks dedicated test coverage for PSI functionality, trigger
parsing rules, and stall accounting. These tests ensure kernel compliance
and validate PSI behavior.
Test series breakdown:
1. psi_invalid_trigger01 (1/4):
Tests trigger input validation in /proc/pressure/memory. Checks that
invalid trigger strings return EINVAL and valid boundary values succeed.
2. psi_multiple_trigger01 (2/4):
Tests that only one trigger is allowed per open file description,
and second triggers on the same or dup()'d file descriptor fail with EBUSY.
3. psi_proc_format01 (3/4):
Validates the file format of /proc/pressure/{cpu,memory,io}, ensuring
percentages are between 0 and 100, totals never decrease, and CPU full
stall reports 0.00.
4. psi_accounting01 (4/4):
Validates real stall accounting within isolated cgroups (cgroup v2)
for CPU contention, memory throttling via memory.high, and O_DIRECT
synchronous block I/O stalls.
Samir Mulani (4):
sched/psi: Add psi_invalid_trigger01 test
sched/psi: Add psi_multiple_trigger01 test
sched/psi: Add psi_proc_format01 test
sched/psi: Add psi_accounting01 test
runtest/sched | 5 +
testcases/kernel/sched/psi/.gitignore | 4 +
testcases/kernel/sched/psi/Makefile | 7 +
testcases/kernel/sched/psi/psi_accounting01.c | 356 ++++++++++++++++++
.../kernel/sched/psi/psi_invalid_trigger01.c | 135 +++++++
.../kernel/sched/psi/psi_multiple_trigger01.c | 172 +++++++++
.../kernel/sched/psi/psi_proc_format01.c | 191 ++++++++++
7 files changed, 870 insertions(+)
create mode 100644 testcases/kernel/sched/psi/.gitignore
create mode 100644 testcases/kernel/sched/psi/Makefile
create mode 100644 testcases/kernel/sched/psi/psi_accounting01.c
create mode 100644 testcases/kernel/sched/psi/psi_invalid_trigger01.c
create mode 100644 testcases/kernel/sched/psi/psi_multiple_trigger01.c
create mode 100644 testcases/kernel/sched/psi/psi_proc_format01.c
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 6+ messages in thread
* [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test
2026-10-24 2:30 [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface Samir Mulani
@ 2026-10-24 2:30 ` Samir Mulani
2026-10-23 17:55 ` [LTP] " linuxtestproject.agent
2026-10-24 2:30 ` [LTP] [PATCH v1 2/4] sched/psi: Add psi_multiple_trigger01 test Samir Mulani
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Samir Mulani @ 2026-10-24 2:30 UTC (permalink / raw)
To: ltp; +Cc: Samir Mulani
Add a test case to verify trigger input validation in /proc/pressure/memory.
In the kernel (kernel/sched/psi.c), the trigger parsing logic (psi_trigger_create)
is identical and shared across /proc/pressure/{cpu,memory,io}. Therefore,
testing /proc/pressure/memory validates the parser for all PSI interfaces.
The test checks that invalid trigger strings return -EINVAL:
- Invalid trigger state (not "some" or "full")
- Missing threshold or window values
- Non-numeric or negative numbers
- Zero threshold or zero window
- Threshold larger than window
- Window larger than the 10 second maximum
It also checks that valid boundary values succeed:
- Smallest threshold (1us)
- Threshold equal to window
- Valid windows of 500ms, 2s, and 10s
- Valid "full" state
Note: The kernel overwrites the last byte of the input buffer with '\0',
so we write strlen() + 1 bytes to prevent losing the last character.
Requires CAP_SYS_RESOURCE for custom window sizes. Reports TCONF if
CONFIG_PSI is not enabled or disabled with psi=0.
This patch also adds the testcases/kernel/sched/psi directory, Makefile,
and .gitignore.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
runtest/sched | 2 +
testcases/kernel/sched/psi/.gitignore | 1 +
testcases/kernel/sched/psi/Makefile | 7 +
.../kernel/sched/psi/psi_invalid_trigger01.c | 135 ++++++++++++++++++
4 files changed, 145 insertions(+)
create mode 100644 testcases/kernel/sched/psi/.gitignore
create mode 100644 testcases/kernel/sched/psi/Makefile
create mode 100644 testcases/kernel/sched/psi/psi_invalid_trigger01.c
diff --git a/runtest/sched b/runtest/sched
index ecedd1a92..cc6e703d7 100644
--- a/runtest/sched
+++ b/runtest/sched
@@ -20,3 +20,5 @@ sched_stress sched_stress.sh
autogroup01 autogroup01
sched_football sched_football
+
+psi_invalid_trigger01 psi_invalid_trigger01
diff --git a/testcases/kernel/sched/psi/.gitignore b/testcases/kernel/sched/psi/.gitignore
new file mode 100644
index 000000000..1d615553a
--- /dev/null
+++ b/testcases/kernel/sched/psi/.gitignore
@@ -0,0 +1 @@
+/psi_invalid_trigger01
diff --git a/testcases/kernel/sched/psi/Makefile b/testcases/kernel/sched/psi/Makefile
new file mode 100644
index 000000000..18896b6f2
--- /dev/null
+++ b/testcases/kernel/sched/psi/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/sched/psi/psi_invalid_trigger01.c b/testcases/kernel/sched/psi/psi_invalid_trigger01.c
new file mode 100644
index 000000000..c532d1ae4
--- /dev/null
+++ b/testcases/kernel/sched/psi/psi_invalid_trigger01.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM Corp.
+ * Author: Samir Mulani <samir@linux.ibm.com>
+ */
+
+/*\
+ * Verify that the PSI trigger parser rejects invalid trigger definitions
+ * written to /proc/pressure/memory with EINVAL and accepts valid ones.
+ *
+ * Each sub-case opens a fresh file descriptor, because only one trigger can
+ * be registered per open file description (a second write fails with EBUSY).
+ *
+ * Rejected with EINVAL:
+ *
+ * - Invalid state keyword: "invalid 100000 1000000"
+ * - Only state keyword: "some"
+ * - Missing window value: "some 100000"
+ * - Non-numeric threshold: "some abc 1000000"
+ * - Non-numeric window: "some 100000 abc"
+ * - Negative threshold: "some -1 1000000"
+ * - Zero threshold: "some 0 1000000"
+ * - Zero window: "some 100000 0"
+ * - Threshold greater than window: "some 1000001 1000000"
+ * - Window greater than the 10s maximum: "some 100000 10000001"
+ * - Window far above the maximum: "some 100000 4294967295"
+ *
+ * Accepted, the write returns the number of bytes written:
+ *
+ * - Smallest threshold: "some 1 1000000"
+ * - Documented minimum window of 500ms: "some 100000 500000"
+ * - Window of 2s: "some 100000 2000000"
+ * - Threshold equal to window: "some 1000000 1000000"
+ * - Window equal to the 10s maximum: "some 100000 10000000"
+ * - The "full" state: "full 100000 1000000"
+ *
+ * The documentation says that windows below 500ms are not accepted, but the
+ * kernel does not enforce a lower limit, so the test does not check for it.
+ */
+
+#include <string.h>
+
+#include "tst_test.h"
+
+#define PSI_MEM "/proc/pressure/memory"
+
+static struct tcase {
+ const char *desc;
+ const char *input;
+ int exp_errno; /* 0 = expect success */
+} tcases[] = {
+ {"invalid state keyword", "invalid 100000 1000000", EINVAL},
+ {"only state keyword", "some", EINVAL},
+ {"missing window value", "some 100000", EINVAL},
+ {"non-numeric threshold", "some abc 1000000", EINVAL},
+ {"non-numeric window", "some 100000 abc", EINVAL},
+ {"negative threshold", "some -1 1000000", EINVAL},
+ {"zero threshold", "some 0 1000000", EINVAL},
+ {"zero window", "some 100000 0", EINVAL},
+ {"threshold greater than window", "some 1000001 1000000", EINVAL},
+ {"window greater than 10s maximum", "some 100000 10000001", EINVAL},
+ {"window far above 10s maximum", "some 100000 4294967295", EINVAL},
+ {"smallest threshold", "some 1 1000000", 0},
+ {"500ms window", "some 100000 500000", 0},
+ {"2s window", "some 100000 2000000", 0},
+ {"threshold equal to window", "some 1000000 1000000", 0},
+ {"window equal to 10s maximum", "some 100000 10000000", 0},
+ {"full state", "full 100000 1000000", 0},
+};
+
+static void run(unsigned int n)
+{
+ struct tcase *tc = &tcases[n];
+ /*
+ * The kernel overwrites the last byte of the written buffer with
+ * a NUL, so the terminating '\0' has to be part of the write.
+ */
+ size_t len = strlen(tc->input) + 1;
+ int fd;
+
+ fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ if (tc->exp_errno) {
+ TST_EXP_FAIL2(write(fd, tc->input, len), tc->exp_errno,
+ "%s", tc->desc);
+ } else {
+ TEST(write(fd, tc->input, len));
+
+ if (TST_RET == (long)len)
+ tst_res(TPASS, "%s: trigger accepted", tc->desc);
+ else if (TST_RET == -1)
+ tst_res(TFAIL | TTERRNO, "%s: write failed", tc->desc);
+ else
+ tst_res(TFAIL, "%s: wrote %ld bytes, expected %zu",
+ tc->desc, TST_RET, len);
+ }
+
+ SAFE_CLOSE(fd);
+}
+
+static void setup(void)
+{
+ char buf[64];
+ int fd;
+
+ if (access(PSI_MEM, F_OK))
+ tst_brk(TCONF, "%s not available (CONFIG_PSI not set)", PSI_MEM);
+
+ fd = SAFE_OPEN(PSI_MEM, O_RDONLY);
+
+ /* psi=0 on the kernel command line makes the interface return EOPNOTSUPP */
+ if (read(fd, buf, sizeof(buf)) < 0) {
+ if (errno == EOPNOTSUPP)
+ tst_brk(TCONF, "PSI is disabled (psi=0 on kernel command line)");
+
+ tst_brk(TBROK | TERRNO, "read(%s) failed", PSI_MEM);
+ }
+
+ SAFE_CLOSE(fd);
+}
+
+static struct tst_test test = {
+ .tcnt = ARRAY_SIZE(tcases),
+ .test = run,
+ .setup = setup,
+ .needs_root = 1,
+ .caps = (struct tst_cap []) {
+ TST_CAP(TST_CAP_REQ, CAP_SYS_RESOURCE),
+ {}
+ },
+ .needs_kconfigs = (const char *const[]) {
+ "CONFIG_PSI=y",
+ NULL
+ },
+};
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [LTP] [PATCH v1 2/4] sched/psi: Add psi_multiple_trigger01 test
2026-10-24 2:30 [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test Samir Mulani
@ 2026-10-24 2:30 ` Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 3/4] sched/psi: Add psi_proc_format01 test Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 4/4] sched/psi: Add psi_accounting01 test Samir Mulani
3 siblings, 0 replies; 6+ messages in thread
From: Samir Mulani @ 2026-10-24 2:30 UTC (permalink / raw)
To: ltp; +Cc: Samir Mulani
Add a test case to check that only one PSI trigger can be registered
on a single open file description.
The test verifies that:
- Writing the first trigger to an open pressure file succeeds.
- Writing a second trigger to the same file descriptor fails with -EBUSY.
- Writing a second trigger through a dup() descriptor also fails with -EBUSY.
- Opening two separate file descriptors allows registering two independent
triggers without conflict.
The test requires CAP_SYS_RESOURCE and reports TCONF if PSI is disabled.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
runtest/sched | 1 +
testcases/kernel/sched/psi/.gitignore | 1 +
.../kernel/sched/psi/psi_multiple_trigger01.c | 172 ++++++++++++++++++
3 files changed, 174 insertions(+)
create mode 100644 testcases/kernel/sched/psi/psi_multiple_trigger01.c
diff --git a/runtest/sched b/runtest/sched
index cc6e703d7..b6dc478a0 100644
--- a/runtest/sched
+++ b/runtest/sched
@@ -22,3 +22,4 @@ autogroup01 autogroup01
sched_football sched_football
psi_invalid_trigger01 psi_invalid_trigger01
+psi_multiple_trigger01 psi_multiple_trigger01
diff --git a/testcases/kernel/sched/psi/.gitignore b/testcases/kernel/sched/psi/.gitignore
index 1d615553a..23a5e6567 100644
--- a/testcases/kernel/sched/psi/.gitignore
+++ b/testcases/kernel/sched/psi/.gitignore
@@ -1 +1,2 @@
/psi_invalid_trigger01
+/psi_multiple_trigger01
diff --git a/testcases/kernel/sched/psi/psi_multiple_trigger01.c b/testcases/kernel/sched/psi/psi_multiple_trigger01.c
new file mode 100644
index 000000000..5859bd580
--- /dev/null
+++ b/testcases/kernel/sched/psi/psi_multiple_trigger01.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM Corp.
+ * Author: Samir Mulani <samir@linux.ibm.com>
+ */
+
+/*\
+ * Verify that only one PSI trigger can be registered per open file
+ * description.
+ *
+ * Documentation/accounting/psi.rst states: "Write operations to a file
+ * descriptor with an already existing psi trigger will fail with EBUSY."
+ *
+ * - Writing a valid trigger to a freshly opened file succeeds.
+ * - Writing a second trigger through the same file descriptor fails with
+ * EBUSY.
+ * - Writing a second trigger through a dup() of the file descriptor fails
+ * with EBUSY, because both descriptors share one open file description.
+ * - Two separately opened files accept different triggers.
+ * - Two separately opened files accept the same trigger.
+ * - After the file holding a trigger is closed a new file accepts a trigger.
+ *
+ * The cases with separately opened files confirm that the limit is per open
+ * file description and not per trigger definition.
+ */
+
+#include <string.h>
+
+#include "tst_test.h"
+
+#define PSI_MEM "/proc/pressure/memory"
+#define TRIG_A "some 100000 1000000"
+#define TRIG_B "full 200000 2000000"
+
+/*
+ * The kernel overwrites the last byte of the written buffer with a NUL, so
+ * the terminating '\0' has to be part of the write.
+ */
+#define TRIG_LEN(trig) ((long)strlen(trig) + 1)
+#define WRITE_TRIG(fd, trig) write((fd), (trig), TRIG_LEN(trig))
+
+#define SET_TRIG(fd, trig) \
+ SAFE_WRITE(SAFE_WRITE_ALL, (fd), (trig), TRIG_LEN(trig))
+
+static void first_write_succeeds(void)
+{
+ int fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ TST_EXP_VAL(WRITE_TRIG(fd, TRIG_A), TRIG_LEN(TRIG_A),
+ "first write to a fresh fd");
+
+ SAFE_CLOSE(fd);
+}
+
+static void second_write_ebusy(void)
+{
+ int fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ SET_TRIG(fd, TRIG_A);
+
+ TST_EXP_FAIL2(WRITE_TRIG(fd, TRIG_B), EBUSY,
+ "second write to the same fd");
+
+ SAFE_CLOSE(fd);
+}
+
+static void dup_fd_shares_trigger(void)
+{
+ int fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+ int dup_fd = SAFE_DUP(fd);
+
+ SET_TRIG(fd, TRIG_A);
+
+ TST_EXP_FAIL2(WRITE_TRIG(dup_fd, TRIG_B), EBUSY,
+ "write to a dup() of an fd that has a trigger");
+
+ SAFE_CLOSE(dup_fd);
+ SAFE_CLOSE(fd);
+}
+
+static void two_fds_different_triggers(void)
+{
+ int fd1 = SAFE_OPEN(PSI_MEM, O_RDWR);
+ int fd2 = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ TST_EXP_VAL(WRITE_TRIG(fd1, TRIG_A), TRIG_LEN(TRIG_A),
+ "fd1 write trigger A");
+ TST_EXP_VAL(WRITE_TRIG(fd2, TRIG_B), TRIG_LEN(TRIG_B),
+ "fd2 write trigger B");
+
+ SAFE_CLOSE(fd1);
+ SAFE_CLOSE(fd2);
+}
+
+static void two_fds_same_trigger(void)
+{
+ int fd1 = SAFE_OPEN(PSI_MEM, O_RDWR);
+ int fd2 = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ TST_EXP_VAL(WRITE_TRIG(fd1, TRIG_A), TRIG_LEN(TRIG_A),
+ "fd1 write trigger A");
+ TST_EXP_VAL(WRITE_TRIG(fd2, TRIG_A), TRIG_LEN(TRIG_A),
+ "fd2 write trigger A again");
+
+ SAFE_CLOSE(fd1);
+ SAFE_CLOSE(fd2);
+}
+
+static void trigger_after_close(void)
+{
+ int fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ SET_TRIG(fd, TRIG_A);
+ SAFE_CLOSE(fd);
+
+ fd = SAFE_OPEN(PSI_MEM, O_RDWR);
+
+ TST_EXP_VAL(WRITE_TRIG(fd, TRIG_B), TRIG_LEN(TRIG_B),
+ "write a trigger after closing the previous fd");
+
+ SAFE_CLOSE(fd);
+}
+
+static void (*const tests[])(void) = {
+ first_write_succeeds,
+ second_write_ebusy,
+ dup_fd_shares_trigger,
+ two_fds_different_triggers,
+ two_fds_same_trigger,
+ trigger_after_close,
+};
+
+static void run(unsigned int n)
+{
+ tests[n]();
+}
+
+static void setup(void)
+{
+ char buf[64];
+ int fd;
+
+ if (access(PSI_MEM, F_OK))
+ tst_brk(TCONF, "%s not available (CONFIG_PSI not set)", PSI_MEM);
+
+ fd = SAFE_OPEN(PSI_MEM, O_RDONLY);
+
+ /* psi=0 on the kernel command line makes the interface return EOPNOTSUPP */
+ if (read(fd, buf, sizeof(buf)) < 0) {
+ if (errno == EOPNOTSUPP)
+ tst_brk(TCONF, "PSI is disabled (psi=0 on kernel command line)");
+
+ tst_brk(TBROK | TERRNO, "read(%s) failed", PSI_MEM);
+ }
+
+ SAFE_CLOSE(fd);
+}
+
+static struct tst_test test = {
+ .tcnt = ARRAY_SIZE(tests),
+ .test = run,
+ .setup = setup,
+ .needs_root = 1,
+ .caps = (struct tst_cap []) {
+ TST_CAP(TST_CAP_REQ, CAP_SYS_RESOURCE),
+ {}
+ },
+ .needs_kconfigs = (const char *const[]) {
+ "CONFIG_PSI=y",
+ NULL
+ },
+};
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [LTP] [PATCH v1 3/4] sched/psi: Add psi_proc_format01 test
2026-10-24 2:30 [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 2/4] sched/psi: Add psi_multiple_trigger01 test Samir Mulani
@ 2026-10-24 2:30 ` Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 4/4] sched/psi: Add psi_accounting01 test Samir Mulani
3 siblings, 0 replies; 6+ messages in thread
From: Samir Mulani @ 2026-10-24 2:30 UTC (permalink / raw)
To: ltp; +Cc: Samir Mulani
Add a test case to verify the format and values in /proc/pressure/{cpu,memory,io}.
The test checks that:
- Each file has valid "some" and "full" lines with avg10, avg60, avg300,
and total fields.
- Averages are valid percentages between 0.00 and 100.00.
- Total stall times do not decrease over time.
- /proc/pressure/cpu reports "full" lines as 0.00 (per Linux 5.13+ behavior).
This test does not generate heavy system load and only validates interface
parsing. It reports TCONF if PSI is disabled.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
runtest/sched | 1 +
testcases/kernel/sched/psi/.gitignore | 1 +
.../kernel/sched/psi/psi_proc_format01.c | 191 ++++++++++++++++++
3 files changed, 193 insertions(+)
create mode 100644 testcases/kernel/sched/psi/psi_proc_format01.c
diff --git a/runtest/sched b/runtest/sched
index b6dc478a0..0651fafd9 100644
--- a/runtest/sched
+++ b/runtest/sched
@@ -23,3 +23,4 @@ sched_football sched_football
psi_invalid_trigger01 psi_invalid_trigger01
psi_multiple_trigger01 psi_multiple_trigger01
+psi_proc_format01 psi_proc_format01
diff --git a/testcases/kernel/sched/psi/.gitignore b/testcases/kernel/sched/psi/.gitignore
index 23a5e6567..8d1083ff2 100644
--- a/testcases/kernel/sched/psi/.gitignore
+++ b/testcases/kernel/sched/psi/.gitignore
@@ -1,2 +1,3 @@
/psi_invalid_trigger01
/psi_multiple_trigger01
+/psi_proc_format01
diff --git a/testcases/kernel/sched/psi/psi_proc_format01.c b/testcases/kernel/sched/psi/psi_proc_format01.c
new file mode 100644
index 000000000..7467e13d5
--- /dev/null
+++ b/testcases/kernel/sched/psi/psi_proc_format01.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM Corp.
+ * Author: Samir Mulani <samir@linux.ibm.com>
+ */
+
+/*\
+ * Verify the format of the PSI files in /proc/pressure/.
+ *
+ * - /proc/pressure/{cpu,memory,io} have two lines labelled "some" and "full",
+ * each one carrying avg10, avg60, avg300 and total.
+ * - The averages are percentages, so they have to be between 0 and 100.
+ * - The totals never decrease between two consecutive reads.
+ * - The "full" line of /proc/pressure/cpu is always zero, the full stall
+ * state is undefined at the system level and is reported as zero for
+ * backward compatibility since Linux 5.13.
+ *
+ * The accounting of the counters under load is tested by psi_accounting01.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "tst_test.h"
+#include "tst_safe_stdio.h"
+
+#define PSI_CPU "/proc/pressure/cpu"
+#define PSI_MEM "/proc/pressure/memory"
+#define PSI_IO "/proc/pressure/io"
+
+struct psi_line {
+ char label[8];
+ double avg10;
+ double avg60;
+ double avg300;
+ unsigned long long total;
+};
+
+struct psi_snap {
+ struct psi_line some;
+ struct psi_line full;
+};
+
+#define PSI_LINE "%7s avg10=%lf avg60=%lf avg300=%lf total=%llu"
+
+static const char *const paths[] = {PSI_CPU, PSI_MEM, PSI_IO};
+
+/* Returns 0 if both lines were parsed, -1 otherwise */
+static int read_psi(const char *path, struct psi_snap *snap)
+{
+ FILE *fp = SAFE_FOPEN(path, "r");
+ int ret;
+
+ ret = fscanf(fp, PSI_LINE " " PSI_LINE,
+ snap->some.label, &snap->some.avg10, &snap->some.avg60,
+ &snap->some.avg300, &snap->some.total,
+ snap->full.label, &snap->full.avg10, &snap->full.avg60,
+ &snap->full.avg300, &snap->full.total);
+
+ SAFE_FCLOSE(fp);
+
+ if (ret != 10) {
+ tst_res(TFAIL, "%s: parsed %d/10 values", path, ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int avg_valid(double avg)
+{
+ /* A NaN fails both comparisons */
+ return avg >= 0.0 && avg <= 100.0;
+}
+
+static int line_valid(const char *path, const struct psi_line *line,
+ const char *label)
+{
+ if (strcmp(line->label, label)) {
+ tst_res(TFAIL, "%s: label is '%s', expected '%s'",
+ path, line->label, label);
+ return 0;
+ }
+
+ if (!avg_valid(line->avg10) || !avg_valid(line->avg60) ||
+ !avg_valid(line->avg300)) {
+ tst_res(TFAIL, "%s: '%s' averages out of range: %.2f %.2f %.2f",
+ path, label, line->avg10, line->avg60, line->avg300);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void verify_format(unsigned int n)
+{
+ struct psi_snap snap;
+
+ if (read_psi(paths[n], &snap))
+ return;
+
+ if (line_valid(paths[n], &snap.some, "some") &&
+ line_valid(paths[n], &snap.full, "full"))
+ tst_res(TPASS, "%s has the expected format", paths[n]);
+}
+
+static void verify_monotonic(unsigned int n)
+{
+ struct psi_snap first, second;
+ int i;
+
+ if (read_psi(paths[n], &first))
+ return;
+
+ for (i = 0; i < 10; i++) {
+ usleep(10000);
+
+ if (read_psi(paths[n], &second))
+ return;
+
+ if (second.some.total < first.some.total ||
+ second.full.total < first.full.total) {
+ tst_res(TFAIL,
+ "%s: a total decreased: some %llu -> %llu, full %llu -> %llu",
+ paths[n], first.some.total, second.some.total,
+ first.full.total, second.full.total);
+ return;
+ }
+
+ first = second;
+ }
+
+ tst_res(TPASS, "%s totals never decrease", paths[n]);
+}
+
+static void verify_cpu_full_zero(void)
+{
+ struct psi_snap snap;
+
+ if (read_psi(PSI_CPU, &snap))
+ return;
+
+ if (snap.full.total || snap.full.avg10 || snap.full.avg60 ||
+ snap.full.avg300)
+ tst_res(TFAIL, "%s full line is not zero: avg10=%.2f total=%llu",
+ PSI_CPU, snap.full.avg10, snap.full.total);
+ else
+ tst_res(TPASS, "%s full line is zero", PSI_CPU);
+}
+
+static void run(unsigned int n)
+{
+ if (n < ARRAY_SIZE(paths))
+ verify_format(n);
+ else if (n < 2 * ARRAY_SIZE(paths))
+ verify_monotonic(n - ARRAY_SIZE(paths));
+ else
+ verify_cpu_full_zero();
+}
+
+static void setup(void)
+{
+ char buf[64];
+ int fd;
+
+ if (access(PSI_CPU, F_OK))
+ tst_brk(TCONF, "%s not available (CONFIG_PSI not set)", PSI_CPU);
+
+ fd = SAFE_OPEN(PSI_CPU, O_RDONLY);
+
+ /* psi=0 on the kernel command line makes the interface return EOPNOTSUPP */
+ if (read(fd, buf, sizeof(buf)) < 0) {
+ if (errno == EOPNOTSUPP)
+ tst_brk(TCONF, "PSI is disabled (psi=0 on kernel command line)");
+
+ tst_brk(TBROK | TERRNO, "read(%s) failed", PSI_CPU);
+ }
+
+ SAFE_CLOSE(fd);
+}
+
+static struct tst_test test = {
+ .tcnt = 2 * ARRAY_SIZE(paths) + 1,
+ .test = run,
+ .setup = setup,
+ .min_kver = "5.13",
+ .needs_kconfigs = (const char *const[]) {
+ "CONFIG_PSI=y",
+ NULL
+ },
+};
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [LTP] [PATCH v1 4/4] sched/psi: Add psi_accounting01 test
2026-10-24 2:30 [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface Samir Mulani
` (2 preceding siblings ...)
2026-10-24 2:30 ` [LTP] [PATCH v1 3/4] sched/psi: Add psi_proc_format01 test Samir Mulani
@ 2026-10-24 2:30 ` Samir Mulani
3 siblings, 0 replies; 6+ messages in thread
From: Samir Mulani @ 2026-10-24 2:30 UTC (permalink / raw)
To: ltp; +Cc: Samir Mulani
Add a test case to verify that PSI correctly records stall time when
tasks experience CPU, memory, and I/O pressure.
The test creates a cgroup v2 to isolate the workload and checks that:
- Running two CPU-heavy tasks on a single CPU increases cpu "some" total.
- Touching memory above memory.high triggers reclaim and increases
memory "some" total.
- Doing synchronous O_DIRECT writes to a block device increases io "some"
total.
If the system cannot create enough memory or I/O pressure (for example, on
very fast storage or systems without a test block device), the test cleanly
reports TCONF instead of failing.
The test requires cgroup v2 with memory controller enabled.
Signed-off-by: Samir Mulani <samir@linux.ibm.com>
---
runtest/sched | 1 +
testcases/kernel/sched/psi/.gitignore | 1 +
testcases/kernel/sched/psi/psi_accounting01.c | 356 ++++++++++++++++++
3 files changed, 358 insertions(+)
create mode 100644 testcases/kernel/sched/psi/psi_accounting01.c
diff --git a/runtest/sched b/runtest/sched
index 0651fafd9..b6021411c 100644
--- a/runtest/sched
+++ b/runtest/sched
@@ -24,3 +24,4 @@ sched_football sched_football
psi_invalid_trigger01 psi_invalid_trigger01
psi_multiple_trigger01 psi_multiple_trigger01
psi_proc_format01 psi_proc_format01
+psi_accounting01 psi_accounting01
diff --git a/testcases/kernel/sched/psi/.gitignore b/testcases/kernel/sched/psi/.gitignore
index 8d1083ff2..67407cc1c 100644
--- a/testcases/kernel/sched/psi/.gitignore
+++ b/testcases/kernel/sched/psi/.gitignore
@@ -1,3 +1,4 @@
/psi_invalid_trigger01
/psi_multiple_trigger01
/psi_proc_format01
+/psi_accounting01
diff --git a/testcases/kernel/sched/psi/psi_accounting01.c b/testcases/kernel/sched/psi/psi_accounting01.c
new file mode 100644
index 000000000..8b08f5a14
--- /dev/null
+++ b/testcases/kernel/sched/psi/psi_accounting01.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2026 IBM Corp.
+ * Author: Samir Mulani <samir@linux.ibm.com>
+ */
+
+/*\
+ * Verify that the PSI counters of a cgroup grow when its tasks are stalled.
+ *
+ * The test reads cpu.pressure, memory.pressure and io.pressure of a cgroup of
+ * its own, so load generated by the rest of the system does not influence the
+ * result. For every resource a workload is run by tasks inside the cgroup and
+ * the "some" total is expected to grow:
+ *
+ * - cpu: two busy tasks are pinned to the same CPU, so one of them is always
+ * runnable but not running. The total has to grow by at least 50ms.
+ * - memory: a task keeps touching fresh anonymous pages above memory.high of
+ * the cgroup, which makes it reclaim and get throttled. If memory.events
+ * does not show any new "high" event the workload did not create the stall
+ * and the test reports TCONF. Otherwise the task has been accounted as
+ * stalled, so the total has to grow.
+ * - io: a task does O_DIRECT writes to a block device and has to wait for
+ * their completion. If the writes did not block long enough the test reports
+ * TCONF. Otherwise the total has to grow by a quarter of the time that the
+ * writes were blocked.
+ *
+ * The format of the files in /proc/pressure/ is checked by psi_proc_format01.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+#include "tst_test.h"
+#include "tst_timer.h"
+#include "tst_clocks.h"
+#include "tst_safe_file_at.h"
+#include "lapi/cpuset.h"
+
+#define PSI_PROBE "/proc/pressure/cpu"
+#define MNTPOINT "mntpoint"
+#define IO_FILE MNTPOINT "/psi_io"
+
+#define WORK_MS 2000
+#define CPU_MIN_DELTA_US 50000ULL
+#define MIN_IO_WAIT_US 200000LL
+/* Share of the measured IO wait that has to show up in io.pressure */
+#define IO_DELTA_DIVISOR 4
+
+#define MEM_HIGH (16 * 1024 * 1024)
+#define MEM_REGION (24 * 1024 * 1024)
+/*
+ * Memory above memory.high is throttled for up to two seconds after every
+ * batch of 64 pages (256KB), so the clock has to be checked after each one.
+ */
+#define MEM_STEP (256 * 1024)
+
+#define IO_SIZE (256 * 1024)
+
+static struct tst_cg_group *cg_psi;
+static int cg_fd = -1;
+static int pin_cpu;
+static long long *io_wait_us;
+
+static char io_buf[IO_SIZE] __attribute__((aligned(4096)));
+
+static unsigned long long read_some_total(const char *file)
+{
+ char buf[256];
+ unsigned long long total;
+
+ SAFE_FILE_READAT(cg_fd, file, buf, sizeof(buf));
+
+ if (sscanf(buf, "some avg10=%*f avg60=%*f avg300=%*f total=%llu",
+ &total) != 1)
+ tst_brk(TBROK, "Cannot parse %s: '%s'", file, buf);
+
+ return total;
+}
+
+static void check_delta(const char *file, unsigned long long before,
+ unsigned long long min_delta)
+{
+ unsigned long long after = read_some_total(file);
+
+ if (after < before) {
+ tst_res(TFAIL, "%s some total decreased: %llu -> %llu",
+ file, before, after);
+ return;
+ }
+
+ if (after - before >= min_delta) {
+ tst_res(TPASS, "%s some total grew by %llu us",
+ file, after - before);
+ } else {
+ tst_res(TFAIL,
+ "%s some total grew only by %llu us, expected at least %llu us",
+ file, after - before, min_delta);
+ }
+}
+
+static void pin_to_cpu(int cpu)
+{
+ long ncpus = tst_ncpus_max();
+ size_t size = CPU_ALLOC_SIZE(ncpus);
+ cpu_set_t *mask = CPU_ALLOC(ncpus);
+
+ if (!mask)
+ tst_brk(TBROK | TERRNO, "CPU_ALLOC() failed");
+
+ CPU_ZERO_S(size, mask);
+ CPU_SET_S(cpu, size, mask);
+
+ if (sched_setaffinity(0, size, mask))
+ tst_brk(TBROK | TERRNO, "sched_setaffinity(%d) failed", cpu);
+
+ CPU_FREE(mask);
+}
+
+static void join_cgroup(void)
+{
+ SAFE_CG_PRINTF(cg_psi, "cgroup.procs", "%d", getpid());
+}
+
+static void verify_cpu(void)
+{
+ unsigned long long before = read_some_total("cpu.pressure");
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (!SAFE_FORK()) {
+ join_cgroup();
+ pin_to_cpu(pin_cpu);
+
+ tst_timer_start(CLOCK_MONOTONIC);
+ while (!tst_timer_expired_ms(WORK_MS))
+ ;
+
+ exit(0);
+ }
+ }
+
+ tst_reap_children();
+ check_delta("cpu.pressure", before, CPU_MIN_DELTA_US);
+}
+
+static void verify_memory(void)
+{
+ unsigned long long before = read_some_total("memory.pressure");
+ long high_before, high_after;
+
+ /* The limit is only wanted here, it would throttle the other tests */
+ SAFE_CG_PRINTF(cg_psi, "memory.high", "%d", MEM_HIGH);
+ SAFE_CG_LINES_SCANF(cg_psi, "memory.events", "high %ld", &high_before);
+
+ if (!SAFE_FORK()) {
+ size_t off;
+ char *ptr;
+
+ join_cgroup();
+
+ ptr = SAFE_MMAP(NULL, MEM_REGION, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ tst_timer_start(CLOCK_MONOTONIC);
+
+ while (!tst_timer_expired_ms(WORK_MS)) {
+ for (off = 0; off < MEM_REGION; off += MEM_STEP) {
+ memset(ptr + off, 0xab, MEM_STEP);
+
+ if (tst_timer_expired_ms(WORK_MS))
+ break;
+ }
+
+ /* Drop the pages so that they have to be charged again */
+ if (madvise(ptr, MEM_REGION, MADV_DONTNEED))
+ tst_brk(TBROK | TERRNO, "madvise() failed");
+ }
+
+ exit(0);
+ }
+
+ tst_reap_children();
+
+ SAFE_CG_LINES_SCANF(cg_psi, "memory.events", "high %ld", &high_after);
+ SAFE_CG_PRINT(cg_psi, "memory.high", "max");
+
+ if (high_after <= high_before) {
+ tst_res(TCONF, "Workload did not exceed memory.high (high=%ld)",
+ high_after);
+ return;
+ }
+
+ /*
+ * A "high" event is raised right before the task enters the memory
+ * stall accounting, so any growth is enough at this point.
+ */
+ check_delta("memory.pressure", before, 1);
+}
+
+static void verify_io(void)
+{
+ unsigned long long before = read_some_total("io.pressure");
+
+ *io_wait_us = 0;
+
+ if (!SAFE_FORK()) {
+ struct timespec start, end;
+ int fd;
+
+ join_cgroup();
+
+ memset(io_buf, 0xcd, sizeof(io_buf));
+ fd = SAFE_OPEN(IO_FILE, O_WRONLY | O_CREAT | O_DIRECT, 0600);
+
+ tst_timer_start(CLOCK_MONOTONIC);
+
+ while (!tst_timer_expired_ms(WORK_MS)) {
+ tst_clock_gettime(CLOCK_MONOTONIC, &start);
+
+ if (pwrite(fd, io_buf, IO_SIZE, 0) != IO_SIZE)
+ tst_brk(TBROK | TERRNO, "pwrite() failed");
+
+ tst_clock_gettime(CLOCK_MONOTONIC, &end);
+ *io_wait_us += tst_timespec_diff_us(end, start);
+ }
+
+ SAFE_CLOSE(fd);
+ SAFE_UNLINK(IO_FILE);
+ exit(0);
+ }
+
+ tst_reap_children();
+
+ if (*io_wait_us < MIN_IO_WAIT_US) {
+ tst_res(TCONF, "The workload was blocked on IO for only %lld us",
+ *io_wait_us);
+ return;
+ }
+
+ check_delta("io.pressure", before, *io_wait_us / IO_DELTA_DIVISOR);
+}
+
+static void (*const tests[])(void) = {
+ verify_cpu,
+ verify_memory,
+ verify_io,
+};
+
+static void run(unsigned int n)
+{
+ tests[n]();
+}
+
+static void check_pressure_file(const char *file)
+{
+ if (faccessat(cg_fd, file, R_OK, 0))
+ tst_brk(TCONF | TERRNO, "%s is not available in the cgroup", file);
+}
+
+static void setup(void)
+{
+ char buf[64];
+ long i, ncpus = tst_ncpus_max();
+ size_t size = CPU_ALLOC_SIZE(ncpus);
+ cpu_set_t *mask = CPU_ALLOC(ncpus);
+ int fd;
+
+ if (!mask)
+ tst_brk(TBROK | TERRNO, "CPU_ALLOC() failed");
+
+ fd = SAFE_OPEN(PSI_PROBE, O_RDONLY);
+
+ /* psi=0 on the kernel command line makes the interface return EOPNOTSUPP */
+ if (read(fd, buf, sizeof(buf)) < 0) {
+ if (errno == EOPNOTSUPP)
+ tst_brk(TCONF, "PSI is disabled (psi=0 on kernel command line)");
+
+ tst_brk(TBROK | TERRNO, "read(%s) failed", PSI_PROBE);
+ }
+
+ SAFE_CLOSE(fd);
+
+ /* Pin the busy tasks to a CPU we are actually allowed to use */
+ if (sched_getaffinity(0, size, mask))
+ tst_brk(TBROK | TERRNO, "sched_getaffinity() failed");
+
+ for (i = 0; i < ncpus; i++) {
+ if (CPU_ISSET_S(i, size, mask))
+ break;
+ }
+
+ if (i == ncpus)
+ tst_brk(TBROK, "No usable CPU found");
+
+ pin_cpu = i;
+ CPU_FREE(mask);
+
+ cg_psi = tst_cg_group_mk(tst_cg, "psi");
+ cg_fd = tst_cg_group_unified_dir_fd(cg_psi);
+
+ if (cg_fd < 0)
+ tst_brk(TBROK, "Cannot get the directory of the cgroup");
+
+ check_pressure_file("cpu.pressure");
+ check_pressure_file("memory.pressure");
+ check_pressure_file("io.pressure");
+
+ /* Not every filesystem supports O_DIRECT */
+ fd = open(IO_FILE, O_WRONLY | O_CREAT | O_DIRECT, 0600);
+ if (fd < 0) {
+ if (errno == EINVAL)
+ tst_brk(TCONF, "O_DIRECT is not supported by the filesystem");
+
+ tst_brk(TBROK | TERRNO, "open(%s) failed", IO_FILE);
+ }
+ SAFE_CLOSE(fd);
+ SAFE_UNLINK(IO_FILE);
+
+ io_wait_us = SAFE_MMAP(NULL, sizeof(*io_wait_us), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+}
+
+static void cleanup(void)
+{
+ if (io_wait_us)
+ SAFE_MUNMAP(io_wait_us, sizeof(*io_wait_us));
+
+ if (cg_psi)
+ cg_psi = tst_cg_group_rm(cg_psi);
+}
+
+static struct tst_test test = {
+ .tcnt = ARRAY_SIZE(tests),
+ .test = run,
+ .setup = setup,
+ .cleanup = cleanup,
+ .forks_child = 1,
+ .needs_root = 1,
+ .timeout = 60,
+ .min_kver = "4.20",
+ .min_mem_avail = 256,
+ .needs_device = 1,
+ .mount_device = 1,
+ .mntpoint = MNTPOINT,
+ .needs_cgroup_ver = TST_CG_V2,
+ .needs_cgroup_ctrls = (const char *const[]) {"memory", NULL},
+ .needs_kconfigs = (const char *const[]) {
+ "CONFIG_PSI=y",
+ NULL
+ },
+};
+
--
2.53.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-23 17:55 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-24 2:30 [LTP] [PATCH v1 0/4] sched/psi: Add tests for PSI interface Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 1/4] sched/psi: Add psi_invalid_trigger01 test Samir Mulani
2026-10-23 17:55 ` [LTP] " linuxtestproject.agent
2026-10-24 2:30 ` [LTP] [PATCH v1 2/4] sched/psi: Add psi_multiple_trigger01 test Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 3/4] sched/psi: Add psi_proc_format01 test Samir Mulani
2026-10-24 2:30 ` [LTP] [PATCH v1 4/4] sched/psi: Add psi_accounting01 test Samir Mulani
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.