[LTP] [PATCH v1 2/4] sched/psi: Add psi_multiple_trigger01 test
Samir Mulani
samir@linux.ibm.com
Sat Oct 24 04:30:42 CEST 2026
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
More information about the ltp
mailing list