* [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff
@ 2026-07-09 21:56 Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 01/12] sched/docs: Document cpu_preferred_mask and Preferred CPU concept Shrikanth Hegde
` (11 more replies)
0 siblings, 12 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Very briefly,
- Maintain set of CPUs which can be used by workload. It is denoted as
cpu_preferred_mask
- Periodically compute the steal time. If steal time is high/low based
on the thresholds, either reduce/increase the preferred CPUs. This is
handled in a new driver called steal_monitor
- If a CPU is marked as non-preferred, push the task running on it if
possible.
- Use this CPU state in wakeup and load balance to ensure tasks run
within preferred CPUs.
For more details on idea, problem statement and performance numbers,
please refer to cover-letter of v2[2] and OSPM talk[1].
*** Please review and provide your feedback!! ***
[1]:https://youtu.be/adxUKFPlOp0
[2] v2: https://lore.kernel.org/all/20260407191950.643549-1-sshegde@linux.ibm.com/#t
[3] v6: https://lore.kernel.org/all/20260701141654.500125-1-sshegde@linux.ibm.com/#t
Thank you very much for feedback so far. This has helped the code to
evolve towards a clear abstraction layers and get simplified.
Special thanks to Yury Norov for reviewing this and improving the series
significantly. Really appreciated.
Apologies in advance if I have missed addressing any
comments. If so would be purely accidental, not in any way intentional.
base commit:
tip/sched/core at 'commit 04998aa54848 ("sched/eevdf: Delayed dequeue task can't preempt")'
v6->v7:
- Make new driver steal_monitor into 4-5 patches. (Yury Norov)
- Define CONFIG_STEAL_MONITOR and Make it select CONFIG_PREFERRED_CPU
(Yury Norov)
- Make module parameters fixed at module load (Yury Norov)
- Make module parameters checks using set/get methods via module_param_cb - sashiko
- Simplify is_cpu_allowed. (Yury Norov)
- Added MAINTAINERS entry for new driver.
- Split nohz_full optimization into its own patch.
- Merged load balance patches.
- Use possible CPUs instead of active for steal value calculations.
- Drop __weak symbol for now. Once the need arises, framework can be
designed at that time. (Yury Norov)
- remove whitespace in scoped_guard (Yury Norov)
- remove class check in sched_push_current_non_preferred_cpu (Yury
Norov)
- Make empty stub to do { } while (0) in set_cpu_preferred
- Move is_migration_disabled check to sched_non_preferred_cpu_push_stop - Sashiko
- Increase the migration count only if rq changed - sashiko
- Add missing requeue work on early return - sashiko
- Use WARN_ON_ONCE for design checks instead of WARN_ON.
- Few updates to documentation, comments and changelogs.
Let me know if there is any critical information is missing
regarding new driver such as policy, documentation or missing
implementation. I have ensured checkpatch --strict is happy.
Shrikanth Hegde (12):
sched/docs: Document cpu_preferred_mask and Preferred CPU concept
cpumask: Introduce cpu_preferred_mask
sysfs: Add preferred CPU file
sched/core: Try to use a preferred CPU in is_cpu_allowed
sched/fair: Load balance only among preferred CPUs
sched/core: Push current task from non preferred CPU
sched/debug: Add migration stats due to non preferred CPUs
virt: Introduce steal monitor driver
virt/steal_monitor: Add control knobs for handling steal values
virt/steal_monitor: Provide functions for managing steal values
virt/steal_monitor: Act on steal time periodically and decide on
preferred CPUs
sched, virt/steal_monitor: Keep tick on for faster push on nohz_full
CPU
.../ABI/testing/sysfs-devices-system-cpu | 11 +
Documentation/driver-api/index.rst | 1 +
Documentation/driver-api/steal-monitor.rst | 111 ++++++++++
Documentation/scheduler/sched-arch.rst | 58 +++++
MAINTAINERS | 9 +
drivers/base/cpu.c | 8 +
drivers/virt/Kconfig | 2 +
drivers/virt/Makefile | 1 +
drivers/virt/steal_monitor/Kconfig | 18 ++
drivers/virt/steal_monitor/Makefile | 6 +
drivers/virt/steal_monitor/defaults.c | 107 ++++++++++
drivers/virt/steal_monitor/sm_core.c | 202 ++++++++++++++++++
drivers/virt/steal_monitor/sm_core.h | 37 ++++
include/linux/cpumask.h | 24 +++
include/linux/sched.h | 1 +
kernel/Kconfig.preempt | 3 +
kernel/cpu.c | 6 +
kernel/sched/core.c | 105 ++++++++-
kernel/sched/debug.c | 1 +
kernel/sched/fair.c | 11 +-
kernel/sched/sched.h | 20 ++
21 files changed, 737 insertions(+), 5 deletions(-)
create mode 100644 Documentation/driver-api/steal-monitor.rst
create mode 100644 drivers/virt/steal_monitor/Kconfig
create mode 100644 drivers/virt/steal_monitor/Makefile
create mode 100644 drivers/virt/steal_monitor/defaults.c
create mode 100644 drivers/virt/steal_monitor/sm_core.c
create mode 100644 drivers/virt/steal_monitor/sm_core.h
--
2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v7 01/12] sched/docs: Document cpu_preferred_mask and Preferred CPU concept
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 02/12] cpumask: Introduce cpu_preferred_mask Shrikanth Hegde
` (10 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc,
kernel test robot
Add documentation for new cpumask called cpu_preferred_mask. This could
help users in understanding what this mask is and the concept behind it.
Document how to enable it and implementation aspects of it.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202606180717.yNM0yb41-lkp@intel.com/
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
Documentation/scheduler/sched-arch.rst | 58 ++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/Documentation/scheduler/sched-arch.rst b/Documentation/scheduler/sched-arch.rst
index ed07efea7d02..82bc27c7daac 100644
--- a/Documentation/scheduler/sched-arch.rst
+++ b/Documentation/scheduler/sched-arch.rst
@@ -62,6 +62,64 @@ Your cpu_idle routines need to obey the following rules:
arch/x86/kernel/process.c has examples of both polling and
sleeping idle functions.
+Preferred CPUs
+==============
+
+In virtualised environments it is possible to overcommit CPU resources. i.e.
+the sum of virtual CPUs (vCPUs) of all VMs is greater than number of physical
+CPUs (pCPUs). Under such conditions when all or many VMs have high utilization,
+hypervisor won't be able to satisfy the CPU requirement and has to context
+switch within or across VMs. The hypervisor needs to preempt one vCPU to run
+another. This is called vCPU preemption. This is more expensive compared to
+task context switch within a vCPU.
+
+In such cases it is better that combined vCPU ask from all VMs is reduced
+by not using some of the vCPUs in each VM. vCPUs where workload can be safely
+scheduled which won't increase any contention for pCPU are called as
+"Preferred CPUs".
+
+Main design construct is preferred CPUs are always a subset of active CPUs.
+In most cases preferred CPUs will be same as active CPUs, when there is pCPU
+contention, Preferred CPUs will reduce based on the amount of steal time.
+When the pCPU contention goes away as indicated by steal time, Preferred CPUs
+will become same as active CPUs again. This is done by loading the
+steal_monitor driver available at drivers/virt/steal_monitor.
+
+For scheduling decisions such as wakeup, pushing the task etc, needs this
+CPU state info. This is maintained in cpu_preferred_mask.
+vCPUs which are not in cpu_preferred_mask should be treated as vCPUs which
+should not be used at this moment provided it doesn't break user affinity.
+
+This is achieved by:
+
+1. Selecting a preferred CPU at wakeup using fallback mechanism.
+2. Push the task away from non-preferred CPU at tick.
+3. Only select preferred CPUs for load balance.
+
+/sys/devices/system/cpu/preferred prints the current cpu_preferred_mask in
+cpulist format.
+
+Notes:
+
+1. This feature is available under CONFIG_PREFERRED_CPU. It is selected
+ by steal_monitor driver (CONFIG_STEAL_MONITOR=y). On enabling the driver,
+ CPU preferred state can change based on steal time. Without that driver,
+ preferred CPUs is same as active CPUs.
+
+2. This feature works for FAIR class only.
+
+3. A task pinned, which can't be moved to preferred CPUs will continue
+ to run based on its affinity. But no load balancing happens.
+
+4. Decision to use/not use is driven by kernel. Hence it shouldn't
+ break user affinities. One of the main reasons why CPU hotplug
+ or Isolated cpuset partitions was not a solution.
+
+5. This feature works best only when all the VMs enable the feature as
+ it is a co-operative scheme. If a specific VM doesn't enable this feature
+ it may end up with more CPUs than others, still should lead to better
+ performance when seen from system view.
+ Users who enable this driver must ensure it is enabled in all VMs.
Possible arch/ problems
=======================
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 02/12] cpumask: Introduce cpu_preferred_mask
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 01/12] sched/docs: Document cpu_preferred_mask and Preferred CPU concept Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 03/12] sysfs: Add preferred CPU file Shrikanth Hegde
` (9 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Provide cpu_preferred_mask infrastructure. Define get/set macros
which could be used to get/set CPU state as preferred.
PREFERRED_CPU config will be selected by the driver which handles
steal time values. It is going to set/clear preferred CPU state.
This driver will be called steal_monitor and it is introduced in
subsequent patches. It periodically samples the steal time and
decides on preferred CPU state.
A CPU is set to preferred when it becomes active. Later it may be
marked as non-preferred depending on steal time values with
steal_monitor being enabled.
Always maintain design construct of preferred is subset of active.
i.e. preferred ⊆ active ⊆ online ⊆ present ⊆ possible
With PREFERRED_CPU=n, ensure set_cpu_preferred is a nop and get
method returns the active state in that case.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- removed CONFIG_PREFERRED_CPU as user option.
- Use do { } while (0) for nop
include/linux/cpumask.h | 24 ++++++++++++++++++++++++
kernel/Kconfig.preempt | 3 +++
kernel/cpu.c | 6 ++++++
kernel/sched/core.c | 5 +++++
4 files changed, 38 insertions(+)
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index d3cda0544954..34d08a3d80e1 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -122,12 +122,20 @@ extern struct cpumask __cpu_enabled_mask;
extern struct cpumask __cpu_present_mask;
extern struct cpumask __cpu_active_mask;
extern struct cpumask __cpu_dying_mask;
+
+#ifdef CONFIG_PREFERRED_CPU
+extern struct cpumask __cpu_preferred_mask;
+#else
+#define __cpu_preferred_mask __cpu_active_mask
+#endif
+
#define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
#define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask)
#define cpu_enabled_mask ((const struct cpumask *)&__cpu_enabled_mask)
#define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask)
#define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask)
#define cpu_dying_mask ((const struct cpumask *)&__cpu_dying_mask)
+#define cpu_preferred_mask ((const struct cpumask *)&__cpu_preferred_mask)
extern atomic_t __num_online_cpus;
extern unsigned int __num_possible_cpus;
@@ -1164,6 +1172,12 @@ void init_cpu_possible(const struct cpumask *src);
#define set_cpu_active(cpu, active) assign_cpu((cpu), &__cpu_active_mask, (active))
#define set_cpu_dying(cpu, dying) assign_cpu((cpu), &__cpu_dying_mask, (dying))
+#ifdef CONFIG_PREFERRED_CPU
+#define set_cpu_preferred(cpu, preferred) assign_cpu((cpu), &__cpu_preferred_mask, (preferred))
+#else
+#define set_cpu_preferred(cpu, preferred) do { } while (0)
+#endif
+
void set_cpu_online(unsigned int cpu, bool online);
void set_cpu_possible(unsigned int cpu, bool possible);
@@ -1258,6 +1272,11 @@ static __always_inline bool cpu_dying(unsigned int cpu)
return cpumask_test_cpu(cpu, cpu_dying_mask);
}
+static __always_inline bool cpu_preferred(unsigned int cpu)
+{
+ return cpumask_test_cpu(cpu, cpu_preferred_mask);
+}
+
#else
#define num_online_cpus() 1U
@@ -1296,6 +1315,11 @@ static __always_inline bool cpu_dying(unsigned int cpu)
return false;
}
+static __always_inline bool cpu_preferred(unsigned int cpu)
+{
+ return cpu == 0;
+}
+
#endif /* NR_CPUS > 1 */
#define cpu_is_offline(cpu) unlikely(!cpu_online(cpu))
diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt
index 88c594c6d7fc..ed02e4431230 100644
--- a/kernel/Kconfig.preempt
+++ b/kernel/Kconfig.preempt
@@ -192,3 +192,6 @@ config SCHED_CLASS_EXT
For more information:
Documentation/scheduler/sched-ext.rst
https://github.com/sched-ext/scx
+
+config PREFERRED_CPU
+ bool
diff --git a/kernel/cpu.c b/kernel/cpu.c
index b3c8553d7bd6..376d297a6292 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -3103,6 +3103,11 @@ EXPORT_SYMBOL(__cpu_dying_mask);
atomic_t __num_online_cpus __read_mostly;
EXPORT_SYMBOL(__num_online_cpus);
+#ifdef CONFIG_PREFERRED_CPU
+struct cpumask __cpu_preferred_mask __read_mostly;
+EXPORT_SYMBOL_GPL(__cpu_preferred_mask);
+#endif
+
void init_cpu_present(const struct cpumask *src)
{
cpumask_copy(&__cpu_present_mask, src);
@@ -3160,6 +3165,7 @@ void __init boot_cpu_init(void)
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_active(cpu, true);
+ set_cpu_preferred(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 2e7cde033a31..a45f7c308329 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -8690,6 +8690,9 @@ int sched_cpu_activate(unsigned int cpu)
*/
sched_set_rq_online(rq, cpu);
+ /* preferred is subset of active and follows its state */
+ set_cpu_preferred(cpu, true);
+
return 0;
}
@@ -8703,6 +8706,8 @@ int sched_cpu_deactivate(unsigned int cpu)
if (ret)
return ret;
+ set_cpu_preferred(cpu, false);
+
/*
* Remove CPU from nohz.idle_cpus_mask to prevent participating in
* load balancing when not active
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 03/12] sysfs: Add preferred CPU file
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 01/12] sched/docs: Document cpu_preferred_mask and Preferred CPU concept Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 02/12] cpumask: Introduce cpu_preferred_mask Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 15:41 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 04/12] sched/core: Try to use a preferred CPU in is_cpu_allowed Shrikanth Hegde
` (8 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Add "preferred" file in /sys/devices/system/cpu
This offers
- User can quickly check which CPUs are marked as preferred at this
moment.
- Userspace algorithms irqbalance could use this mask to send irq into
preferred CPUs.
For example:
cat /sys/devices/system/cpu/online
0-719
cat /sys/devices/system/cpu/preferred
0-599 <<< Implies 0-599 are preferred for workloads and 600-719
should be avoided at this moment.
cat /sys/devices/system/cpu/preferred
0-719 <<< All CPUs are usable. There is no preference.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
Documentation/ABI/testing/sysfs-devices-system-cpu | 11 +++++++++++
drivers/base/cpu.c | 8 ++++++++
2 files changed, 19 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 82d10d556cc8..ac1dbb209cc7 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -806,3 +806,14 @@ Date: Nov 2022
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Description:
(RO) the list of CPUs that can be brought online.
+
+What: /sys/devices/system/cpu/preferred
+Date: July 2026
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description:
+ (RO) the list of preferred CPUs at this moment.
+ These are the only CPUs meant to be used at the moment.
+ Using CPU outside of the list could lead to more
+ contention of underlying physical CPU resource. Dynamically
+ changes based on steal time. With CONFIG_PREFERRED_CPU=n it
+ is same as active CPUs. See sched-arch.rst for more details.
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 19d288a3c80c..4ac990efee7c 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -391,6 +391,13 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
}
#endif
+static ssize_t preferred_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_preferred_mask));
+}
+static DEVICE_ATTR_RO(preferred);
+
const struct bus_type cpu_subsys = {
.name = "cpu",
.dev_name = "cpu",
@@ -532,6 +539,7 @@ static struct attribute *cpu_root_attrs[] = {
#ifdef CONFIG_GENERIC_CPU_AUTOPROBE
&dev_attr_modalias.attr,
#endif
+ &dev_attr_preferred.attr,
NULL
};
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 04/12] sched/core: Try to use a preferred CPU in is_cpu_allowed
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (2 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 03/12] sysfs: Add preferred CPU file Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 05/12] sched/fair: Load balance only among preferred CPUs Shrikanth Hegde
` (7 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
When possible, choose a preferred CPUs to pick.
This is essential to maintain user affinities when preferred
CPUs change. A task pinned on non-preferred CPU should continue
to run there, since this is non-user triggered.
If CPU is non-preferred and task can run on other CPUs which are
currently preferred, then choose those other CPUs instead.
This is decided by checking cpus_ptr and cpu_preferred_mask
intersect or not. If yes, task has other preferred CPUs, it can
run there instead.
Overhead is minimal when CPU is preferred.
Push task mechanism uses stopper thread which going to call
select_fallback_rq and use this mechanism to pick only a preferred CPU.
This takes care of wakeup path for FAIR tasks too.
is_cpu_allowed is called to ensure wakeup happens on preferred CPUs.
With that, additional checks in available_idle_cpu is not necessary.
For majority of the cases this would still keep select_fallback_rq
as O(N). task_has_preferred_cpus which is O(N) is called only if
!cpu_preferred. Then task running there is expected to move out.
So subsequent it should run on preferred CPU. This becomes O(N**2)
only for tasks pinned only non preferred CPUs. That is rare case.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- restructured to keep the diff minimal.
- removed bulky comments.
kernel/sched/core.c | 12 ++++++++++--
kernel/sched/sched.h | 12 ++++++++++++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index a45f7c308329..9e8eec4451b6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2509,8 +2509,12 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
return cpu_online(cpu);
/* Non kernel threads are not allowed during either online or offline. */
- if (!(p->flags & PF_KTHREAD))
+ if (!(p->flags & PF_KTHREAD)) {
+ /* Try to use preferred CPU if task's affinity allows */
+ if (task_can_sched_on_preferred(cpu, p))
+ return false;
return cpu_active(cpu);
+ }
/* KTHREAD_IS_PER_CPU is always allowed. */
if (kthread_is_per_cpu(p))
@@ -2520,7 +2524,11 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
if (cpu_dying(cpu))
return false;
- /* But are allowed during online. */
+ /* Try to keep unbound kthreads on a preferred CPU if possible. */
+ if (task_can_sched_on_preferred(cpu, p))
+ return false;
+
+ /* Otherwise, they are allowed to run on online CPU. */
return cpu_online(cpu);
}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 26ae13c86b69..6de6366f2faa 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -4230,4 +4230,16 @@ DEFINE_CLASS_IS_UNCONDITIONAL(sched_change)
#include "ext/ext.h"
+static inline bool task_can_sched_on_preferred(int cpu, struct task_struct *p)
+{
+ if (cpu_preferred(cpu))
+ return false;
+
+ /* Only FAIR tasks honor preferred CPU state */
+ if (unlikely(p->sched_class != &fair_sched_class))
+ return false;
+
+ return cpumask_intersects(p->cpus_ptr, cpu_preferred_mask);
+}
+
#endif /* _KERNEL_SCHED_SCHED_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 05/12] sched/fair: Load balance only among preferred CPUs
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (3 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 04/12] sched/core: Try to use a preferred CPU in is_cpu_allowed Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 06/12] sched/core: Push current task from non preferred CPU Shrikanth Hegde
` (6 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
When cpu is marked as non preferred, any load pulled towards it is
pointless since in the next tick task will be pushed out again.
So, Consider only preferred CPUs for load balance.
This makes it not fight against the push task mechanism which happens
at tick. Also, this stops active balance to happen on non-preferred CPU
pulling the load.
This means there is no load balancing if the task is pinned only to
non-preferred CPUs. They will continue to run where they were previously
running before the CPUs was marked as non-preferred.
Bailout early for NEWIDLE and IDLE balance as load balancing is done
only on preferred CPUs.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Merged two load balance related patches.
kernel/sched/fair.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index df8c9c2c7918..53a34daf78d7 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -13399,7 +13399,7 @@ static int sched_balance_rq(int this_cpu, struct rq *this_rq,
};
bool need_unlock = false;
- cpumask_and(cpus, sched_domain_span(sd), cpu_active_mask);
+ cpumask_and(cpus, sched_domain_span(sd), cpu_preferred_mask);
schedstat_inc(sd->lb_count[idle]);
@@ -14308,6 +14308,10 @@ static void _nohz_idle_balance(struct rq *this_rq, unsigned int flags)
if (!idle_cpu(balance_cpu))
continue;
+ /* There is no point in pulling the load, just to push it out next */
+ if (!cpu_preferred(balance_cpu))
+ continue;
+
/*
* If this CPU gets work to do, stop the load balancing
* work being done for other CPUs. Next load
@@ -14482,9 +14486,10 @@ static int sched_balance_newidle(struct rq *this_rq, struct rq_flags *rf)
this_rq->idle_stamp = rq_clock(this_rq);
/*
- * Do not pull tasks towards !active CPUs...
+ * Do not pull tasks towards !preferred CPUs...
+ * preferred is always a subset of active.
*/
- if (!cpu_active(this_cpu))
+ if (!cpu_preferred(this_cpu))
return 0;
/*
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 06/12] sched/core: Push current task from non preferred CPU
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (4 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 05/12] sched/fair: Load balance only among preferred CPUs Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 23:02 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 07/12] sched/debug: Add migration stats due to non preferred CPUs Shrikanth Hegde
` (5 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Actively push out task running on a non-preferred CPU. Since the task is
running on the CPU, need to stop the cpu and push the task out.
However, if the task is pinned only to non-preferred CPUs, it will continue
running there. This will help in maintaining the userspace affinities
unlike CPU hotplug or isolated cpusets.
Though code is similar to __balance_push_cpu_stop and quite close to
push_cpu_stop, it is being kept separate as it provides a cleaner
implementation with CONFIG_PREFERRED_CPU.
Add push_task_work_done flag to protect work buffer.
Works only with FAIR class.
For now, only current running task is pushed out. This keeps the code
simpler. In future optimization maybe done to move all the queued
task on the rq.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Moved is_migration_disabled
- removed fair class check
kernel/sched/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
kernel/sched/sched.h | 8 +++++
2 files changed, 86 insertions(+)
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 9e8eec4451b6..74c93a88bf84 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -5774,6 +5774,9 @@ void sched_tick(void)
unsigned long hw_pressure;
u64 resched_latency;
+ if (!cpu_preferred(cpu))
+ sched_push_current_non_preferred_cpu(rq);
+
if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
arch_scale_freq_tick();
@@ -11292,3 +11295,78 @@ void sched_change_end(struct sched_change_ctx *ctx)
p->sched_class->prio_changed(rq, p, ctx->prio);
}
}
+
+#ifdef CONFIG_PREFERRED_CPU
+static DEFINE_PER_CPU(struct cpu_stop_work, npc_push_task_work);
+
+static int sched_non_preferred_cpu_push_stop(void *arg)
+{
+ struct task_struct *p = arg;
+ struct rq *rq = this_rq();
+ struct rq_flags rf;
+ int cpu;
+
+ /* sanity checks and clear */
+ if (cpu_preferred(rq->cpu) || is_migration_disabled(p)) {
+ scoped_guard(rq_lock, rq)
+ rq->push_task_work_done = false;
+ put_task_struct(p);
+ return 0;
+ }
+
+ raw_spin_lock_irq(&p->pi_lock);
+
+ /* This could take rq lock. So call it before rq lock is taken */
+ cpu = select_fallback_rq(rq->cpu, p);
+ rq_lock(rq, &rf);
+ rq->push_task_work_done = false;
+ update_rq_clock(rq);
+
+ context_unsafe_alias(rq);
+
+ if (task_rq(p) == rq && task_on_rq_queued(p))
+ rq = __migrate_task(rq, &rf, p, cpu);
+
+ rq_unlock(rq, &rf);
+ raw_spin_unlock_irq(&p->pi_lock);
+ put_task_struct(p);
+
+ return 0;
+}
+
+/*
+ * Push the current task running on non-preferred CPU(npc).
+ * Using this non preferred CPU will lead to more vCPU preemptions
+ * in the host. So it is better not to use this CPU.
+ *
+ * Since task is running, call a stopper to push the task out. This is
+ * similar to how task moves during hotplug. In select_fallback_rq a
+ * preferred CPU will be chosen and henceforth task shouldn't come back to
+ * this CPU again.
+ *
+ * Works for FAIR class only
+ *
+ * If task is affined only non-preferred CPUs, it can't be moved out
+ */
+void sched_push_current_non_preferred_cpu(struct rq *rq)
+{
+ struct task_struct *push_task = rq->curr;
+
+ /* Don't push the task if task's affinity doesn't allow */
+ if (!task_can_sched_on_preferred(rq->cpu, push_task))
+ return;
+
+ /* There is already a stopper thread. Don't race with it. */
+ if (rq->push_task_work_done)
+ return;
+
+ /* sched_tick runs with interrupts disabled. */
+ get_task_struct(push_task);
+
+ scoped_guard(rq_lock, rq)
+ rq->push_task_work_done = true;
+
+ stop_one_cpu_nowait(rq->cpu, sched_non_preferred_cpu_push_stop,
+ push_task, this_cpu_ptr(&npc_push_task_work));
+}
+#endif
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 6de6366f2faa..80c02e2c09eb 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1277,6 +1277,8 @@ struct rq {
struct list_head cfs_tasks;
+ bool push_task_work_done;
+
struct sched_avg avg_rt;
struct sched_avg avg_dl;
#ifdef CONFIG_HAVE_SCHED_AVG_IRQ
@@ -4242,4 +4244,10 @@ static inline bool task_can_sched_on_preferred(int cpu, struct task_struct *p)
return cpumask_intersects(p->cpus_ptr, cpu_preferred_mask);
}
+#ifdef CONFIG_PREFERRED_CPU
+void sched_push_current_non_preferred_cpu(struct rq *rq);
+#else /* !CONFIG_PREFERRED_CPU */
+static inline void sched_push_current_non_preferred_cpu(struct rq *rq) { }
+#endif
+
#endif /* _KERNEL_SCHED_SCHED_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 07/12] sched/debug: Add migration stats due to non preferred CPUs
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (5 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 06/12] sched/core: Push current task from non preferred CPU Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 08/12] virt: Introduce steal monitor driver Shrikanth Hegde
` (4 subsequent siblings)
11 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Add a new stat,
- nr_migrations_cpu_non_preferred: number of migrations happened since
a CPU was marked as non preferred due to high steal time.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Check if rq indeed changed or not before increment.
include/linux/sched.h | 1 +
kernel/sched/core.c | 10 ++++++++--
kernel/sched/debug.c | 1 +
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 968b18a7f470..37849d2f1dbd 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -554,6 +554,7 @@ struct sched_statistics {
u64 nr_failed_migrations_running;
u64 nr_failed_migrations_hot;
u64 nr_forced_migrations;
+ u64 nr_migrations_cpu_non_preferred;
u64 nr_wakeups;
u64 nr_wakeups_sync;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 74c93a88bf84..1ca1eefbdaf9 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -11324,8 +11324,14 @@ static int sched_non_preferred_cpu_push_stop(void *arg)
context_unsafe_alias(rq);
- if (task_rq(p) == rq && task_on_rq_queued(p))
- rq = __migrate_task(rq, &rf, p, cpu);
+ if (task_rq(p) == rq && task_on_rq_queued(p)) {
+ struct rq *dest_rq;
+
+ dest_rq = __migrate_task(rq, &rf, p, cpu);
+ if (rq != dest_rq)
+ schedstat_inc(p->stats.nr_migrations_cpu_non_preferred);
+ rq = dest_rq;
+ }
rq_unlock(rq, &rf);
raw_spin_unlock_irq(&p->pi_lock);
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 72236db67983..5ebb2055e6d5 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -1446,6 +1446,7 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns,
P_SCHEDSTAT(nr_failed_migrations_running);
P_SCHEDSTAT(nr_failed_migrations_hot);
P_SCHEDSTAT(nr_forced_migrations);
+ P_SCHEDSTAT(nr_migrations_cpu_non_preferred);
P_SCHEDSTAT(nr_wakeups);
P_SCHEDSTAT(nr_wakeups_sync);
P_SCHEDSTAT(nr_wakeups_migrate);
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 08/12] virt: Introduce steal monitor driver
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (6 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 07/12] sched/debug: Add migration stats due to non preferred CPUs Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 1:27 ` Randy Dunlap
2026-07-10 20:53 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values Shrikanth Hegde
` (3 subsequent siblings)
11 siblings, 2 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Introduce a new driver in virt named steal_monitor. This driver
will compute the steal time and drive the policy decisions of preferred
CPU state.
More on it can be found in the Documentation/driver-api/steal-monitor.rst
There is a new kconfig called STEAL_MONITOR. Having that driver is going
to select PREFERRED_CPU. This makes configs driven by user preference.
It is recommended to build it as module and let user load the module.
When the module is disabled, preferred is same as active.
File layout of the driver is designed with having arch specific
files in the future.
- sm_core.c - contains main driver code. This includes the periodic
work function and take action on steal time.
- defaults.c - contains the functions used for handling steal values.
- sm_core.h - header file which includes data structure.
Main structure of steal monitor has,
- work: deferred periodic work function
- prev_steal, prev_time: To calculate the delta in periodic work.
- interval_ms, high_threshold, low_threshold: debug knobs of
steal_monitor.
- prev_direction: Simple direction control to avoid oscillations.
While there, Add MAINTAINERS entry for this new driver.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Combined all introductory patches.
- Introduce STEAL_MONITOR which selects PREFERRED_CPU.
- Added MAINTAINERS entry.
- Yury, I have kept you as reviewer entry, Let me know if it needs to
change.
Documentation/driver-api/index.rst | 1 +
Documentation/driver-api/steal-monitor.rst | 111 +++++++++++++++++++++
MAINTAINERS | 9 ++
drivers/virt/Kconfig | 2 +
drivers/virt/Makefile | 1 +
drivers/virt/steal_monitor/Kconfig | 18 ++++
drivers/virt/steal_monitor/Makefile | 6 ++
drivers/virt/steal_monitor/sm_core.c | 38 +++++++
drivers/virt/steal_monitor/sm_core.h | 27 +++++
9 files changed, 213 insertions(+)
create mode 100644 Documentation/driver-api/steal-monitor.rst
create mode 100644 drivers/virt/steal_monitor/Kconfig
create mode 100644 drivers/virt/steal_monitor/Makefile
create mode 100644 drivers/virt/steal_monitor/sm_core.c
create mode 100644 drivers/virt/steal_monitor/sm_core.h
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index eaf7161ff957..ec12f396a5e6 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -138,6 +138,7 @@ Subsystem-specific APIs
sm501
soundwire/index
spi
+ steal-monitor
surface_aggregator/index
switchtec
sync_file
diff --git a/Documentation/driver-api/steal-monitor.rst b/Documentation/driver-api/steal-monitor.rst
new file mode 100644
index 000000000000..94f4aa1aaa7d
--- /dev/null
+++ b/Documentation/driver-api/steal-monitor.rst
@@ -0,0 +1,111 @@
+.. SPDX-License-Identifier: GPL-2.0
+=============
+Steal Monitor
+=============
+
+:Author: Shrikanth Hegde
+
+Introduction
+============
+
+Steal monitor is a driver aimed at solving the Noisy Neighbour problem
+in virtualized environments. The performance of workload
+running in one VM gets affected significantly due to other VMs and
+combined they make slower forward progress.
+
+When there is overcommit of CPU resources, i.e. sum of virtual CPUs (vCPUs)
+of all VMs is greater than number of physical CPUs (pCPUs) and
+when all or many VMs have high utilization, hypervisor won't be able
+to satisfy the CPU requirement and has to context switch within or
+across VMs. I.e. the hypervisor needs to preempt one vCPU to run
+another. This is called vCPU preemption.
+This is more expensive compared to task context switch within a vCPU.
+
+In such cases it is better that combined vCPU ask from all VMs is reduced
+by not using some of the vCPUs. vCPUs where workload can be safely
+scheduled which won't increase any contention for pCPU are called as
+"Preferred CPUs".
+
+See more on "Preferred CPUs" in Documentation/scheduler/sched-arch.rst.
+
+This driver makes CONFIG_PREFERRED_CPU=y which enables the scheduler core
+infrastructure to move tasks to Preferred CPUs where possible.
+
+Core idea
+=========
+steal time is an indication available today in Guest which shows contention
+for underlying physical CPU. Use it as a hint in the guest to fold the
+workload to a reduced set of vCPUs. When there is contention, steal time
+will show up in all the guests. When each guest honors the hint and folds
+the workload to a smaller set of vCPUs (Preferred CPUs), it reduces the
+contention and thereby reduces vCPU preemption.
+This is achieved without any cross-guest communication.
+
+Steal monitor driver effectively does:
+
+1. Periodically computes steal time across the system.
+
+2. If steal time is greater than high threshold, reduce the number of
+ preferred CPUs by 1 core. Ensure at least one core is left always.
+ This avoids running into extreme cases.
+
+3. If steal time is lower or equal to low threshold, increase the
+ number of preferred CPUs by 1 core. If preferred is same as active,
+ nothing to be done.
+
+4. Ensure preferred CPUs is always subset of active CPUs.
+ On feature disable it is same as active CPUs.
+
+This feature works best only when all the VMs enable the feature as
+it is a co-operative scheme. If a specific VM doesn't enable this feature
+it may end up with more CPUs than others, still should lead to better
+performance when seen from system view.
+Those who enable this driver must ensure it is enabled in all VMs.
+
+Module Parameters
+=================
+interval_ms
+-----------
+How often steal monitor checks for steal time.
+Default: 1000 i.e 1 second. Value should be in between 10ms to 100sec.
+
+This controls how fast steal monitor driver reacts to changes to
+the contention of physical CPUs. Since it does a fair amount of
+work, setting too low will have overheads. Setting it too
+high might render it ineffective.
+
+low_threshold
+-------------
+lower threshold value in percentage * 100.
+Default: 200, i.e 2% steal is considered as low threshold.
+Can't be higher than high_threshold.
+
+This determines what values should be considered as nil/no steal values.
+When steal monitor see steal time is below or equal to this value, it
+will increase the preferred CPUs by 1 core. Having value as zero
+might cause oscillations.
+
+high_threshold
+--------------
+higher threshold value in percentage * 100
+Default: 500, i.e 5% steal is considered as high threshold.
+Can't be lower than low_threshold. Must be less than 10000.
+
+This determines what values should be considered as high steal values.
+When steal monitor sees steal time is higher than this value, it will
+reduce the preferred CPUs by 1 core.
+
+Notes
+=====
+Selecting this driver makes CONFIG_PREFERRED_CPU=y. That makes configs
+driven by user preference.
+
+It is recommended to build CONFIG_STEAL_MONITOR=m due ot below reasons:
+
+1. Doing periodic work has additional overheads. Enabling this driver
+ in systems where steal time cannot happen is of no use. There is no
+ benefit with additional overheads in such systems.
+
+2. This works well when all VMs work in co-operative manner. When an
+ administrative user enables it in one VM, he/she will likely enable
+ it all VMs.
diff --git a/MAINTAINERS b/MAINTAINERS
index 15011f5752a9..6735f9dae530 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25914,6 +25914,15 @@ F: rust/helpers/jump_label.c
F: rust/kernel/generated_arch_static_branch_asm.rs.S
F: rust/kernel/jump_label.rs
+STEAL TIME MONITOR DRIVER
+M: Shrikanth Hegde <sshegde@linux.ibm.com>
+R: Yury Norov <yury.norov@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/core
+F: Documentation/driver-api/steal-monitor.rst
+F: drivers/virt/steal_monitor/
+
STI AUDIO (ASoC) DRIVERS
M: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
L: linux-sound@vger.kernel.org
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
index 52eb7e4ba71f..a52233b2502e 100644
--- a/drivers/virt/Kconfig
+++ b/drivers/virt/Kconfig
@@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig"
source "drivers/virt/acrn/Kconfig"
+source "drivers/virt/steal_monitor/Kconfig"
+
endif
source "drivers/virt/coco/Kconfig"
diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
index f29901bd7820..b67fd8968ec3 100644
--- a/drivers/virt/Makefile
+++ b/drivers/virt/Makefile
@@ -9,4 +9,5 @@ obj-y += vboxguest/
obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/
obj-$(CONFIG_ACRN_HSM) += acrn/
+obj-$(CONFIG_STEAL_MONITOR) += steal_monitor/
obj-y += coco/
diff --git a/drivers/virt/steal_monitor/Kconfig b/drivers/virt/steal_monitor/Kconfig
new file mode 100644
index 000000000000..c7d7599c30ce
--- /dev/null
+++ b/drivers/virt/steal_monitor/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config STEAL_MONITOR
+ tristate "Dynamic vCPU management based on steal time"
+ depends on PARAVIRT && SMP
+ select PREFERRED_CPU
+ default m
+ help
+ This driver helps to reduce the steal time in paravirtualised
+ environment, thereby reducing vCPU preemption. Reducing vCPU
+ preemption provides improved lock holder preemption and reduces
+ cost of vCPU preemption in the host.
+
+ By default preferred CPUs will be same as active CPUs. Depending
+ on the steal time when steal_monitor driver is enabled,
+ preferred CPUs could become subset of active CPUs.
+
+ It is recommended to build it as module and load the module
+ to enable it.
diff --git a/drivers/virt/steal_monitor/Makefile b/drivers/virt/steal_monitor/Makefile
new file mode 100644
index 000000000000..bd7d120a79b5
--- /dev/null
+++ b/drivers/virt/steal_monitor/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Steal time monitor to alter preferred CPU state.
+obj-$(CONFIG_STEAL_MONITOR) += steal_monitor.o
+
+steal_monitor-y := sm_core.o
diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
new file mode 100644
index 000000000000..180db424846c
--- /dev/null
+++ b/drivers/virt/steal_monitor/sm_core.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Steal time Monitor.
+ *
+ * Periodically compute steal time. Based on the thresholds either
+ * reduce/increase the preferred CPUs which can be used
+ * by the workload to avoid vCPU preemption to an extent possible.
+ *
+ * Available as module with CONFIG_STEAL_MONITOR=m
+ *
+ * Copyright (C) 2026 IBM
+ * Author: Shrikanth Hegde <sshegde@linux.ibm.com>
+ */
+
+#include "sm_core.h"
+
+struct steal_monitor sm_core_ctx;
+
+static int __init steal_monitor_init(void)
+{
+ pr_info("steal_monitor is enabled\n");
+ return 0;
+}
+
+static void __exit steal_monitor_exit(void)
+{
+ guard(cpus_read_lock)();
+ cpumask_copy(&__cpu_preferred_mask, cpu_active_mask);
+
+ pr_info("steal_monitor is disabled\n");
+}
+
+module_init(steal_monitor_init);
+module_exit(steal_monitor_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Virtualization Steal Time Monitor");
diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
new file mode 100644
index 000000000000..8bbb606add99
--- /dev/null
+++ b/drivers/virt/steal_monitor/sm_core.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __VIRT_STEAL_CORE_H
+#define __VIRT_STEAL_CORE_H
+
+#include <linux/types.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/cpuhplock.h>
+#include <linux/cpumask.h>
+#include <linux/workqueue.h>
+#include <linux/ktime.h>
+
+struct steal_monitor {
+ struct delayed_work work;
+ u64 prev_steal;
+ int prev_direction;
+ unsigned int interval_ms;
+ unsigned int high_threshold;
+ unsigned int low_threshold;
+ ktime_t prev_time;
+};
+
+extern struct steal_monitor sm_core_ctx;
+
+#endif /* __VIRT_STEAL_CORE_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (7 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 08/12] virt: Introduce steal monitor driver Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 4:27 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing " Shrikanth Hegde
` (2 subsequent siblings)
11 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
These are the knobs to control the steal_monitor.
interval_ms:
How often steal monitor checks for steal time.
(Default: 1000 i.e 1 second)
This controls how fast steal monitor driver reacts to changes to
the contention of physical CPUs. Since it does a fair amount of
work, setting too low will have overheads. Setting it too high
might render the feature ineffective.
Can be set between 10 to 100000. i.e. 10ms to 100seconds.
low_threshold:
lower threshold value in percentage * 100.
(Default: 200, i.e 2% steal is considered as low threshold)
This determines what values should be considered as nil/no steal values.
When steal monitor see steal time is below or equal to this value, it
will increase the preferred CPUs by 1 core. Having value as zero
might cause oscillations
high_threshold:
higher threshold value in percentage * 100
(Default: 500, i.e 5% steal is considered as high threshold)
This determines what values should be considered as high steal values.
When steal monitor sees steal time is higher than this value, it will
reduce the preferred CPUs by 1 core.
module_param_cb methods are used to do the validation checks.
This helps to ensure one configures sane values.
Parameters values can't be changed at runtime. One has to unload
the module and change it.
Also available at: Documentation/driver-api/steal-monitor.rst
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Add module_param_cb to do parameter checks.
- Make parameters read only after module load.
drivers/virt/steal_monitor/sm_core.c | 92 +++++++++++++++++++++++++++-
1 file changed, 91 insertions(+), 1 deletion(-)
diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
index 180db424846c..4a03c14337be 100644
--- a/drivers/virt/steal_monitor/sm_core.c
+++ b/drivers/virt/steal_monitor/sm_core.c
@@ -14,7 +14,97 @@
#include "sm_core.h"
-struct steal_monitor sm_core_ctx;
+struct steal_monitor sm_core_ctx = {
+ .interval_ms = 1000, /* 1 second */
+ .high_threshold = 500, /* 5% */
+ .low_threshold = 200, /* 2% */
+};
+
+static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
+{
+ unsigned int interval;
+ int ret;
+
+ ret = kstrtouint(val, 0, &interval);
+ if (ret)
+ return ret;
+
+ if (interval < 10 || interval > 100000) {
+ pr_err("steal_monitor: interval_ms must be between 10 and 100000\n");
+ return -EINVAL;
+ }
+
+ return param_set_uint(val, kp);
+}
+
+static const struct kernel_param_ops interval_ms_ops = {
+ .set = param_set_interval_ms,
+ .get = param_get_uint,
+};
+
+module_param_cb(interval_ms, &interval_ms_ops, &sm_core_ctx.interval_ms, 0444);
+MODULE_PARM_DESC(interval_ms,
+ "Sampling frequency in milliseconds. default: 1000");
+
+static int param_set_high_threshold(const char *val, const struct kernel_param *kp)
+{
+ unsigned int threshold;
+ int ret;
+
+ ret = kstrtouint(val, 0, &threshold);
+ if (ret)
+ return ret;
+
+ if (threshold <= sm_core_ctx.low_threshold) {
+ pr_err("steal_monitor: high_threshold (%u) must be more than low_threshold (%u)\n",
+ threshold, sm_core_ctx.low_threshold);
+ return -EINVAL;
+ }
+
+ if (threshold >= 100 * 100) {
+ pr_err("steal_monitor: high_threshold (%u) can't be more than 99.99%%\n",
+ threshold);
+ return -EINVAL;
+ }
+
+ return param_set_uint(val, kp);
+}
+
+static const struct kernel_param_ops high_threshold_ops = {
+ .set = param_set_high_threshold,
+ .get = param_get_uint,
+};
+
+module_param_cb(high_threshold, &high_threshold_ops, &sm_core_ctx.high_threshold, 0444);
+MODULE_PARM_DESC(high_threshold,
+ "High steal threshold. default: 500 i.e 5%. Must be > low_threshold");
+
+static int param_set_low_threshold(const char *val, const struct kernel_param *kp)
+{
+ unsigned int threshold;
+ int ret;
+
+ ret = kstrtouint(val, 0, &threshold);
+ if (ret)
+ return ret;
+
+ if (threshold >= sm_core_ctx.high_threshold) {
+ pr_err("steal_monitor: low_threshold (%u) must be less than high_threshold (%u)\n",
+ threshold, sm_core_ctx.high_threshold);
+ return -EINVAL;
+ }
+
+ return param_set_uint(val, kp);
+}
+
+static const struct kernel_param_ops low_threshold_ops = {
+ .set = param_set_low_threshold,
+ .get = param_get_uint,
+};
+
+module_param_cb(low_threshold, &low_threshold_ops, &sm_core_ctx.low_threshold, 0444);
+MODULE_PARM_DESC(low_threshold,
+ "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
static int __init steal_monitor_init(void)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing steal values
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (8 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 4:30 ` Shrikanth Hegde
2026-07-10 20:00 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU Shrikanth Hegde
11 siblings, 2 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Provide functions which is going to be used in the periodic work
function to calculate and handle steal time values.
get_system_steal_time()
- steal monitor takes global view of steal time instead of individual
vCPU. For this collect overall steal values across all the vCPUs or
vCPUs of interest.
- Sum up steal time values across possible CPUs. This helps to keep it
a monotonically increasing number and avoids spikes due to CPU
hotplug.
decrease_preferred_cpus()
- Called when there is high steal time. It needs to decide which CPUs to
mark as non-preferred and set that state.
- Get first housekeeping CPU and its core mask. Mark it as
protected core. This helps to keep at least one core as preferred.
kernel ensures at least one housekeeping CPU must stay active.
- Find the last CPU outside of this protected core mask. (target CPU)
- Based on that target CPU, get its sibling and mark them as
non-preferred.
increase_preferred_cpus()
- Called when there is low steal time. It needs to decide which CPUs to
mark as preferred and set that state.
- Get the first active non-preferred CPUs. This likely is the last
set of CPUs being marked as non-preferred.
- get the siblings of that CPU and mark them as preferred.
get_num_cpus_steal_ratio()
- This method informs the steal_monitor core, how many CPUs it needs to
consider for steal ratio calculations.
- Return number of possible CPUs as get_system_steal_time computes
steal values across possible CPUs.
Notes:
1. Using core instead of individual CPUs performs better as SMT is
quite common and some hypervisor such as powerVM does core scheduling.
2. This doesn't do any NUMA splicing to keep the code simpler and
minimal overhead. Current code expects CPUs spread uniformly
across NUMA nodes.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Combined patches which added helper functions.
- Use possible CPUs for steal value calculations.
drivers/virt/steal_monitor/Makefile | 2 +-
drivers/virt/steal_monitor/defaults.c | 100 ++++++++++++++++++++++++++
drivers/virt/steal_monitor/sm_core.h | 8 +++
3 files changed, 109 insertions(+), 1 deletion(-)
create mode 100644 drivers/virt/steal_monitor/defaults.c
diff --git a/drivers/virt/steal_monitor/Makefile b/drivers/virt/steal_monitor/Makefile
index bd7d120a79b5..273a6dd59fea 100644
--- a/drivers/virt/steal_monitor/Makefile
+++ b/drivers/virt/steal_monitor/Makefile
@@ -3,4 +3,4 @@
# Steal time monitor to alter preferred CPU state.
obj-$(CONFIG_STEAL_MONITOR) += steal_monitor.o
-steal_monitor-y := sm_core.o
+steal_monitor-y := sm_core.o defaults.o
diff --git a/drivers/virt/steal_monitor/defaults.c b/drivers/virt/steal_monitor/defaults.c
new file mode 100644
index 000000000000..d4b016317554
--- /dev/null
+++ b/drivers/virt/steal_monitor/defaults.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Base file contains the default implementations.
+ *
+ * Copyright (C) 2026 IBM
+ * Author: Shrikanth Hegde <sshegde@linux.ibm.com>
+ */
+#include "sm_core.h"
+
+/*
+ * Returns steal time of the full system.
+ * Compute collective steal time across all possible CPUs.
+ */
+u64 get_system_steal_time(void)
+{
+ int cpu;
+ u64 total_steal = 0;
+
+ for_each_possible_cpu(cpu)
+ total_steal += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
+
+ return total_steal;
+}
+
+/*
+ * Returns number of CPUs to consider for steal ratio.
+ * Return possible CPUs.
+ */
+unsigned int get_num_cpus_steal_ratio(void)
+{
+ return num_possible_cpus();
+}
+
+/*
+ * Take action to decrease preferred CPUs.
+ *
+ * Decrease the preferred CPUs by 1 core.
+ * Take out the last core in the active & preferred.
+ *
+ * Must ensure
+ * - least one housekeeping core is always kept as preferred
+ * - preferred is always subset of active.
+ */
+void decrease_preferred_cpus(struct steal_monitor *ctx)
+{
+ int tmp_cpu, first_hk_cpu, last_cpu;
+ const struct cpumask *first_hk_core;
+ int target_cpu = nr_cpu_ids;
+
+ guard(cpus_read_lock)();
+ first_hk_cpu = cpumask_first_and(housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
+ cpu_preferred_mask);
+ last_cpu = cpumask_last(cpu_preferred_mask);
+
+ if (first_hk_cpu >= nr_cpu_ids || last_cpu >= nr_cpu_ids)
+ return;
+
+ /* Always leave first housekeeping core as preferred. */
+ first_hk_core = topology_sibling_cpumask(first_hk_cpu);
+
+ /* Find the last CPU which doesn't belong to that first hk_core. */
+ if (!cpumask_test_cpu(last_cpu, first_hk_core)) {
+ target_cpu = last_cpu;
+ } else {
+ for_each_cpu_andnot(tmp_cpu, cpu_preferred_mask, first_hk_core)
+ target_cpu = tmp_cpu;
+ }
+
+ /* Only the first housekeeping core remains */
+ if (target_cpu >= nr_cpu_ids)
+ return;
+
+ for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(target_cpu),
+ cpu_preferred_mask)
+ set_cpu_preferred(tmp_cpu, false);
+}
+
+/*
+ * Take action to increase preferred CPUs.
+ *
+ * Increase the preferred CPUs by 1 core.
+ * Add the first core in active & !preferred
+ *
+ * Must ensure preferred is subset of active.
+ */
+void increase_preferred_cpus(struct steal_monitor *ctx)
+{
+ int first_cpu, tmp_cpu;
+
+ guard(cpus_read_lock)();
+ first_cpu = cpumask_first_andnot(cpu_active_mask, cpu_preferred_mask);
+
+ /* All CPUs are preferred. Nothing to increase further */
+ if (first_cpu >= nr_cpu_ids)
+ return;
+
+ for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(first_cpu),
+ cpu_active_mask)
+ set_cpu_preferred(tmp_cpu, true);
+}
diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
index 8bbb606add99..ee68cd8b1944 100644
--- a/drivers/virt/steal_monitor/sm_core.h
+++ b/drivers/virt/steal_monitor/sm_core.h
@@ -11,6 +11,9 @@
#include <linux/cpumask.h>
#include <linux/workqueue.h>
#include <linux/ktime.h>
+#include <linux/kernel_stat.h>
+#include <linux/topology.h>
+#include <linux/sched/isolation.h>
struct steal_monitor {
struct delayed_work work;
@@ -24,4 +27,9 @@ struct steal_monitor {
extern struct steal_monitor sm_core_ctx;
+u64 get_system_steal_time(void);
+unsigned int get_num_cpus_steal_ratio(void);
+void increase_preferred_cpus(struct steal_monitor *ctx);
+void decrease_preferred_cpus(struct steal_monitor *ctx);
+
#endif /* __VIRT_STEAL_CORE_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (9 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing " Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 20:37 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU Shrikanth Hegde
11 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
schedule work at regular intervals. Interval is determined by
interval_ms parameter. schedule_delayed_work is used since interval_ms
is usually in order of milliseconds. Work need not happen instantly.
Periodic work function essentially does:
- Calculate the steal_ratio as below.
steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
It is calculated to consider the fractional values of steal time.
I.e 10 means 0.1% steal time. A few tricks such as divide by 10,000
are used to avoid possible overflow.
- If steal value is higher than high threshold, call the method to reduce
the preferred CPUs.
- If steal value is lower or equal to low threshold, call the method to
increase the preferred CPUs.
- If the steal value is in between, no action is taken.
- Save the values for next delta calculations.
- Save the current direction of steal values to avoid oscillations.
So two consecutive values of high values or low values are taken for
decrease/increase of preferred CPUs.
- Ensure design checks are met.
1. At least one core/CPU must be there in preferred mask.
2. preferred CPUs is subset of active CPUs.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Merge two patches which did periodic work function.
- Misc checks for early firing, requeue work, math safety.
drivers/virt/steal_monitor/sm_core.c | 76 +++++++++++++++++++++++++++-
drivers/virt/steal_monitor/sm_core.h | 1 +
2 files changed, 76 insertions(+), 1 deletion(-)
diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
index 4a03c14337be..09a5c3a299c3 100644
--- a/drivers/virt/steal_monitor/sm_core.c
+++ b/drivers/virt/steal_monitor/sm_core.c
@@ -20,6 +20,12 @@ struct steal_monitor sm_core_ctx = {
.low_threshold = 200, /* 2% */
};
+enum sm_direction {
+ SM_DIR_INCREASE = -1,
+ SM_DIR_NONE = 0,
+ SM_DIR_DECREASE = 1,
+};
+
static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
{
unsigned int interval;
@@ -106,14 +112,82 @@ module_param_cb(low_threshold, &low_threshold_ops, &sm_core_ctx.low_threshold, 0
MODULE_PARM_DESC(low_threshold,
"Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
+static void compute_preferred_cpus_work(struct work_struct *work)
+{
+ u64 curr_steal, delta_steal, delta_ns, steal_ratio;
+ ktime_t now;
+
+ now = ktime_get();
+ delta_ns = ktime_to_ns(ktime_sub(now, sm_core_ctx.prev_time));
+
+ if (unlikely(delta_ns < NSEC_PER_MSEC)) {
+ pr_err_ratelimited("steal_monitor: work scheduled too soon delta_ns: %llu\n",
+ delta_ns);
+ goto requeue_work;
+ }
+
+ curr_steal = get_system_steal_time();
+ delta_steal = curr_steal > sm_core_ctx.prev_steal ?
+ curr_steal - sm_core_ctx.prev_steal : 0;
+
+ /* Update for next calculation */
+ sm_core_ctx.prev_steal = curr_steal;
+ sm_core_ctx.prev_time = now;
+
+ /*
+ * steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
+ * To avoid possible overflow, divide the denominator early.
+ * Note minimum interval is 10ms.
+ */
+ delta_ns = div_u64(delta_ns * get_num_cpus_steal_ratio(), 100 * 100);
+ steal_ratio = div64_u64(delta_steal, delta_ns);
+
+ if (sm_core_ctx.prev_direction == SM_DIR_DECREASE &&
+ steal_ratio > sm_core_ctx.high_threshold)
+ decrease_preferred_cpus(&sm_core_ctx);
+ if (sm_core_ctx.prev_direction == SM_DIR_INCREASE &&
+ steal_ratio <= sm_core_ctx.low_threshold)
+ increase_preferred_cpus(&sm_core_ctx);
+
+ /*
+ * mark the direction. Increasing the gap between hi and lo_threshold
+ * helps to avoid ping-pongs.
+ */
+ if (steal_ratio > sm_core_ctx.high_threshold)
+ sm_core_ctx.prev_direction = SM_DIR_DECREASE;
+ else if (steal_ratio <= sm_core_ctx.low_threshold)
+ sm_core_ctx.prev_direction = SM_DIR_INCREASE;
+ else
+ sm_core_ctx.prev_direction = SM_DIR_NONE;
+
+requeue_work:
+ /* maintain design constructs always */
+ WARN_ON_ONCE(cpumask_empty(cpu_preferred_mask));
+ WARN_ON_ONCE(!cpumask_subset(cpu_preferred_mask, cpu_active_mask));
+
+ /* Trigger for next sampling */
+ schedule_delayed_work(&sm_core_ctx.work,
+ msecs_to_jiffies(sm_core_ctx.interval_ms));
+}
+
static int __init steal_monitor_init(void)
{
- pr_info("steal_monitor is enabled\n");
+ pr_info("steal_monitor is enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
+ sm_core_ctx.interval_ms, sm_core_ctx.high_threshold, sm_core_ctx.low_threshold);
+
+ INIT_DELAYED_WORK(&sm_core_ctx.work, compute_preferred_cpus_work);
+ sm_core_ctx.prev_steal = get_system_steal_time();
+ sm_core_ctx.prev_time = ktime_get();
+
+ schedule_delayed_work(&sm_core_ctx.work,
+ msecs_to_jiffies(sm_core_ctx.interval_ms));
+
return 0;
}
static void __exit steal_monitor_exit(void)
{
+ cancel_delayed_work_sync(&sm_core_ctx.work);
guard(cpus_read_lock)();
cpumask_copy(&__cpu_preferred_mask, cpu_active_mask);
diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
index ee68cd8b1944..7c7a9bced682 100644
--- a/drivers/virt/steal_monitor/sm_core.h
+++ b/drivers/virt/steal_monitor/sm_core.h
@@ -14,6 +14,7 @@
#include <linux/kernel_stat.h>
#include <linux/topology.h>
#include <linux/sched/isolation.h>
+#include <linux/math64.h>
struct steal_monitor {
struct delayed_work work;
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
` (10 preceding siblings ...)
2026-07-09 21:56 ` [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs Shrikanth Hegde
@ 2026-07-09 21:56 ` Shrikanth Hegde
2026-07-10 6:35 ` Shrikanth Hegde
11 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-09 21:56 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: sshegde, tglx, gregkh, pbonzini, seanjc, vschneid, huschle,
rostedt, dietmar.eggemann, maddy, srikar, hdanton, chleroy,
vineeth, frederic, arighi, pauld, christian.loehle, tj,
tommaso.cucinotta, maz, rafael, rdunlap, kernellwp, linux-doc
Enable tick on nohz full CPU when it is marked as non-preferred.
This helps to push the task out faster and in more predictable
manner on nohz_full CPUs.
Steal time handling code will call tick_nohz_dep_set_cpu with
TICK_DEP_BIT_SCHED. This helps to push the task out of nohz_full
faster as push task depends on tick.
If there is pinned task on non-preferred CPU, it may not stop the tick.
That is rare case. Even then, this preferred CPU state change can
happen only inside the guest. So even if guest stop the tick,
it may not necessary mean power saving since host disabling the
tick is what matters more.
sched_can_stop_tick flow doesn't change if cpu_preferred. On disabling
the feature, module ensure it restores the CPU as preferred.
Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
---
v6->v7:
- Split into own patch as it is a optimization.
drivers/virt/steal_monitor/defaults.c | 9 ++++++++-
drivers/virt/steal_monitor/sm_core.h | 1 +
kernel/sched/core.c | 4 ++++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/virt/steal_monitor/defaults.c b/drivers/virt/steal_monitor/defaults.c
index d4b016317554..1d6197eda5eb 100644
--- a/drivers/virt/steal_monitor/defaults.c
+++ b/drivers/virt/steal_monitor/defaults.c
@@ -70,9 +70,16 @@ void decrease_preferred_cpus(struct steal_monitor *ctx)
if (target_cpu >= nr_cpu_ids)
return;
+ /*
+ * set tick bit for nohz_full CPU to push the task out. Once the tasks
+ * are pushed out, bit will be cleared if there are no tasks.
+ */
for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(target_cpu),
- cpu_preferred_mask)
+ cpu_active_mask) {
set_cpu_preferred(tmp_cpu, false);
+ if (tick_nohz_full_cpu(tmp_cpu))
+ tick_nohz_dep_set_cpu(tmp_cpu, TICK_DEP_BIT_SCHED);
+ }
}
/*
diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
index 7c7a9bced682..bf3f82f3e0cb 100644
--- a/drivers/virt/steal_monitor/sm_core.h
+++ b/drivers/virt/steal_monitor/sm_core.h
@@ -15,6 +15,7 @@
#include <linux/topology.h>
#include <linux/sched/isolation.h>
#include <linux/math64.h>
+#include <linux/tick.h>
struct steal_monitor {
struct delayed_work work;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 1ca1eefbdaf9..6ed61182b19f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1473,6 +1473,10 @@ bool sched_can_stop_tick(struct rq *rq)
return false;
}
+ /* Keep the tick running until CFS tasks are pushed out */
+ if (!cpu_preferred(rq->cpu) && rq->cfs.h_nr_queued)
+ return false;
+
return true;
}
#endif /* CONFIG_NO_HZ_FULL */
--
2.47.3
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v7 08/12] virt: Introduce steal monitor driver
2026-07-09 21:56 ` [PATCH v7 08/12] virt: Introduce steal monitor driver Shrikanth Hegde
@ 2026-07-10 1:27 ` Randy Dunlap
2026-07-10 4:28 ` Shrikanth Hegde
2026-07-10 20:53 ` Yury Norov
1 sibling, 1 reply; 25+ messages in thread
From: Randy Dunlap @ 2026-07-10 1:27 UTC (permalink / raw)
To: Shrikanth Hegde, linux-kernel, mingo, peterz, juri.lelli,
vincent.guittot, yury.norov, kprateek.nayak, iii, corbet
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, kernellwp, linux-doc
Hi,
On 7/9/26 2:56 PM, Shrikanth Hegde wrote:
> diff --git a/Documentation/driver-api/steal-monitor.rst b/Documentation/driver-api/steal-monitor.rst
> new file mode 100644
> index 000000000000..94f4aa1aaa7d
> --- /dev/null
> +++ b/Documentation/driver-api/steal-monitor.rst
> @@ -0,0 +1,111 @@
> +.. SPDX-License-Identifier: GPL-2.0
Insert a blank line here, please, to avoid this warning:
Documentation/driver-api/steal-monitor.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent. [docutils]
> +=============
> +Steal Monitor
> +=============
> +
> +:Author: Shrikanth Hegde
> +
> +Introduction
> +============
--
~Randy
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values
2026-07-09 21:56 ` [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values Shrikanth Hegde
@ 2026-07-10 4:27 ` Shrikanth Hegde
0 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-10 4:27 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, rdunlap, kernellwp, linux-doc
On 7/10/26 3:26 AM, Shrikanth Hegde wrote:
> These are the knobs to control the steal_monitor.
>
> interval_ms:
> How often steal monitor checks for steal time.
> (Default: 1000 i.e 1 second)
> This controls how fast steal monitor driver reacts to changes to
> the contention of physical CPUs. Since it does a fair amount of
> work, setting too low will have overheads. Setting it too high
> might render the feature ineffective.
> Can be set between 10 to 100000. i.e. 10ms to 100seconds.
>
> low_threshold:
> lower threshold value in percentage * 100.
> (Default: 200, i.e 2% steal is considered as low threshold)
> This determines what values should be considered as nil/no steal values.
> When steal monitor see steal time is below or equal to this value, it
> will increase the preferred CPUs by 1 core. Having value as zero
> might cause oscillations
>
> high_threshold:
> higher threshold value in percentage * 100
> (Default: 500, i.e 5% steal is considered as high threshold)
> This determines what values should be considered as high steal values.
> When steal monitor sees steal time is higher than this value, it will
> reduce the preferred CPUs by 1 core.
>
> module_param_cb methods are used to do the validation checks.
> This helps to ensure one configures sane values.
> Parameters values can't be changed at runtime. One has to unload
> the module and change it.
>
> Also available at: Documentation/driver-api/steal-monitor.rst
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> v6->v7:
> - Add module_param_cb to do parameter checks.
> - Make parameters read only after module load.
>
>
> drivers/virt/steal_monitor/sm_core.c | 92 +++++++++++++++++++++++++++-
> 1 file changed, 91 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
> index 180db424846c..4a03c14337be 100644
> --- a/drivers/virt/steal_monitor/sm_core.c
> +++ b/drivers/virt/steal_monitor/sm_core.c
> @@ -14,7 +14,97 @@
>
> #include "sm_core.h"
>
> -struct steal_monitor sm_core_ctx;
> +struct steal_monitor sm_core_ctx = {
> + .interval_ms = 1000, /* 1 second */
> + .high_threshold = 500, /* 5% */
> + .low_threshold = 200, /* 2% */
> +};
> +
> +static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
> +{
> + unsigned int interval;
> + int ret;
> +
> + ret = kstrtouint(val, 0, &interval);
> + if (ret)
> + return ret;
> +
> + if (interval < 10 || interval > 100000) {
> + pr_err("steal_monitor: interval_ms must be between 10 and 100000\n");
> + return -EINVAL;
> + }
> +
> + return param_set_uint(val, kp);
> +}
> +
> +static const struct kernel_param_ops interval_ms_ops = {
> + .set = param_set_interval_ms,
> + .get = param_get_uint,
> +};
> +
> +module_param_cb(interval_ms, &interval_ms_ops, &sm_core_ctx.interval_ms, 0444);
> +MODULE_PARM_DESC(interval_ms,
> + "Sampling frequency in milliseconds. default: 1000");
> +
> +static int param_set_high_threshold(const char *val, const struct kernel_param *kp)
> +{
> + unsigned int threshold;
> + int ret;
> +
> + ret = kstrtouint(val, 0, &threshold);
> + if (ret)
> + return ret;
> +
> + if (threshold <= sm_core_ctx.low_threshold) {
> + pr_err("steal_monitor: high_threshold (%u) must be more than low_threshold (%u)\n",
> + threshold, sm_core_ctx.low_threshold);
> + return -EINVAL;
> + }
> +
> + if (threshold >= 100 * 100) {
> + pr_err("steal_monitor: high_threshold (%u) can't be more than 99.99%%\n",
> + threshold);
> + return -EINVAL;
> + }
> +
> + return param_set_uint(val, kp);
> +}
> +
> +static const struct kernel_param_ops high_threshold_ops = {
> + .set = param_set_high_threshold,
> + .get = param_get_uint,
> +};
> +
> +module_param_cb(high_threshold, &high_threshold_ops, &sm_core_ctx.high_threshold, 0444);
> +MODULE_PARM_DESC(high_threshold,
> + "High steal threshold. default: 500 i.e 5%. Must be > low_threshold");
> +
> +static int param_set_low_threshold(const char *val, const struct kernel_param *kp)
> +{
> + unsigned int threshold;
> + int ret;
> +
> + ret = kstrtouint(val, 0, &threshold);
> + if (ret)
> + return ret;
> +
> + if (threshold >= sm_core_ctx.high_threshold) {
> + pr_err("steal_monitor: low_threshold (%u) must be less than high_threshold (%u)\n",
> + threshold, sm_core_ctx.high_threshold);
> + return -EINVAL;
> + }
> +
> + return param_set_uint(val, kp);
> +}
> +
> +static const struct kernel_param_ops low_threshold_ops = {
> + .set = param_set_low_threshold,
> + .get = param_get_uint,
> +};
> +
> +module_param_cb(low_threshold, &low_threshold_ops, &sm_core_ctx.low_threshold, 0444);
> +MODULE_PARM_DESC(low_threshold,
> + "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
>
As reported correctly by sashiko, below fails, but is a valid parameter set.
modprobe steal_monitor low_threshold=700 high_threshold=1500
Since low_threshold and high_thresholds are interdependent, I need to
defer this to steal_monitor_init. That's probably what yury mentioned
earlier, just that i didn't understand.
I think interval_ms, high_thresholds check can still be as module_param_cb
as they are independent.
I will fix it in v8.
Effectively diff:
---
diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
index 09a5c3a299c3..38007791a2dd 100644
--- a/drivers/virt/steal_monitor/sm_core.c
+++ b/drivers/virt/steal_monitor/sm_core.c
@@ -61,12 +61,6 @@ static int param_set_high_threshold(const char *val, const struct kernel_param *
if (ret)
return ret;
- if (threshold <= sm_core_ctx.low_threshold) {
- pr_err("steal_monitor: high_threshold (%u) must be more than low_threshold (%u)\n",
- threshold, sm_core_ctx.low_threshold);
- return -EINVAL;
- }
-
if (threshold >= 100 * 100) {
pr_err("steal_monitor: high_threshold (%u) can't be more than 99.99%%\n",
threshold);
@@ -85,30 +79,7 @@ module_param_cb(high_threshold, &high_threshold_ops, &sm_core_ctx.high_threshold
MODULE_PARM_DESC(high_threshold,
"High steal threshold. default: 500 i.e 5%. Must be > low_threshold");
-static int param_set_low_threshold(const char *val, const struct kernel_param *kp)
-{
- unsigned int threshold;
- int ret;
-
- ret = kstrtouint(val, 0, &threshold);
- if (ret)
- return ret;
-
- if (threshold >= sm_core_ctx.high_threshold) {
- pr_err("steal_monitor: low_threshold (%u) must be less than high_threshold (%u)\n",
- threshold, sm_core_ctx.high_threshold);
- return -EINVAL;
- }
-
- return param_set_uint(val, kp);
-}
-
-static const struct kernel_param_ops low_threshold_ops = {
- .set = param_set_low_threshold,
- .get = param_get_uint,
-};
-
-module_param_cb(low_threshold, &low_threshold_ops, &sm_core_ctx.low_threshold, 0444);
+module_param_named(low_threshold, sm_core_ctx.low_threshold, uint, 0444);
MODULE_PARM_DESC(low_threshold,
"Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
@@ -172,6 +143,11 @@ static void compute_preferred_cpus_work(struct work_struct *work)
static int __init steal_monitor_init(void)
{
+ if (sm_core_ctx.low_threshold >= sm_core_ctx.high_threshold) {
+ pr_err("steal_monitor: low_threshold (%u) must be less than high_threshold (%u)\n",
+ sm_core_ctx.low_threshold, sm_core_ctx.high_threshold);
+ return -EINVAL;
+ }
pr_info("steal_monitor is enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
sm_core_ctx.interval_ms, sm_core_ctx.high_threshold, sm_core_ctx.low_threshold);
> static int __init steal_monitor_init(void)
> {
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v7 08/12] virt: Introduce steal monitor driver
2026-07-10 1:27 ` Randy Dunlap
@ 2026-07-10 4:28 ` Shrikanth Hegde
0 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-10 4:28 UTC (permalink / raw)
To: Randy Dunlap, linux-kernel, mingo, peterz, juri.lelli,
vincent.guittot, yury.norov, kprateek.nayak, iii, corbet
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, kernellwp, linux-doc
Hi Randy, Thanks for taking a look.
On 7/10/26 6:57 AM, Randy Dunlap wrote:
> Hi,
>
> On 7/9/26 2:56 PM, Shrikanth Hegde wrote:
>> diff --git a/Documentation/driver-api/steal-monitor.rst b/Documentation/driver-api/steal-monitor.rst
>> new file mode 100644
>> index 000000000000..94f4aa1aaa7d
>> --- /dev/null
>> +++ b/Documentation/driver-api/steal-monitor.rst
>> @@ -0,0 +1,111 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>
> Insert a blank line here, please, to avoid this warning:
> Documentation/driver-api/steal-monitor.rst:2: WARNING: Explicit markup ends without a blank line; unexpected unindent. [docutils]
Ah, same applies to the first one. Ok thanks. Will fix it in v8
>
>
>> +=============
>> +Steal Monitor
>> +=============
>> +
>> +:Author: Shrikanth Hegde
>> +
>> +Introduction
>> +============
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing steal values
2026-07-09 21:56 ` [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing " Shrikanth Hegde
@ 2026-07-10 4:30 ` Shrikanth Hegde
2026-07-10 20:00 ` Yury Norov
1 sibling, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-10 4:30 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, rdunlap, kernellwp, linux-doc
On 7/10/26 3:26 AM, Shrikanth Hegde wrote:
> +void increase_preferred_cpus(struct steal_monitor *ctx);
> +void decrease_preferred_cpus(struct steal_monitor *ctx);
ctx is not being used in the function as of now, but i forgot to remove after
that __weak removal. Will fix it in v8.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU
2026-07-09 21:56 ` [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU Shrikanth Hegde
@ 2026-07-10 6:35 ` Shrikanth Hegde
0 siblings, 0 replies; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-10 6:35 UTC (permalink / raw)
To: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii
Cc: tglx, gregkh, pbonzini, seanjc, vschneid, huschle, rostedt,
dietmar.eggemann, maddy, srikar, hdanton, chleroy, vineeth,
frederic, arighi, pauld, christian.loehle, tj, tommaso.cucinotta,
maz, rafael, rdunlap, kernellwp, linux-doc, corbet
Hi.
On 7/10/26 3:26 AM, Shrikanth Hegde wrote:
> Enable tick on nohz full CPU when it is marked as non-preferred.
> This helps to push the task out faster and in more predictable
> manner on nohz_full CPUs.
>
> Steal time handling code will call tick_nohz_dep_set_cpu with
> TICK_DEP_BIT_SCHED. This helps to push the task out of nohz_full
> faster as push task depends on tick.
>
> If there is pinned task on non-preferred CPU, it may not stop the tick.
> That is rare case. Even then, this preferred CPU state change can
> happen only inside the guest. So even if guest stop the tick,
> it may not necessary mean power saving since host disabling the
> tick is what matters more.
>
> sched_can_stop_tick flow doesn't change if cpu_preferred. On disabling
> the feature, module ensure it restores the CPU as preferred.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
I will drop this patch 12 as it is an optimization for rare case.
The reason being,
- If there is one pinned task, it leaves the tick on.
- Can't just the clear the tick while disable, that will cause break.
- need to know if there was one pinned task in sched_can_stop_tick
which is tricky.
- optimization is applicable only for a forever running thread on nohz_full
CPU and non-pinned. Such use case is rare i think.
and
- Task eventually move out that nohz_full CPU, it takes a few more
seconds. Likely it happens on natural boundary, maybe a external
interrupt, background workqueue or kthread etc.
- One configures nohz_full CPU carefully. If the need arises,
lets bother about at that time.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 03/12] sysfs: Add preferred CPU file
2026-07-09 21:56 ` [PATCH v7 03/12] sysfs: Add preferred CPU file Shrikanth Hegde
@ 2026-07-10 15:41 ` Yury Norov
2026-07-10 17:00 ` Shrikanth Hegde
0 siblings, 1 reply; 25+ messages in thread
From: Yury Norov @ 2026-07-10 15:41 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 03:26:39AM +0530, Shrikanth Hegde wrote:
> Add "preferred" file in /sys/devices/system/cpu
>
> This offers
> - User can quickly check which CPUs are marked as preferred at this
> moment.
> - Userspace algorithms irqbalance could use this mask to send irq into
> preferred CPUs.
>
> For example:
> cat /sys/devices/system/cpu/online
> 0-719
> cat /sys/devices/system/cpu/preferred
> 0-599 <<< Implies 0-599 are preferred for workloads and 600-719
> should be avoided at this moment.
>
> cat /sys/devices/system/cpu/preferred
> 0-719 <<< All CPUs are usable. There is no preference.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> Documentation/ABI/testing/sysfs-devices-system-cpu | 11 +++++++++++
> drivers/base/cpu.c | 8 ++++++++
> 2 files changed, 19 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> index 82d10d556cc8..ac1dbb209cc7 100644
> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> @@ -806,3 +806,14 @@ Date: Nov 2022
> Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
> Description:
> (RO) the list of CPUs that can be brought online.
> +
> +What: /sys/devices/system/cpu/preferred
> +Date: July 2026
> +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
> +Description:
> + (RO) the list of preferred CPUs at this moment.
> + These are the only CPUs meant to be used at the moment.
> + Using CPU outside of the list could lead to more
> + contention of underlying physical CPU resource. Dynamically
> + changes based on steal time. With CONFIG_PREFERRED_CPU=n it
> + is same as active CPUs. See sched-arch.rst for more details.
This should mention that it's about paravirtualization.
> diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
> index 19d288a3c80c..4ac990efee7c 100644
> --- a/drivers/base/cpu.c
> +++ b/drivers/base/cpu.c
> @@ -391,6 +391,13 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
> }
> #endif
>
> +static ssize_t preferred_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_preferred_mask));
> +}
> +static DEVICE_ATTR_RO(preferred);
> +
> const struct bus_type cpu_subsys = {
> .name = "cpu",
> .dev_name = "cpu",
> @@ -532,6 +539,7 @@ static struct attribute *cpu_root_attrs[] = {
> #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
> &dev_attr_modalias.attr,
> #endif
> + &dev_attr_preferred.attr,
#ifdef CONFIG_PREFERRED_CPUS ?
> NULL
> };
>
> --
> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 03/12] sysfs: Add preferred CPU file
2026-07-10 15:41 ` Yury Norov
@ 2026-07-10 17:00 ` Shrikanth Hegde
2026-07-10 23:09 ` Yury Norov
0 siblings, 1 reply; 25+ messages in thread
From: Shrikanth Hegde @ 2026-07-10 17:00 UTC (permalink / raw)
To: Yury Norov
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
Hi Yury, thanks for taking a look.
On 7/10/26 9:11 PM, Yury Norov wrote:
> On Fri, Jul 10, 2026 at 03:26:39AM +0530, Shrikanth Hegde wrote:
>> Add "preferred" file in /sys/devices/system/cpu
>>
>> This offers
>> - User can quickly check which CPUs are marked as preferred at this
>> moment.
>> - Userspace algorithms irqbalance could use this mask to send irq into
>> preferred CPUs.
>>
>> For example:
>> cat /sys/devices/system/cpu/online
>> 0-719
>> cat /sys/devices/system/cpu/preferred
>> 0-599 <<< Implies 0-599 are preferred for workloads and 600-719
>> should be avoided at this moment.
>>
>> cat /sys/devices/system/cpu/preferred
>> 0-719 <<< All CPUs are usable. There is no preference.
>>
>> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
>> ---
>> Documentation/ABI/testing/sysfs-devices-system-cpu | 11 +++++++++++
>> drivers/base/cpu.c | 8 ++++++++
>> 2 files changed, 19 insertions(+)
>>
>> diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> index 82d10d556cc8..ac1dbb209cc7 100644
>> --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
>> +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
>> @@ -806,3 +806,14 @@ Date: Nov 2022
>> Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
>> Description:
>> (RO) the list of CPUs that can be brought online.
>> +
>> +What: /sys/devices/system/cpu/preferred
>> +Date: July 2026
>> +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
>> +Description:
>> + (RO) the list of preferred CPUs at this moment.
>> + These are the only CPUs meant to be used at the moment.
>> + Using CPU outside of the list could lead to more
>> + contention of underlying physical CPU resource. Dynamically
>> + changes based on steal time. With CONFIG_PREFERRED_CPU=n it
>> + is same as active CPUs. See sched-arch.rst for more details.
>
> This should mention that it's about paravirtualization.
Ok. I will rephrase it.
>
>> diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
>> index 19d288a3c80c..4ac990efee7c 100644
>> --- a/drivers/base/cpu.c
>> +++ b/drivers/base/cpu.c
>> @@ -391,6 +391,13 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
>> }
>> #endif
>>
>> +static ssize_t preferred_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_preferred_mask));
>> +}
>> +static DEVICE_ATTR_RO(preferred);
>> +
>> const struct bus_type cpu_subsys = {
>> .name = "cpu",
>> .dev_name = "cpu",
>> @@ -532,6 +539,7 @@ static struct attribute *cpu_root_attrs[] = {
>> #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
>> &dev_attr_modalias.attr,
>> #endif
>> + &dev_attr_preferred.attr,
>
> #ifdef CONFIG_PREFERRED_CPUS ?
Not needed no? It will print active CPUs.
>
>> NULL
>> };
>>
>> --
>> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing steal values
2026-07-09 21:56 ` [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing " Shrikanth Hegde
2026-07-10 4:30 ` Shrikanth Hegde
@ 2026-07-10 20:00 ` Yury Norov
1 sibling, 0 replies; 25+ messages in thread
From: Yury Norov @ 2026-07-10 20:00 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 03:26:46AM +0530, Shrikanth Hegde wrote:
> Provide functions which is going to be used in the periodic work
> function to calculate and handle steal time values.
>
> get_system_steal_time()
> - steal monitor takes global view of steal time instead of individual
> vCPU. For this collect overall steal values across all the vCPUs or
> vCPUs of interest.
> - Sum up steal time values across possible CPUs. This helps to keep it
> a monotonically increasing number and avoids spikes due to CPU
> hotplug.
>
> decrease_preferred_cpus()
> - Called when there is high steal time. It needs to decide which CPUs to
> mark as non-preferred and set that state.
> - Get first housekeeping CPU and its core mask. Mark it as
> protected core. This helps to keep at least one core as preferred.
> kernel ensures at least one housekeeping CPU must stay active.
> - Find the last CPU outside of this protected core mask. (target CPU)
> - Based on that target CPU, get its sibling and mark them as
> non-preferred.
>
> increase_preferred_cpus()
> - Called when there is low steal time. It needs to decide which CPUs to
> mark as preferred and set that state.
> - Get the first active non-preferred CPUs. This likely is the last
> set of CPUs being marked as non-preferred.
> - get the siblings of that CPU and mark them as preferred.
>
> get_num_cpus_steal_ratio()
> - This method informs the steal_monitor core, how many CPUs it needs to
> consider for steal ratio calculations.
> - Return number of possible CPUs as get_system_steal_time computes
> steal values across possible CPUs.
>
> Notes:
> 1. Using core instead of individual CPUs performs better as SMT is
> quite common and some hypervisor such as powerVM does core scheduling.
>
> 2. This doesn't do any NUMA splicing to keep the code simpler and
> minimal overhead. Current code expects CPUs spread uniformly
> across NUMA nodes.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> v6->v7:
> - Combined patches which added helper functions.
> - Use possible CPUs for steal value calculations.
>
> drivers/virt/steal_monitor/Makefile | 2 +-
> drivers/virt/steal_monitor/defaults.c | 100 ++++++++++++++++++++++++++
> drivers/virt/steal_monitor/sm_core.h | 8 +++
What for do you split functionality into sm_core and default? There's
no non-default implementation, right?
I'd just put everything in drivers/virt/steal_monitor.c. It would be
~300 LOCs file - quite bearable.
> 3 files changed, 109 insertions(+), 1 deletion(-)
> create mode 100644 drivers/virt/steal_monitor/defaults.c
>
> diff --git a/drivers/virt/steal_monitor/Makefile b/drivers/virt/steal_monitor/Makefile
> index bd7d120a79b5..273a6dd59fea 100644
> --- a/drivers/virt/steal_monitor/Makefile
> +++ b/drivers/virt/steal_monitor/Makefile
> @@ -3,4 +3,4 @@
> # Steal time monitor to alter preferred CPU state.
> obj-$(CONFIG_STEAL_MONITOR) += steal_monitor.o
>
> -steal_monitor-y := sm_core.o
> +steal_monitor-y := sm_core.o defaults.o
> diff --git a/drivers/virt/steal_monitor/defaults.c b/drivers/virt/steal_monitor/defaults.c
> new file mode 100644
> index 000000000000..d4b016317554
> --- /dev/null
> +++ b/drivers/virt/steal_monitor/defaults.c
> @@ -0,0 +1,100 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Base file contains the default implementations.
> + *
> + * Copyright (C) 2026 IBM
> + * Author: Shrikanth Hegde <sshegde@linux.ibm.com>
> + */
> +#include "sm_core.h"
> +
> +/*
> + * Returns steal time of the full system.
> + * Compute collective steal time across all possible CPUs.
> + */
> +u64 get_system_steal_time(void)
> +{
> + int cpu;
> + u64 total_steal = 0;
> +
> + for_each_possible_cpu(cpu)
> + total_steal += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
> +
> + return total_steal;
> +}
> +
> +/*
> + * Returns number of CPUs to consider for steal ratio.
> + * Return possible CPUs.
> + */
> +unsigned int get_num_cpus_steal_ratio(void)
> +{
> + return num_possible_cpus();
> +}
> +
> +/*
> + * Take action to decrease preferred CPUs.
> + *
> + * Decrease the preferred CPUs by 1 core.
> + * Take out the last core in the active & preferred.
> + *
> + * Must ensure
> + * - least one housekeeping core is always kept as preferred
> + * - preferred is always subset of active.
> + */
> +void decrease_preferred_cpus(struct steal_monitor *ctx)
> +{
> + int tmp_cpu, first_hk_cpu, last_cpu;
> + const struct cpumask *first_hk_core;
> + int target_cpu = nr_cpu_ids;
> +
> + guard(cpus_read_lock)();
> + first_hk_cpu = cpumask_first_and(housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
> + cpu_preferred_mask);
Nit: you can return here if nothing found, and save on the 2nd
traverse.
> + last_cpu = cpumask_last(cpu_preferred_mask);
> +
> + if (first_hk_cpu >= nr_cpu_ids || last_cpu >= nr_cpu_ids)
> + return;
> +
> + /* Always leave first housekeeping core as preferred. */
> + first_hk_core = topology_sibling_cpumask(first_hk_cpu);
> +
> + /* Find the last CPU which doesn't belong to that first hk_core. */
> + if (!cpumask_test_cpu(last_cpu, first_hk_core)) {
> + target_cpu = last_cpu;
> + } else {
> + for_each_cpu_andnot(tmp_cpu, cpu_preferred_mask, first_hk_core)
> + target_cpu = tmp_cpu;
> + }
> +
> + /* Only the first housekeeping core remains */
> + if (target_cpu >= nr_cpu_ids)
> + return;
> +
> + for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(target_cpu),
> + cpu_preferred_mask)
> + set_cpu_preferred(tmp_cpu, false);
I think it should return status: if the function can't disable CPUs
now, it would be a good hint for the caller that it would be useless
to call it again.
You may keep status in struct steal_monitor like:
if (steal_ratio > sm_core_ctx.high_threshold) {
if (sm_core_ctx->status | CANT_DECREASE) {
pr_something();
else
sm_core_ctx->status = decrease();
It would be a good hint to user that he has the driver misconfigured,
and save the driver extra work. Same for increase().
> +}
> +
> +/*
> + * Take action to increase preferred CPUs.
> + *
> + * Increase the preferred CPUs by 1 core.
> + * Add the first core in active & !preferred
> + *
> + * Must ensure preferred is subset of active.
> + */
> +void increase_preferred_cpus(struct steal_monitor *ctx)
> +{
> + int first_cpu, tmp_cpu;
> +
> + guard(cpus_read_lock)();
> + first_cpu = cpumask_first_andnot(cpu_active_mask, cpu_preferred_mask);
> +
> + /* All CPUs are preferred. Nothing to increase further */
> + if (first_cpu >= nr_cpu_ids)
> + return;
> +
> + for_each_cpu_and(tmp_cpu, topology_sibling_cpumask(first_cpu),
> + cpu_active_mask)
> + set_cpu_preferred(tmp_cpu, true);
> +}
> diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
> index 8bbb606add99..ee68cd8b1944 100644
> --- a/drivers/virt/steal_monitor/sm_core.h
> +++ b/drivers/virt/steal_monitor/sm_core.h
> @@ -11,6 +11,9 @@
> #include <linux/cpumask.h>
> #include <linux/workqueue.h>
> #include <linux/ktime.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/topology.h>
> +#include <linux/sched/isolation.h>
>
> struct steal_monitor {
> struct delayed_work work;
> @@ -24,4 +27,9 @@ struct steal_monitor {
>
> extern struct steal_monitor sm_core_ctx;
>
> +u64 get_system_steal_time(void);
> +unsigned int get_num_cpus_steal_ratio(void);
> +void increase_preferred_cpus(struct steal_monitor *ctx);
> +void decrease_preferred_cpus(struct steal_monitor *ctx);
> +
> #endif /* __VIRT_STEAL_CORE_H */
> --
> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs
2026-07-09 21:56 ` [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs Shrikanth Hegde
@ 2026-07-10 20:37 ` Yury Norov
0 siblings, 0 replies; 25+ messages in thread
From: Yury Norov @ 2026-07-10 20:37 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 03:26:47AM +0530, Shrikanth Hegde wrote:
> schedule work at regular intervals. Interval is determined by
> interval_ms parameter. schedule_delayed_work is used since interval_ms
> is usually in order of milliseconds. Work need not happen instantly.
>
> Periodic work function essentially does:
> - Calculate the steal_ratio as below.
>
> steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
>
> It is calculated to consider the fractional values of steal time.
> I.e 10 means 0.1% steal time. A few tricks such as divide by 10,000
> are used to avoid possible overflow.
> - If steal value is higher than high threshold, call the method to reduce
> the preferred CPUs.
> - If steal value is lower or equal to low threshold, call the method to
> increase the preferred CPUs.
> - If the steal value is in between, no action is taken.
> - Save the values for next delta calculations.
> - Save the current direction of steal values to avoid oscillations.
> So two consecutive values of high values or low values are taken for
> decrease/increase of preferred CPUs.
> - Ensure design checks are met.
> 1. At least one core/CPU must be there in preferred mask.
> 2. preferred CPUs is subset of active CPUs.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> v6->v7:
> - Merge two patches which did periodic work function.
> - Misc checks for early firing, requeue work, math safety.
>
> drivers/virt/steal_monitor/sm_core.c | 76 +++++++++++++++++++++++++++-
> drivers/virt/steal_monitor/sm_core.h | 1 +
> 2 files changed, 76 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
> index 4a03c14337be..09a5c3a299c3 100644
> --- a/drivers/virt/steal_monitor/sm_core.c
> +++ b/drivers/virt/steal_monitor/sm_core.c
> @@ -20,6 +20,12 @@ struct steal_monitor sm_core_ctx = {
> .low_threshold = 200, /* 2% */
> };
>
> +enum sm_direction {
> + SM_DIR_INCREASE = -1,
> + SM_DIR_NONE = 0,
> + SM_DIR_DECREASE = 1,
> +};
> +
> static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
> {
> unsigned int interval;
> @@ -106,14 +112,82 @@ module_param_cb(low_threshold, &low_threshold_ops, &sm_core_ctx.low_threshold, 0
> MODULE_PARM_DESC(low_threshold,
> "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
>
> +static void compute_preferred_cpus_work(struct work_struct *work)
> +{
> + u64 curr_steal, delta_steal, delta_ns, steal_ratio;
> + ktime_t now;
> +
> + now = ktime_get();
> + delta_ns = ktime_to_ns(ktime_sub(now, sm_core_ctx.prev_time));
> +
> + if (unlikely(delta_ns < NSEC_PER_MSEC)) {
> + pr_err_ratelimited("steal_monitor: work scheduled too soon delta_ns: %llu\n",
> + delta_ns);
> + goto requeue_work;
> + }
> +
> + curr_steal = get_system_steal_time();
> + delta_steal = curr_steal > sm_core_ctx.prev_steal ?
> + curr_steal - sm_core_ctx.prev_steal : 0;
> +
> + /* Update for next calculation */
> + sm_core_ctx.prev_steal = curr_steal;
> + sm_core_ctx.prev_time = now;
> +
> + /*
> + * steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
> + * To avoid possible overflow, divide the denominator early.
> + * Note minimum interval is 10ms.
> + */
> + delta_ns = div_u64(delta_ns * get_num_cpus_steal_ratio(), 100 * 100);
> + steal_ratio = div64_u64(delta_steal, delta_ns);
> +
> + if (sm_core_ctx.prev_direction == SM_DIR_DECREASE &&
> + steal_ratio > sm_core_ctx.high_threshold)
> + decrease_preferred_cpus(&sm_core_ctx);
> + if (sm_core_ctx.prev_direction == SM_DIR_INCREASE &&
> + steal_ratio <= sm_core_ctx.low_threshold)
> + increase_preferred_cpus(&sm_core_ctx);
I already said, I don't like this SM_DIR approach. If you want to
avoid oscillations, just increase the gap. If it doesn't work, then we
need to understand why.
> +
> + /*
> + * mark the direction. Increasing the gap between hi and lo_threshold
> + * helps to avoid ping-pongs.
> + */
> + if (steal_ratio > sm_core_ctx.high_threshold)
> + sm_core_ctx.prev_direction = SM_DIR_DECREASE;
> + else if (steal_ratio <= sm_core_ctx.low_threshold)
> + sm_core_ctx.prev_direction = SM_DIR_INCREASE;
> + else
> + sm_core_ctx.prev_direction = SM_DIR_NONE;
> +
> +requeue_work:
> + /* maintain design constructs always */
> + WARN_ON_ONCE(cpumask_empty(cpu_preferred_mask));
> + WARN_ON_ONCE(!cpumask_subset(cpu_preferred_mask, cpu_active_mask));
cpu_read_lock here? And again, you should do something to restore
integrity. WARN_ON is not enough. The simplest and safest thing you
can do is to unload the driver. You definitely shouldn't schedule a
new work against the broken cpu_preferred_mask.
> +
> + /* Trigger for next sampling */
> + schedule_delayed_work(&sm_core_ctx.work,
> + msecs_to_jiffies(sm_core_ctx.interval_ms));
> +}
> +
> static int __init steal_monitor_init(void)
> {
> - pr_info("steal_monitor is enabled\n");
> + pr_info("steal_monitor is enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
> + sm_core_ctx.interval_ms, sm_core_ctx.high_threshold, sm_core_ctx.low_threshold);
> +
> + INIT_DELAYED_WORK(&sm_core_ctx.work, compute_preferred_cpus_work);
> + sm_core_ctx.prev_steal = get_system_steal_time();
> + sm_core_ctx.prev_time = ktime_get();
> +
> + schedule_delayed_work(&sm_core_ctx.work,
> + msecs_to_jiffies(sm_core_ctx.interval_ms));
> +
> return 0;
> }
>
> static void __exit steal_monitor_exit(void)
> {
> + cancel_delayed_work_sync(&sm_core_ctx.work);
cancel_delayed_work_sync() is not enough for a self-requeueing work.
compute_preferred_cpus_work() always requeues itself. Module unload
can return with delayed work armed against module text/data. Use
disable_delayed_work_sync() or a stop flag checked before requeueing.
> guard(cpus_read_lock)();
> cpumask_copy(&__cpu_preferred_mask, cpu_active_mask);
>
> diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
> index ee68cd8b1944..7c7a9bced682 100644
> --- a/drivers/virt/steal_monitor/sm_core.h
> +++ b/drivers/virt/steal_monitor/sm_core.h
> @@ -14,6 +14,7 @@
> #include <linux/kernel_stat.h>
> #include <linux/topology.h>
> #include <linux/sched/isolation.h>
> +#include <linux/math64.h>
>
> struct steal_monitor {
> struct delayed_work work;
> --
> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 08/12] virt: Introduce steal monitor driver
2026-07-09 21:56 ` [PATCH v7 08/12] virt: Introduce steal monitor driver Shrikanth Hegde
2026-07-10 1:27 ` Randy Dunlap
@ 2026-07-10 20:53 ` Yury Norov
1 sibling, 0 replies; 25+ messages in thread
From: Yury Norov @ 2026-07-10 20:53 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 03:26:44AM +0530, Shrikanth Hegde wrote:
> Introduce a new driver in virt named steal_monitor. This driver
> will compute the steal time and drive the policy decisions of preferred
> CPU state.
>
> More on it can be found in the Documentation/driver-api/steal-monitor.rst
>
> There is a new kconfig called STEAL_MONITOR. Having that driver is going
> to select PREFERRED_CPU. This makes configs driven by user preference.
> It is recommended to build it as module and let user load the module.
>
> When the module is disabled, preferred is same as active.
>
> File layout of the driver is designed with having arch specific
> files in the future.
>
> - sm_core.c - contains main driver code. This includes the periodic
> work function and take action on steal time.
> - defaults.c - contains the functions used for handling steal values.
> - sm_core.h - header file which includes data structure.
>
> Main structure of steal monitor has,
> - work: deferred periodic work function
> - prev_steal, prev_time: To calculate the delta in periodic work.
> - interval_ms, high_threshold, low_threshold: debug knobs of
> steal_monitor.
> - prev_direction: Simple direction control to avoid oscillations.
>
> While there, Add MAINTAINERS entry for this new driver.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> v6->v7:
> - Combined all introductory patches.
> - Introduce STEAL_MONITOR which selects PREFERRED_CPU.
> - Added MAINTAINERS entry.
> - Yury, I have kept you as reviewer entry, Let me know if it needs to
> change.
>
> Documentation/driver-api/index.rst | 1 +
> Documentation/driver-api/steal-monitor.rst | 111 +++++++++++++++++++++
> MAINTAINERS | 9 ++
> drivers/virt/Kconfig | 2 +
> drivers/virt/Makefile | 1 +
> drivers/virt/steal_monitor/Kconfig | 18 ++++
> drivers/virt/steal_monitor/Makefile | 6 ++
> drivers/virt/steal_monitor/sm_core.c | 38 +++++++
> drivers/virt/steal_monitor/sm_core.h | 27 +++++
> 9 files changed, 213 insertions(+)
> create mode 100644 Documentation/driver-api/steal-monitor.rst
> create mode 100644 drivers/virt/steal_monitor/Kconfig
> create mode 100644 drivers/virt/steal_monitor/Makefile
> create mode 100644 drivers/virt/steal_monitor/sm_core.c
> create mode 100644 drivers/virt/steal_monitor/sm_core.h
>
> diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
> index eaf7161ff957..ec12f396a5e6 100644
> --- a/Documentation/driver-api/index.rst
> +++ b/Documentation/driver-api/index.rst
> @@ -138,6 +138,7 @@ Subsystem-specific APIs
> sm501
> soundwire/index
> spi
> + steal-monitor
> surface_aggregator/index
> switchtec
> sync_file
> diff --git a/Documentation/driver-api/steal-monitor.rst b/Documentation/driver-api/steal-monitor.rst
> new file mode 100644
> index 000000000000..94f4aa1aaa7d
> --- /dev/null
> +++ b/Documentation/driver-api/steal-monitor.rst
> @@ -0,0 +1,111 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +=============
> +Steal Monitor
> +=============
> +
> +:Author: Shrikanth Hegde
> +
> +Introduction
> +============
> +
> +Steal monitor is a driver aimed at solving the Noisy Neighbour problem
> +in virtualized environments. The performance of workload
> +running in one VM gets affected significantly due to other VMs and
> +combined they make slower forward progress.
> +
> +When there is overcommit of CPU resources, i.e. sum of virtual CPUs (vCPUs)
> +of all VMs is greater than number of physical CPUs (pCPUs) and
> +when all or many VMs have high utilization, hypervisor won't be able
> +to satisfy the CPU requirement and has to context switch within or
> +across VMs. I.e. the hypervisor needs to preempt one vCPU to run
> +another. This is called vCPU preemption.
> +This is more expensive compared to task context switch within a vCPU.
> +
> +In such cases it is better that combined vCPU ask from all VMs is reduced
> +by not using some of the vCPUs. vCPUs where workload can be safely
> +scheduled which won't increase any contention for pCPU are called as
> +"Preferred CPUs".
> +
> +See more on "Preferred CPUs" in Documentation/scheduler/sched-arch.rst.
> +
> +This driver makes CONFIG_PREFERRED_CPU=y which enables the scheduler core
> +infrastructure to move tasks to Preferred CPUs where possible.
> +
> +Core idea
> +=========
> +steal time is an indication available today in Guest which shows contention
> +for underlying physical CPU. Use it as a hint in the guest to fold the
> +workload to a reduced set of vCPUs. When there is contention, steal time
> +will show up in all the guests. When each guest honors the hint and folds
> +the workload to a smaller set of vCPUs (Preferred CPUs), it reduces the
> +contention and thereby reduces vCPU preemption.
> +This is achieved without any cross-guest communication.
> +
> +Steal monitor driver effectively does:
> +
> +1. Periodically computes steal time across the system.
> +
> +2. If steal time is greater than high threshold, reduce the number of
> + preferred CPUs by 1 core. Ensure at least one core is left always.
> + This avoids running into extreme cases.
> +
> +3. If steal time is lower or equal to low threshold, increase the
> + number of preferred CPUs by 1 core. If preferred is same as active,
> + nothing to be done.
> +
> +4. Ensure preferred CPUs is always subset of active CPUs.
> + On feature disable it is same as active CPUs.
> +
> +This feature works best only when all the VMs enable the feature as
> +it is a co-operative scheme. If a specific VM doesn't enable this feature
> +it may end up with more CPUs than others, still should lead to better
> +performance when seen from system view.
> +Those who enable this driver must ensure it is enabled in all VMs.
> +
> +Module Parameters
> +=================
> +interval_ms
> +-----------
> +How often steal monitor checks for steal time.
> +Default: 1000 i.e 1 second. Value should be in between 10ms to 100sec.
> +
> +This controls how fast steal monitor driver reacts to changes to
> +the contention of physical CPUs. Since it does a fair amount of
> +work, setting too low will have overheads. Setting it too
> +high might render it ineffective.
> +
> +low_threshold
> +-------------
> +lower threshold value in percentage * 100.
> +Default: 200, i.e 2% steal is considered as low threshold.
> +Can't be higher than high_threshold.
> +
> +This determines what values should be considered as nil/no steal values.
> +When steal monitor see steal time is below or equal to this value, it
> +will increase the preferred CPUs by 1 core. Having value as zero
> +might cause oscillations.
> +
> +high_threshold
> +--------------
> +higher threshold value in percentage * 100
> +Default: 500, i.e 5% steal is considered as high threshold.
> +Can't be lower than low_threshold. Must be less than 10000.
> +
> +This determines what values should be considered as high steal values.
> +When steal monitor sees steal time is higher than this value, it will
> +reduce the preferred CPUs by 1 core.
> +
> +Notes
> +=====
> +Selecting this driver makes CONFIG_PREFERRED_CPU=y. That makes configs
> +driven by user preference.
> +
> +It is recommended to build CONFIG_STEAL_MONITOR=m due ot below reasons:
> +
> +1. Doing periodic work has additional overheads. Enabling this driver
> + in systems where steal time cannot happen is of no use. There is no
> + benefit with additional overheads in such systems.
> +
> +2. This works well when all VMs work in co-operative manner. When an
> + administrative user enables it in one VM, he/she will likely enable
> + it all VMs.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 15011f5752a9..6735f9dae530 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25914,6 +25914,15 @@ F: rust/helpers/jump_label.c
> F: rust/kernel/generated_arch_static_branch_asm.rs.S
> F: rust/kernel/jump_label.rs
>
> +STEAL TIME MONITOR DRIVER
> +M: Shrikanth Hegde <sshegde@linux.ibm.com>
> +R: Yury Norov <yury.norov@gmail.com>
> +L: linux-kernel@vger.kernel.org
> +S: Maintained
> +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/core
> +F: Documentation/driver-api/steal-monitor.rst
> +F: drivers/virt/steal_monitor/
> +
> STI AUDIO (ASoC) DRIVERS
> M: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> L: linux-sound@vger.kernel.org
> diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig
> index 52eb7e4ba71f..a52233b2502e 100644
> --- a/drivers/virt/Kconfig
> +++ b/drivers/virt/Kconfig
> @@ -47,6 +47,8 @@ source "drivers/virt/nitro_enclaves/Kconfig"
>
> source "drivers/virt/acrn/Kconfig"
>
> +source "drivers/virt/steal_monitor/Kconfig"
> +
> endif
>
> source "drivers/virt/coco/Kconfig"
> diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile
> index f29901bd7820..b67fd8968ec3 100644
> --- a/drivers/virt/Makefile
> +++ b/drivers/virt/Makefile
> @@ -9,4 +9,5 @@ obj-y += vboxguest/
>
> obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/
> obj-$(CONFIG_ACRN_HSM) += acrn/
> +obj-$(CONFIG_STEAL_MONITOR) += steal_monitor/
> obj-y += coco/
> diff --git a/drivers/virt/steal_monitor/Kconfig b/drivers/virt/steal_monitor/Kconfig
> new file mode 100644
> index 000000000000..c7d7599c30ce
> --- /dev/null
> +++ b/drivers/virt/steal_monitor/Kconfig
> @@ -0,0 +1,18 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config STEAL_MONITOR
The config should go in the last patch of the series. Otherwise,
in case of bisection, you'll have half-written driver enabled by
default.
> + tristate "Dynamic vCPU management based on steal time"
> + depends on PARAVIRT && SMP
> + select PREFERRED_CPU
> + default m
> + help
> + This driver helps to reduce the steal time in paravirtualised
> + environment, thereby reducing vCPU preemption. Reducing vCPU
> + preemption provides improved lock holder preemption and reduces
> + cost of vCPU preemption in the host.
> +
> + By default preferred CPUs will be same as active CPUs. Depending
> + on the steal time when steal_monitor driver is enabled,
> + preferred CPUs could become subset of active CPUs.
> +
> + It is recommended to build it as module and load the module
> + to enable it.
> diff --git a/drivers/virt/steal_monitor/Makefile b/drivers/virt/steal_monitor/Makefile
> new file mode 100644
> index 000000000000..bd7d120a79b5
> --- /dev/null
> +++ b/drivers/virt/steal_monitor/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Steal time monitor to alter preferred CPU state.
> +obj-$(CONFIG_STEAL_MONITOR) += steal_monitor.o
> +
> +steal_monitor-y := sm_core.o
> diff --git a/drivers/virt/steal_monitor/sm_core.c b/drivers/virt/steal_monitor/sm_core.c
> new file mode 100644
> index 000000000000..180db424846c
> --- /dev/null
> +++ b/drivers/virt/steal_monitor/sm_core.c
> @@ -0,0 +1,38 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Steal time Monitor.
> + *
> + * Periodically compute steal time. Based on the thresholds either
> + * reduce/increase the preferred CPUs which can be used
> + * by the workload to avoid vCPU preemption to an extent possible.
> + *
> + * Available as module with CONFIG_STEAL_MONITOR=m
> + *
> + * Copyright (C) 2026 IBM
> + * Author: Shrikanth Hegde <sshegde@linux.ibm.com>
> + */
> +
> +#include "sm_core.h"
> +
> +struct steal_monitor sm_core_ctx;
> +
> +static int __init steal_monitor_init(void)
> +{
> + pr_info("steal_monitor is enabled\n");
> + return 0;
> +}
> +
> +static void __exit steal_monitor_exit(void)
> +{
> + guard(cpus_read_lock)();
> + cpumask_copy(&__cpu_preferred_mask, cpu_active_mask);
> +
> + pr_info("steal_monitor is disabled\n");
> +}
> +
> +module_init(steal_monitor_init);
> +module_exit(steal_monitor_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("IBM Corporation");
> +MODULE_DESCRIPTION("Virtualization Steal Time Monitor");
> diff --git a/drivers/virt/steal_monitor/sm_core.h b/drivers/virt/steal_monitor/sm_core.h
> new file mode 100644
> index 000000000000..8bbb606add99
> --- /dev/null
> +++ b/drivers/virt/steal_monitor/sm_core.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef __VIRT_STEAL_CORE_H
> +#define __VIRT_STEAL_CORE_H
> +
> +#include <linux/types.h>
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/cpuhplock.h>
> +#include <linux/cpumask.h>
> +#include <linux/workqueue.h>
> +#include <linux/ktime.h>
> +
> +struct steal_monitor {
> + struct delayed_work work;
> + u64 prev_steal;
> + int prev_direction;
Did you run pahole on it?
> + unsigned int interval_ms;
> + unsigned int high_threshold;
> + unsigned int low_threshold;
> + ktime_t prev_time;
This 'prev_' prefix is useless and distracting. Just drop it.
> +};
> +
> +extern struct steal_monitor sm_core_ctx;
> +
> +#endif /* __VIRT_STEAL_CORE_H */
> --
> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 06/12] sched/core: Push current task from non preferred CPU
2026-07-09 21:56 ` [PATCH v7 06/12] sched/core: Push current task from non preferred CPU Shrikanth Hegde
@ 2026-07-10 23:02 ` Yury Norov
0 siblings, 0 replies; 25+ messages in thread
From: Yury Norov @ 2026-07-10 23:02 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 03:26:42AM +0530, Shrikanth Hegde wrote:
> Actively push out task running on a non-preferred CPU. Since the task is
> running on the CPU, need to stop the cpu and push the task out.
> However, if the task is pinned only to non-preferred CPUs, it will continue
> running there. This will help in maintaining the userspace affinities
> unlike CPU hotplug or isolated cpusets.
>
> Though code is similar to __balance_push_cpu_stop and quite close to
> push_cpu_stop, it is being kept separate as it provides a cleaner
> implementation with CONFIG_PREFERRED_CPU.
>
> Add push_task_work_done flag to protect work buffer.
> Works only with FAIR class.
>
> For now, only current running task is pushed out. This keeps the code
> simpler. In future optimization maybe done to move all the queued
> task on the rq.
>
> Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> ---
> v6->v7:
> - Moved is_migration_disabled
> - removed fair class check
>
> kernel/sched/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
> kernel/sched/sched.h | 8 +++++
> 2 files changed, 86 insertions(+)
>
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index 9e8eec4451b6..74c93a88bf84 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -5774,6 +5774,9 @@ void sched_tick(void)
> unsigned long hw_pressure;
> u64 resched_latency;
>
> + if (!cpu_preferred(cpu))
> + sched_push_current_non_preferred_cpu(rq);
> +
> if (housekeeping_cpu(cpu, HK_TYPE_KERNEL_NOISE))
> arch_scale_freq_tick();
>
> @@ -11292,3 +11295,78 @@ void sched_change_end(struct sched_change_ctx *ctx)
> p->sched_class->prio_changed(rq, p, ctx->prio);
> }
> }
> +
> +#ifdef CONFIG_PREFERRED_CPU
> +static DEFINE_PER_CPU(struct cpu_stop_work, npc_push_task_work);
> +
> +static int sched_non_preferred_cpu_push_stop(void *arg)
> +{
> + struct task_struct *p = arg;
> + struct rq *rq = this_rq();
> + struct rq_flags rf;
> + int cpu;
> +
> + /* sanity checks and clear */
> + if (cpu_preferred(rq->cpu) || is_migration_disabled(p)) {
> + scoped_guard(rq_lock, rq)
> + rq->push_task_work_done = false;
> + put_task_struct(p);
> + return 0;
> + }
> +
> + raw_spin_lock_irq(&p->pi_lock);
> +
> + /* This could take rq lock. So call it before rq lock is taken */
> + cpu = select_fallback_rq(rq->cpu, p);
> + rq_lock(rq, &rf);
> + rq->push_task_work_done = false;
> + update_rq_clock(rq);
> +
> + context_unsafe_alias(rq);
> +
> + if (task_rq(p) == rq && task_on_rq_queued(p))
> + rq = __migrate_task(rq, &rf, p, cpu);
> +
> + rq_unlock(rq, &rf);
> + raw_spin_unlock_irq(&p->pi_lock);
> + put_task_struct(p);
> +
> + return 0;
> +}
> +
> +/*
> + * Push the current task running on non-preferred CPU(npc).
> + * Using this non preferred CPU will lead to more vCPU preemptions
> + * in the host. So it is better not to use this CPU.
> + *
> + * Since task is running, call a stopper to push the task out. This is
> + * similar to how task moves during hotplug. In select_fallback_rq a
> + * preferred CPU will be chosen and henceforth task shouldn't come back to
> + * this CPU again.
> + *
> + * Works for FAIR class only
> + *
> + * If task is affined only non-preferred CPUs, it can't be moved out
> + */
> +void sched_push_current_non_preferred_cpu(struct rq *rq)
> +{
> + struct task_struct *push_task = rq->curr;
> +
> + /* Don't push the task if task's affinity doesn't allow */
> + if (!task_can_sched_on_preferred(rq->cpu, push_task))
> + return;
Shouldn't you protect it with the rq lock against races with affinity or
policy changes? The task_can_sched_on_preferred() checks p->sched_class
and p->cpus_ptr.
> +
> + /* There is already a stopper thread. Don't race with it. */
> + if (rq->push_task_work_done)
> + return;
> +
> + /* sched_tick runs with interrupts disabled. */
> + get_task_struct(push_task);
> +
> + scoped_guard(rq_lock, rq)
> + rq->push_task_work_done = true;
> +
> + stop_one_cpu_nowait(rq->cpu, sched_non_preferred_cpu_push_stop,
> + push_task, this_cpu_ptr(&npc_push_task_work));
> +}
> +#endif
> diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
> index 6de6366f2faa..80c02e2c09eb 100644
> --- a/kernel/sched/sched.h
> +++ b/kernel/sched/sched.h
> @@ -1277,6 +1277,8 @@ struct rq {
>
> struct list_head cfs_tasks;
>
> + bool push_task_work_done;
> +
> struct sched_avg avg_rt;
> struct sched_avg avg_dl;
> #ifdef CONFIG_HAVE_SCHED_AVG_IRQ
> @@ -4242,4 +4244,10 @@ static inline bool task_can_sched_on_preferred(int cpu, struct task_struct *p)
> return cpumask_intersects(p->cpus_ptr, cpu_preferred_mask);
> }
>
> +#ifdef CONFIG_PREFERRED_CPU
> +void sched_push_current_non_preferred_cpu(struct rq *rq);
> +#else /* !CONFIG_PREFERRED_CPU */
> +static inline void sched_push_current_non_preferred_cpu(struct rq *rq) { }
> +#endif
> +
> #endif /* _KERNEL_SCHED_SCHED_H */
> --
> 2.47.3
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v7 03/12] sysfs: Add preferred CPU file
2026-07-10 17:00 ` Shrikanth Hegde
@ 2026-07-10 23:09 ` Yury Norov
0 siblings, 0 replies; 25+ messages in thread
From: Yury Norov @ 2026-07-10 23:09 UTC (permalink / raw)
To: Shrikanth Hegde
Cc: linux-kernel, mingo, peterz, juri.lelli, vincent.guittot,
yury.norov, kprateek.nayak, iii, corbet, tglx, gregkh, pbonzini,
seanjc, vschneid, huschle, rostedt, dietmar.eggemann, maddy,
srikar, hdanton, chleroy, vineeth, frederic, arighi, pauld,
christian.loehle, tj, tommaso.cucinotta, maz, rafael, rdunlap,
kernellwp, linux-doc
On Fri, Jul 10, 2026 at 10:30:43PM +0530, Shrikanth Hegde wrote:
> Hi Yury, thanks for taking a look.
>
> On 7/10/26 9:11 PM, Yury Norov wrote:
> > On Fri, Jul 10, 2026 at 03:26:39AM +0530, Shrikanth Hegde wrote:
> > > Add "preferred" file in /sys/devices/system/cpu
> > >
> > > This offers
> > > - User can quickly check which CPUs are marked as preferred at this
> > > moment.
> > > - Userspace algorithms irqbalance could use this mask to send irq into
> > > preferred CPUs.
> > >
> > > For example:
> > > cat /sys/devices/system/cpu/online
> > > 0-719
> > > cat /sys/devices/system/cpu/preferred
> > > 0-599 <<< Implies 0-599 are preferred for workloads and 600-719
> > > should be avoided at this moment.
> > >
> > > cat /sys/devices/system/cpu/preferred
> > > 0-719 <<< All CPUs are usable. There is no preference.
> > >
> > > Signed-off-by: Shrikanth Hegde <sshegde@linux.ibm.com>
> > > ---
> > > Documentation/ABI/testing/sysfs-devices-system-cpu | 11 +++++++++++
> > > drivers/base/cpu.c | 8 ++++++++
> > > 2 files changed, 19 insertions(+)
> > >
> > > diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
> > > index 82d10d556cc8..ac1dbb209cc7 100644
> > > --- a/Documentation/ABI/testing/sysfs-devices-system-cpu
> > > +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
> > > @@ -806,3 +806,14 @@ Date: Nov 2022
> > > Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
> > > Description:
> > > (RO) the list of CPUs that can be brought online.
> > > +
> > > +What: /sys/devices/system/cpu/preferred
> > > +Date: July 2026
> > > +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
> > > +Description:
> > > + (RO) the list of preferred CPUs at this moment.
> > > + These are the only CPUs meant to be used at the moment.
> > > + Using CPU outside of the list could lead to more
> > > + contention of underlying physical CPU resource. Dynamically
> > > + changes based on steal time. With CONFIG_PREFERRED_CPU=n it
> > > + is same as active CPUs. See sched-arch.rst for more details.
> >
> > This should mention that it's about paravirtualization.
>
> Ok. I will rephrase it.
>
> >
> > > diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
> > > index 19d288a3c80c..4ac990efee7c 100644
> > > --- a/drivers/base/cpu.c
> > > +++ b/drivers/base/cpu.c
> > > @@ -391,6 +391,13 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
> > > }
> > > #endif
> > > +static ssize_t preferred_show(struct device *dev,
> > > + struct device_attribute *attr, char *buf)
> > > +{
> > > + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_preferred_mask));
> > > +}
> > > +static DEVICE_ATTR_RO(preferred);
> > > +
> > > const struct bus_type cpu_subsys = {
> > > .name = "cpu",
> > > .dev_name = "cpu",
> > > @@ -532,6 +539,7 @@ static struct attribute *cpu_root_attrs[] = {
> > > #ifdef CONFIG_GENERIC_CPU_AUTOPROBE
> > > &dev_attr_modalias.attr,
> > > #endif
> > > + &dev_attr_preferred.attr,
> >
> > #ifdef CONFIG_PREFERRED_CPUS ?
>
> Not needed no? It will print active CPUs.
If I didn't enable preferred CPUs, I'll be pretty surprised having
them in my statistics.
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2026-07-10 23:09 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 21:56 [PATCH v7 00/12] sched, steal_monitor: Introduce cpu_preferred_mask and steal-driven vCPU backoff Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 01/12] sched/docs: Document cpu_preferred_mask and Preferred CPU concept Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 02/12] cpumask: Introduce cpu_preferred_mask Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 03/12] sysfs: Add preferred CPU file Shrikanth Hegde
2026-07-10 15:41 ` Yury Norov
2026-07-10 17:00 ` Shrikanth Hegde
2026-07-10 23:09 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 04/12] sched/core: Try to use a preferred CPU in is_cpu_allowed Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 05/12] sched/fair: Load balance only among preferred CPUs Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 06/12] sched/core: Push current task from non preferred CPU Shrikanth Hegde
2026-07-10 23:02 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 07/12] sched/debug: Add migration stats due to non preferred CPUs Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 08/12] virt: Introduce steal monitor driver Shrikanth Hegde
2026-07-10 1:27 ` Randy Dunlap
2026-07-10 4:28 ` Shrikanth Hegde
2026-07-10 20:53 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 09/12] virt/steal_monitor: Add control knobs for handling steal values Shrikanth Hegde
2026-07-10 4:27 ` Shrikanth Hegde
2026-07-09 21:56 ` [PATCH v7 10/12] virt/steal_monitor: Provide functions for managing " Shrikanth Hegde
2026-07-10 4:30 ` Shrikanth Hegde
2026-07-10 20:00 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 11/12] virt/steal_monitor: Act on steal time periodically and decide on preferred CPUs Shrikanth Hegde
2026-07-10 20:37 ` Yury Norov
2026-07-09 21:56 ` [PATCH v7 12/12] sched, virt/steal_monitor: Keep tick on for faster push on nohz_full CPU Shrikanth Hegde
2026-07-10 6:35 ` Shrikanth Hegde
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox