Linux cgroups development
 help / color / mirror / Atom feed
* [PATCH] sched/psi: add cpu_prio pressure metric for high-priority task stalls
@ 2026-08-11 10:46 shisiyuan
  2026-08-12 13:30 ` Peter Zijlstra
  0 siblings, 1 reply; 2+ messages in thread
From: shisiyuan @ 2026-08-11 10:46 UTC (permalink / raw)
  To: Johannes Weiner, Suren Baghdasaryan, Tejun Heo, Michal Koutny,
	Ingo Molnar, Juri Lelli, Vincent Guittot, Jonathan Corbet
  Cc: Shuah Khan, Peter Zijlstra, Dietmar Eggemann, Steven Rostedt,
	Ben Segall, Mel Gorman, Valentin Schneider, K Prateek Nayak,
	linux-doc, linux-kernel, cgroups, shisiyuan

From: shisiyuan <shisiyuan@lixiang.com>

Introduce a new PSI (Pressure Stall Information) indicator that
measures how much walltime priority-sensitive tasks spend waiting
for CPU, exposed as /proc/pressure/cpu_prio alongside the existing
io/memory/cpu/irq metrics.

A task is considered "high priority" when its static priority is
at or below CONFIG_PSI_TASK_PRIO_THLD (default 118). The scheduler
tracks a dedicated task count and state bit for such tasks,
mirroring the existing CPU SOME/FULL accounting, so CPU contention
affecting latency-sensitive workloads can be observed independently
of overall CPU pressure. Both 'some' and 'full' states are tracked;
'full' is undefined at the system level (always reported as zero),
same as regular CPU pressure, but is meaningful at the cgroup level,
where it reflects the share of time no high-priority task in that
cgroup is able to run.

To keep the metric accurate across priority changes, ENQUEUE_PSI/
DEQUEUE_PSI flags are added and set by set_user_nice(),
sched_setscheduler() and rt_mutex_setprio(), forcing PSI state to
be re-evaluated whenever a task's priority is adjusted rather than
only on enqueue/dequeue.

The priority threshold is exported as a Kconfig knob
(CONFIG_PSI_TASK_PRIO_THLD) so it can be tuned per platform without
touching source code.

Signed-off-by: shisiyuan <shisiyuan@lixiang.com>
---
 Documentation/accounting/psi.rst | 25 ++++++++--
 include/linux/psi_types.h        | 17 ++++++-
 init/Kconfig                     | 18 +++++++
 kernel/cgroup/cgroup.c           | 23 +++++++++
 kernel/sched/core.c              |  2 +-
 kernel/sched/psi.c               | 82 ++++++++++++++++++++++++++++++--
 kernel/sched/sched.h             |  3 ++
 kernel/sched/stats.h             | 12 ++++-
 kernel/sched/syscalls.c          |  4 +-
 9 files changed, 173 insertions(+), 13 deletions(-)

diff --git a/Documentation/accounting/psi.rst b/Documentation/accounting/psi.rst
index d455db3e5..c7ff168b0 100644
--- a/Documentation/accounting/psi.rst
+++ b/Documentation/accounting/psi.rst
@@ -35,7 +35,7 @@ Pressure interface
 ==================
 
 Pressure information for each resource is exported through the
-respective file in /proc/pressure/ -- cpu, memory, and io.
+respective file in /proc/pressure/ -- cpu, cpu_prio, memory, and io.
 
 The format is as such::
 
@@ -64,6 +64,25 @@ as well as medium and long term trends. The total absolute stall time
 spikes which wouldn't necessarily make a dent in the time averages,
 or to average trends over custom time frames.
 
+cpu_prio pressure
+-----------------
+
+/proc/pressure/cpu_prio reports CPU pressure experienced specifically
+by high priority tasks, using the same "some"/"full" definitions as
+above but restricted to tasks whose (static) priority is at or below
+CONFIG_PSI_TASK_PRIO_THLD (a Kconfig-tunable threshold, defaulting to
+118). This makes it possible to observe CPU contention affecting
+latency-sensitive workloads independently of, and without being
+diluted by, the overall CPU pressure reported by /proc/pressure/cpu.
+
+Just like regular CPU pressure, cpu_prio "full" is undefined at the
+system level (it is always reported as zero in
+/proc/pressure/cpu_prio), but it is meaningful at the cgroup level,
+where it represents the share of time in which every high-priority
+task in the cgroup is stalled on the CPU, i.e. no high-priority task
+in that cgroup is able to run. See "Cgroup2 interface" below for the
+corresponding cpu_prio.pressure file.
+
 Monitoring for pressure thresholds
 ==================================
 
@@ -181,8 +200,8 @@ Cgroup2 interface
 In a system with a CONFIG_CGROUPS=y kernel and the cgroup2 filesystem
 mounted, pressure stall information is also tracked for tasks grouped
 into cgroups. Each subdirectory in the cgroupfs mountpoint contains
-cpu.pressure, memory.pressure, and io.pressure files; the format is
-the same as the /proc/pressure/ files.
+cpu.pressure, cpu_prio.pressure, memory.pressure, and io.pressure
+files; the format is the same as the /proc/pressure/ files.
 
 Per-cgroup psi monitors can be specified and used the same way as
 system-wide ones.
diff --git a/include/linux/psi_types.h b/include/linux/psi_types.h
index dd10c2229..b0909a48c 100644
--- a/include/linux/psi_types.h
+++ b/include/linux/psi_types.h
@@ -15,6 +15,7 @@ enum psi_task_count {
 	NR_IOWAIT,
 	NR_MEMSTALL,
 	NR_RUNNING,
+	NR_HIGH_PRIO_RUNNING,
 	/*
 	 * For IO and CPU stalls the presence of running/oncpu tasks
 	 * in the domain means a partial rather than a full stall.
@@ -25,23 +26,32 @@ enum psi_task_count {
 	 * threads and memstall ones.
 	 */
 	NR_MEMSTALL_RUNNING,
-	NR_PSI_TASK_COUNTS = 4,
+	NR_PSI_TASK_COUNTS = 5,
 };
 
+#ifdef CONFIG_PSI_TASK_PRIO_THLD
+#define PSI_TASK_PRIO_THLD CONFIG_PSI_TASK_PRIO_THLD
+#else
+#define PSI_TASK_PRIO_THLD 118
+#endif
+
 /* Task state bitmasks */
 #define TSK_IOWAIT	(1 << NR_IOWAIT)
 #define TSK_MEMSTALL	(1 << NR_MEMSTALL)
 #define TSK_RUNNING	(1 << NR_RUNNING)
+#define TSK_HIGH_PRIO_RUNNING  (1 << NR_HIGH_PRIO_RUNNING)
 #define TSK_MEMSTALL_RUNNING	(1 << NR_MEMSTALL_RUNNING)
 
 /* Only one task can be scheduled, no corresponding task count */
 #define TSK_ONCPU	(1 << NR_PSI_TASK_COUNTS)
+#define TSK_HIGH_PRIO_ONCPU   (1 << (NR_PSI_TASK_COUNTS + 1))
 
 /* Resources that workloads could be stalled on */
 enum psi_res {
 	PSI_IO,
 	PSI_MEM,
 	PSI_CPU,
+	PSI_CPU_HIGH_PRIO,
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 	PSI_IRQ,
 #endif
@@ -61,6 +71,8 @@ enum psi_states {
 	PSI_MEM_FULL,
 	PSI_CPU_SOME,
 	PSI_CPU_FULL,
+	PSI_CPU_HIGH_PRIO_TASK_SOME,
+	PSI_CPU_HIGH_PRIO_TASK_FULL,
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 	PSI_IRQ_FULL,
 #endif
@@ -75,6 +87,9 @@ enum psi_states {
 /* Flag whether to re-arm avgs_work, see details in get_recent_times() */
 #define PSI_STATE_RESCHEDULE	(1 << (NR_PSI_STATES + 1))
 
+/* Use one bit in the state mask to track TSK_HIGH_PRIO_ONCPU */
+#define PSI_HIGH_PRIO_ONCPU	(1 << (NR_PSI_STATES + 2))
+
 enum psi_aggregators {
 	PSI_AVGS = 0,
 	PSI_POLL,
diff --git a/init/Kconfig b/init/Kconfig
index 10f2013b5..582c62822 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -760,6 +760,24 @@ config PSI_DEFAULT_DISABLED
 
 	  Say N if unsure.
 
+config PSI_TASK_PRIO_THLD
+	int "Priority threshold for PSI cpu_prio"
+	default 118
+	depends on PSI
+	help
+	  Tasks whose (static) priority value is less than or equal to
+	  this threshold are considered "high priority" for the purpose
+	  of the /proc/pressure/cpu_prio pressure stall metric.
+
+	  This metric tracks how much walltime high priority tasks are
+	  stalled waiting for CPU, which can be used to detect CPU
+	  contention affecting latency-sensitive workloads.
+
+	  The default value corresponds to tasks which are latency-sensitive.
+
+	  If unsure, leave at the default value.
+
+
 endmenu # "CPU/Task time and stats accounting"
 
 config CPU_ISOLATION
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index b5b461d44..5ae4378d3 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -3989,6 +3989,14 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
 	return psi_show(seq, psi, PSI_CPU);
 }
 
+static int cgroup_cpu_prio_pressure_show(struct seq_file *seq, void *v)
+{
+	struct cgroup *cgrp = seq_css(seq)->cgroup;
+	struct psi_group *psi = cgroup_psi(cgrp);
+
+	return psi_show(seq, psi, PSI_CPU_HIGH_PRIO);
+}
+
 static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
 			      size_t nbytes, enum psi_res res)
 {
@@ -4073,6 +4081,13 @@ static ssize_t cgroup_cpu_pressure_write(struct kernfs_open_file *of,
 	return pressure_write(of, buf, nbytes, PSI_CPU);
 }
 
+static ssize_t cgroup_cpu_prio_pressure_write(struct kernfs_open_file *of,
+					      char *buf, size_t nbytes,
+					      loff_t off)
+{
+	return pressure_write(of, buf, nbytes, PSI_CPU_HIGH_PRIO);
+}
+
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 static int cgroup_irq_pressure_show(struct seq_file *seq, void *v)
 {
@@ -5572,6 +5587,14 @@ static struct cftype cgroup_psi_files[] = {
 		.poll = cgroup_pressure_poll,
 		.release = cgroup_pressure_release,
 	},
+	{
+		.name = "cpu_prio.pressure",
+		.file_offset = offsetof(struct cgroup, psi_files[PSI_CPU_HIGH_PRIO]),
+		.seq_show = cgroup_cpu_prio_pressure_show,
+		.write = cgroup_cpu_prio_pressure_write,
+		.poll = cgroup_pressure_poll,
+		.release = cgroup_pressure_release,
+	},
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 	{
 		.name = "irq.pressure",
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c..1eae2f892 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -7627,7 +7627,7 @@ void rt_mutex_post_schedule(void)
 void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task)
 {
 	int prio, oldprio, queue_flag =
-		DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
+		DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK | DEQUEUE_PSI;
 	const struct sched_class *prev_class, *next_class;
 	struct rq_flags rf;
 	struct rq *rq;
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index e2e825dcd..888d966c2 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -243,6 +243,7 @@ void __init psi_init(void)
 static u32 test_states(unsigned int *tasks, u32 state_mask)
 {
 	const bool oncpu = state_mask & PSI_ONCPU;
+	const bool oncpu_high_prio = state_mask & PSI_HIGH_PRIO_ONCPU;
 
 	if (tasks[NR_IOWAIT]) {
 		state_mask |= BIT(PSI_IO_SOME);
@@ -259,6 +260,12 @@ static u32 test_states(unsigned int *tasks, u32 state_mask)
 	if (tasks[NR_RUNNING] > oncpu)
 		state_mask |= BIT(PSI_CPU_SOME);
 
+	if (tasks[NR_HIGH_PRIO_RUNNING] > oncpu_high_prio)
+		state_mask |= BIT(PSI_CPU_HIGH_PRIO_TASK_SOME);
+
+	if (tasks[NR_HIGH_PRIO_RUNNING] && !oncpu_high_prio)
+		state_mask |= BIT(PSI_CPU_HIGH_PRIO_TASK_FULL);
+
 	if (tasks[NR_RUNNING] && !oncpu)
 		state_mask |= BIT(PSI_CPU_FULL);
 
@@ -784,10 +791,16 @@ static void record_times(struct psi_group_cpu *groupc, u64 now)
 		groupc->times[PSI_CPU_SOME] += delta;
 		if (groupc->state_mask & (1 << PSI_CPU_FULL))
 			groupc->times[PSI_CPU_FULL] += delta;
+		if (groupc->state_mask & (1 << PSI_CPU_HIGH_PRIO_TASK_SOME)) {
+			groupc->times[PSI_CPU_HIGH_PRIO_TASK_SOME] += delta;
+			if (groupc->state_mask & (1 << PSI_CPU_HIGH_PRIO_TASK_FULL))
+				groupc->times[PSI_CPU_HIGH_PRIO_TASK_FULL] += delta;
+		}
 	}
 
 	if (groupc->state_mask & (1 << PSI_NONIDLE))
 		groupc->times[PSI_NONIDLE] += delta;
+
 }
 
 #define for_each_group(iter, group) \
@@ -813,11 +826,24 @@ static void psi_group_change(struct psi_group *group, int cpu,
 	if (unlikely(clear & TSK_ONCPU)) {
 		state_mask = 0;
 		clear &= ~TSK_ONCPU;
+		if (unlikely(clear & TSK_HIGH_PRIO_ONCPU))
+			clear &= ~TSK_HIGH_PRIO_ONCPU;
 	} else if (unlikely(set & TSK_ONCPU)) {
 		state_mask = PSI_ONCPU;
 		set &= ~TSK_ONCPU;
+		if (unlikely(set & TSK_HIGH_PRIO_ONCPU)) {
+			state_mask |= PSI_HIGH_PRIO_ONCPU;
+			set &= ~TSK_HIGH_PRIO_ONCPU;
+		}
 	} else {
-		state_mask = groupc->state_mask & PSI_ONCPU;
+		state_mask = groupc->state_mask & (PSI_ONCPU | PSI_HIGH_PRIO_ONCPU);
+		if (unlikely(clear & TSK_HIGH_PRIO_ONCPU)) {
+			state_mask &= ~PSI_HIGH_PRIO_ONCPU;
+			clear &= ~TSK_HIGH_PRIO_ONCPU;
+		} else if (unlikely(set & TSK_HIGH_PRIO_ONCPU)) {
+			state_mask |= PSI_HIGH_PRIO_ONCPU;
+			set &= ~TSK_HIGH_PRIO_ONCPU;
+		}
 	}
 
 	/*
@@ -934,7 +960,12 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next,
 	now = cpu_clock(cpu);
 
 	if (next->pid) {
-		psi_flags_change(next, 0, TSK_ONCPU);
+		int set = TSK_ONCPU;
+
+		if (next->prio <= PSI_TASK_PRIO_THLD)
+			set |= TSK_HIGH_PRIO_ONCPU;
+
+		psi_flags_change(next, 0, set);
 		/*
 		 * Set TSK_ONCPU on @next's cgroups. If @next shares any
 		 * ancestors with @prev, those will already have @prev's
@@ -947,7 +978,7 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next,
 				common = group;
 				break;
 			}
-			psi_group_change(group, cpu, 0, TSK_ONCPU, now, true);
+			psi_group_change(group, cpu, 0, set, now, true);
 		}
 	}
 
@@ -955,6 +986,9 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next,
 		int clear = TSK_ONCPU, set = 0;
 		bool wake_clock = true;
 
+		if (prev->prio <= PSI_TASK_PRIO_THLD)
+			clear |= TSK_HIGH_PRIO_ONCPU;
+
 		/*
 		 * When we're going to sleep, psi_dequeue() lets us
 		 * handle TSK_RUNNING, TSK_MEMSTALL_RUNNING and
@@ -963,6 +997,9 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next,
 		 */
 		if (sleep) {
 			clear |= TSK_RUNNING;
+			if (prev->prio <= PSI_TASK_PRIO_THLD)
+				clear |= TSK_HIGH_PRIO_RUNNING;
+
 			if (prev->in_memstall)
 				clear |= TSK_MEMSTALL_RUNNING;
 			if (prev->in_iowait)
@@ -995,6 +1032,16 @@ void psi_task_switch(struct task_struct *prev, struct task_struct *next,
 		 */
 		if ((prev->psi_flags ^ next->psi_flags) & ~TSK_ONCPU) {
 			clear &= ~TSK_ONCPU;
+			/*
+			 * Both usual and high-prio "ONCPU" bits are handled up to
+			 * the common ancestor already, above that only propagate
+			 * a change in high-prio ONCPU if next differs from prev.
+			 */
+			if (next->prio <= PSI_TASK_PRIO_THLD) {
+				clear &= ~TSK_HIGH_PRIO_ONCPU;
+				if (!(prev->prio <= PSI_TASK_PRIO_THLD))
+					set |= TSK_HIGH_PRIO_ONCPU;
+			}
 			for_each_group(group, common)
 				psi_group_change(group, cpu, clear, set, now, wake_clock);
 		}
@@ -1280,7 +1327,8 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res)
 		int w;
 
 		/* CPU FULL is undefined at the system level */
-		if (!(group == &psi_system && res == PSI_CPU && full)) {
+		if (!(group == &psi_system &&
+		      (res == PSI_CPU || res == PSI_CPU_HIGH_PRIO) && full)) {
 			for (w = 0; w < 3; w++)
 				avg[w] = group->avg[res * 2 + full][w];
 			total = div_u64(group->total[PSI_AVGS][res * 2 + full],
@@ -1550,6 +1598,11 @@ static int psi_cpu_show(struct seq_file *m, void *v)
 	return psi_show(m, &psi_system, PSI_CPU);
 }
 
+static int psi_cpu_prio_show(struct seq_file *m, void *v)
+{
+	return psi_show(m, &psi_system, PSI_CPU_HIGH_PRIO);
+}
+
 static int psi_io_open(struct inode *inode, struct file *file)
 {
 	return single_open(file, psi_io_show, NULL);
@@ -1565,6 +1618,11 @@ static int psi_cpu_open(struct inode *inode, struct file *file)
 	return single_open(file, psi_cpu_show, NULL);
 }
 
+static int psi_cpu_prio_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, psi_cpu_prio_show, NULL);
+}
+
 static ssize_t psi_write(struct file *file, const char __user *user_buf,
 			 size_t nbytes, enum psi_res res)
 {
@@ -1638,6 +1696,12 @@ static ssize_t psi_cpu_write(struct file *file, const char __user *user_buf,
 	return psi_write(file, user_buf, nbytes, PSI_CPU);
 }
 
+static ssize_t psi_cpu_prio_write(struct file *file, const char __user *user_buf,
+				  size_t nbytes, loff_t *ppos)
+{
+	return psi_write(file, user_buf, nbytes, PSI_CPU_HIGH_PRIO);
+}
+
 static __poll_t psi_fop_poll(struct file *file, poll_table *wait)
 {
 	struct seq_file *seq = file->private_data;
@@ -1680,6 +1744,15 @@ static const struct proc_ops psi_cpu_proc_ops = {
 	.proc_release	= psi_fop_release,
 };
 
+static const struct proc_ops psi_cpu_prio_proc_ops = {
+	.proc_open	= psi_cpu_prio_open,
+	.proc_read	= seq_read,
+	.proc_lseek	= seq_lseek,
+	.proc_write	= psi_cpu_prio_write,
+	.proc_poll	= psi_fop_poll,
+	.proc_release	= psi_fop_release,
+};
+
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 static int psi_irq_show(struct seq_file *m, void *v)
 {
@@ -1714,6 +1787,7 @@ static int __init psi_proc_init(void)
 		proc_create("pressure/io", 0666, NULL, &psi_io_proc_ops);
 		proc_create("pressure/memory", 0666, NULL, &psi_memory_proc_ops);
 		proc_create("pressure/cpu", 0666, NULL, &psi_cpu_proc_ops);
+		proc_create("pressure/cpu_prio", 0666, NULL, &psi_cpu_prio_proc_ops);
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
 		proc_create("pressure/irq", 0666, NULL, &psi_irq_proc_ops);
 #endif
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 56acf502b..4b6cce3e7 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2553,6 +2553,7 @@ extern const u32		sched_prio_to_wmult[40];
 #define DEQUEUE_MIGRATING	0x0010 /* Matches ENQUEUE_MIGRATING */
 #define DEQUEUE_DELAYED		0x0020 /* Matches ENQUEUE_DELAYED */
 #define DEQUEUE_CLASS		0x0040 /* Matches ENQUEUE_CLASS */
+#define DEQUEUE_PSI		0x0080 /* Matches ENQUEUE_PSI */
 
 #define DEQUEUE_SPECIAL		0x00010000
 #define DEQUEUE_THROTTLE	0x00020000
@@ -2565,6 +2566,8 @@ extern const u32		sched_prio_to_wmult[40];
 #define ENQUEUE_MIGRATING	0x0010
 #define ENQUEUE_DELAYED		0x0020
 #define ENQUEUE_CLASS		0x0040
+#define ENQUEUE_PSI		0x0080
+
 
 #define ENQUEUE_HEAD		0x00010000
 #define ENQUEUE_REPLENISH	0x00020000
diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h
index ebe0a7765..66f5cf16d 100644
--- a/kernel/sched/stats.h
+++ b/kernel/sched/stats.h
@@ -128,7 +128,8 @@ static inline void psi_enqueue(struct task_struct *p, int flags)
 		return;
 
 	/* Same runqueue, nothing changed for psi */
-	if (flags & ENQUEUE_RESTORE)
+	/* If change scheduler or priority (ENQUEUE_PSI), still update psi */
+	if ((flags & ENQUEUE_RESTORE) && !(flags & ENQUEUE_PSI))
 		return;
 
 	/* psi_sched_switch() will handle the flags */
@@ -145,15 +146,21 @@ static inline void psi_enqueue(struct task_struct *p, int flags)
 	} else if (flags & ENQUEUE_MIGRATED) {
 		/* CPU migration of runnable task */
 		set = TSK_RUNNING;
+		if (p->prio <= PSI_TASK_PRIO_THLD)
+			set |= TSK_HIGH_PRIO_RUNNING;
 		if (p->in_memstall)
 			set |= TSK_MEMSTALL | TSK_MEMSTALL_RUNNING;
+
 	} else {
 		/* Wakeup of new or sleeping task */
 		if (p->in_iowait)
 			clear |= TSK_IOWAIT;
 		set = TSK_RUNNING;
+		if (p->prio <= PSI_TASK_PRIO_THLD)
+			set |= TSK_HIGH_PRIO_RUNNING;
 		if (p->in_memstall)
 			set |= TSK_MEMSTALL_RUNNING;
+
 	}
 
 	psi_task_change(p, clear, set);
@@ -165,7 +172,8 @@ static inline void psi_dequeue(struct task_struct *p, int flags)
 		return;
 
 	/* Same runqueue, nothing changed for psi */
-	if (flags & DEQUEUE_SAVE)
+	/* If change scheduler or priority (DEQUEUE_PSI), still update psi */
+	if ((flags & DEQUEUE_SAVE) && !(flags & DEQUEUE_PSI))
 		return;
 
 	/*
diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c
index b215b0ead..5a4cfd0ba 100644
--- a/kernel/sched/syscalls.c
+++ b/kernel/sched/syscalls.c
@@ -85,7 +85,7 @@ void set_user_nice(struct task_struct *p, long nice)
 		return;
 	}
 
-	scoped_guard (sched_change, p, DEQUEUE_SAVE) {
+	scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_PSI) {
 		p->static_prio = NICE_TO_PRIO(nice);
 		set_load_weight(p, true);
 		old_prio = p->prio;
@@ -500,7 +500,7 @@ int __sched_setscheduler(struct task_struct *p,
 	struct balance_callback *head;
 	struct rq_flags rf;
 	int reset_on_fork;
-	int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
+	int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK | DEQUEUE_PSI;
 	struct rq *rq;
 	bool cpuset_locked = false;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] sched/psi: add cpu_prio pressure metric for high-priority task stalls
  2026-08-11 10:46 [PATCH] sched/psi: add cpu_prio pressure metric for high-priority task stalls shisiyuan
@ 2026-08-12 13:30 ` Peter Zijlstra
  0 siblings, 0 replies; 2+ messages in thread
From: Peter Zijlstra @ 2026-08-12 13:30 UTC (permalink / raw)
  To: shisiyuan
  Cc: Johannes Weiner, Suren Baghdasaryan, Tejun Heo, Michal Koutny,
	Ingo Molnar, Juri Lelli, Vincent Guittot, Jonathan Corbet,
	Shuah Khan, Dietmar Eggemann, Steven Rostedt, Ben Segall,
	Mel Gorman, Valentin Schneider, K Prateek Nayak, linux-doc,
	linux-kernel, cgroups, shisiyuan

On Tue, Aug 11, 2026 at 06:46:10PM +0800, shisiyuan wrote:
> From: shisiyuan <shisiyuan@lixiang.com>
> 
> Introduce a new PSI (Pressure Stall Information) indicator that
> measures how much walltime priority-sensitive tasks spend waiting
> for CPU, exposed as /proc/pressure/cpu_prio alongside the existing
> io/memory/cpu/irq metrics.
> 
> A task is considered "high priority" when its static priority is
> at or below CONFIG_PSI_TASK_PRIO_THLD (default 118). The scheduler
> tracks a dedicated task count and state bit for such tasks,
> mirroring the existing CPU SOME/FULL accounting, so CPU contention
> affecting latency-sensitive workloads can be observed independently
> of overall CPU pressure. Both 'some' and 'full' states are tracked;
> 'full' is undefined at the system level (always reported as zero),
> same as regular CPU pressure, but is meaningful at the cgroup level,
> where it reflects the share of time no high-priority task in that
> cgroup is able to run.
> 
> To keep the metric accurate across priority changes, ENQUEUE_PSI/
> DEQUEUE_PSI flags are added and set by set_user_nice(),
> sched_setscheduler() and rt_mutex_setprio(), forcing PSI state to
> be re-evaluated whenever a task's priority is adjusted rather than
> only on enqueue/dequeue.
> 
> The priority threshold is exported as a Kconfig knob
> (CONFIG_PSI_TASK_PRIO_THLD) so it can be tuned per platform without
> touching source code.

Yeah, I think not.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-12 13:31 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-11 10:46 [PATCH] sched/psi: add cpu_prio pressure metric for high-priority task stalls shisiyuan
2026-08-12 13:30 ` Peter Zijlstra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox