[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