19a8e740eSShrikanth Hegde // SPDX-License-Identifier: GPL-2.0-only
29a8e740eSShrikanth Hegde /*
39a8e740eSShrikanth Hegde * Steal time governor driver periodically computes steal time.
49a8e740eSShrikanth Hegde * Based on the thresholds it either reduce/increase the preferred
59a8e740eSShrikanth Hegde * CPUs which can be used by the workload to avoid vCPU preemption
69a8e740eSShrikanth Hegde * to an extent possible in paravirtualized environment.
79a8e740eSShrikanth Hegde *
89a8e740eSShrikanth Hegde * Available with CONFIG_STEAL_GOVERNOR
99a8e740eSShrikanth Hegde *
109a8e740eSShrikanth Hegde * Copyright (C) 2026 IBM
119a8e740eSShrikanth Hegde * Author: Shrikanth Hegde <sshegde@linux.ibm.com>
129a8e740eSShrikanth Hegde */
139a8e740eSShrikanth Hegde
149a8e740eSShrikanth Hegde #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
159a8e740eSShrikanth Hegde
16*27d47ebcSShrikanth Hegde #include <linux/cleanup.h>
179a8e740eSShrikanth Hegde #include <linux/cpuhplock.h>
189a8e740eSShrikanth Hegde #include <linux/cpumask.h>
199a8e740eSShrikanth Hegde #include <linux/init.h>
209a8e740eSShrikanth Hegde #include <linux/kernel.h>
21*27d47ebcSShrikanth Hegde #include <linux/kernel_stat.h>
229a8e740eSShrikanth Hegde #include <linux/kconfig.h>
239a8e740eSShrikanth Hegde #include <linux/ktime.h>
24*27d47ebcSShrikanth Hegde #include <linux/math64.h>
259a8e740eSShrikanth Hegde #include <linux/module.h>
26*27d47ebcSShrikanth Hegde #include <linux/sched/isolation.h>
27*27d47ebcSShrikanth Hegde #include <linux/topology.h>
289a8e740eSShrikanth Hegde #include <linux/types.h>
299a8e740eSShrikanth Hegde #include <linux/workqueue.h>
309a8e740eSShrikanth Hegde #ifdef CONFIG_XEN
319a8e740eSShrikanth Hegde #include <xen/xen.h>
329a8e740eSShrikanth Hegde #endif
339a8e740eSShrikanth Hegde
349a8e740eSShrikanth Hegde #if !IS_ENABLED(CONFIG_PREFERRED_CPU)
359a8e740eSShrikanth Hegde #error "Steal Governor requires CONFIG_PREFERRED_CPU"
369a8e740eSShrikanth Hegde #endif
379a8e740eSShrikanth Hegde
389a8e740eSShrikanth Hegde struct steal_governor {
399a8e740eSShrikanth Hegde ktime_t time;
409a8e740eSShrikanth Hegde u64 steal;
419a8e740eSShrikanth Hegde unsigned long delay;
429a8e740eSShrikanth Hegde unsigned int interval_ms;
439a8e740eSShrikanth Hegde unsigned int high_threshold;
449a8e740eSShrikanth Hegde unsigned int low_threshold;
459a8e740eSShrikanth Hegde struct delayed_work work;
469a8e740eSShrikanth Hegde };
479a8e740eSShrikanth Hegde
484b9302d4SShrikanth Hegde static struct steal_governor sg_ctx = {
494b9302d4SShrikanth Hegde .interval_ms = 1000, /* 1 second */
504b9302d4SShrikanth Hegde .high_threshold = 500, /* 5% */
514b9302d4SShrikanth Hegde .low_threshold = 200, /* 2% */
524b9302d4SShrikanth Hegde };
539a8e740eSShrikanth Hegde
restore_preferred_to_active(void)549a8e740eSShrikanth Hegde static void restore_preferred_to_active(void)
559a8e740eSShrikanth Hegde {
569a8e740eSShrikanth Hegde int cpu;
579a8e740eSShrikanth Hegde
589a8e740eSShrikanth Hegde guard(cpus_read_lock)();
599a8e740eSShrikanth Hegde for_each_cpu(cpu, cpu_active_mask)
609a8e740eSShrikanth Hegde set_cpu_preferred(cpu, true);
619a8e740eSShrikanth Hegde }
629a8e740eSShrikanth Hegde
param_set_interval_ms(const char * val,const struct kernel_param * kp)634b9302d4SShrikanth Hegde static int param_set_interval_ms(const char *val, const struct kernel_param *kp)
644b9302d4SShrikanth Hegde {
654b9302d4SShrikanth Hegde unsigned int interval;
664b9302d4SShrikanth Hegde int ret;
674b9302d4SShrikanth Hegde
684b9302d4SShrikanth Hegde ret = kstrtouint(val, 0, &interval);
694b9302d4SShrikanth Hegde if (ret)
704b9302d4SShrikanth Hegde return ret;
714b9302d4SShrikanth Hegde
724b9302d4SShrikanth Hegde if (interval < 100 || interval > 100000) {
734b9302d4SShrikanth Hegde pr_err("interval_ms must be between 100 and 100000\n");
744b9302d4SShrikanth Hegde return -EINVAL;
754b9302d4SShrikanth Hegde }
764b9302d4SShrikanth Hegde
774b9302d4SShrikanth Hegde return param_set_uint(val, kp);
784b9302d4SShrikanth Hegde }
794b9302d4SShrikanth Hegde
804b9302d4SShrikanth Hegde static const struct kernel_param_ops interval_ms_ops = {
814b9302d4SShrikanth Hegde .set = param_set_interval_ms,
824b9302d4SShrikanth Hegde .get = param_get_uint,
834b9302d4SShrikanth Hegde };
844b9302d4SShrikanth Hegde
854b9302d4SShrikanth Hegde module_param_cb(interval_ms, &interval_ms_ops, &sg_ctx.interval_ms, 0444);
864b9302d4SShrikanth Hegde MODULE_PARM_DESC(interval_ms,
874b9302d4SShrikanth Hegde "Sampling frequency in milliseconds. default: 1000");
884b9302d4SShrikanth Hegde
param_set_high_threshold(const char * val,const struct kernel_param * kp)894b9302d4SShrikanth Hegde static int param_set_high_threshold(const char *val, const struct kernel_param *kp)
904b9302d4SShrikanth Hegde {
914b9302d4SShrikanth Hegde unsigned int threshold;
924b9302d4SShrikanth Hegde int ret;
934b9302d4SShrikanth Hegde
944b9302d4SShrikanth Hegde ret = kstrtouint(val, 0, &threshold);
954b9302d4SShrikanth Hegde if (ret)
964b9302d4SShrikanth Hegde return ret;
974b9302d4SShrikanth Hegde
984b9302d4SShrikanth Hegde if (threshold >= 100 * 100) {
994b9302d4SShrikanth Hegde pr_err("high_threshold (%u) can't be more than 99.99%%\n", threshold);
1004b9302d4SShrikanth Hegde return -EINVAL;
1014b9302d4SShrikanth Hegde }
1024b9302d4SShrikanth Hegde
1034b9302d4SShrikanth Hegde return param_set_uint(val, kp);
1044b9302d4SShrikanth Hegde }
1054b9302d4SShrikanth Hegde
1064b9302d4SShrikanth Hegde static const struct kernel_param_ops high_threshold_ops = {
1074b9302d4SShrikanth Hegde .set = param_set_high_threshold,
1084b9302d4SShrikanth Hegde .get = param_get_uint,
1094b9302d4SShrikanth Hegde };
1104b9302d4SShrikanth Hegde
1114b9302d4SShrikanth Hegde module_param_cb(high_threshold, &high_threshold_ops, &sg_ctx.high_threshold, 0444);
1124b9302d4SShrikanth Hegde MODULE_PARM_DESC(high_threshold,
1134b9302d4SShrikanth Hegde "High steal threshold. default: 500 i.e 5%. Must be > low_threshold");
1144b9302d4SShrikanth Hegde
1154b9302d4SShrikanth Hegde module_param_named(low_threshold, sg_ctx.low_threshold, uint, 0444);
1164b9302d4SShrikanth Hegde MODULE_PARM_DESC(low_threshold,
1174b9302d4SShrikanth Hegde "Low steal threshold. default: 200 i.e 2%. Must be < high_threshold");
1184b9302d4SShrikanth Hegde
119*27d47ebcSShrikanth Hegde /* Return collective steal time across system. */
get_system_steal_time(void)120*27d47ebcSShrikanth Hegde static u64 get_system_steal_time(void)
121*27d47ebcSShrikanth Hegde {
122*27d47ebcSShrikanth Hegde return kcpustat_field_total(CPUTIME_STEAL, cpu_possible_mask);
123*27d47ebcSShrikanth Hegde }
124*27d47ebcSShrikanth Hegde
125*27d47ebcSShrikanth Hegde /* Return number of CPUs to consider for steal ratio. */
get_system_cpus(void)126*27d47ebcSShrikanth Hegde static unsigned int get_system_cpus(void)
127*27d47ebcSShrikanth Hegde {
128*27d47ebcSShrikanth Hegde return num_active_cpus();
129*27d47ebcSShrikanth Hegde }
130*27d47ebcSShrikanth Hegde
131*27d47ebcSShrikanth Hegde /*
132*27d47ebcSShrikanth Hegde * Called when the steal governor detects high physical CPU contention.
133*27d47ebcSShrikanth Hegde * It finds the last active core in the preferred mask and mark those
134*27d47ebcSShrikanth Hegde * CPUs as non-preferred.
135*27d47ebcSShrikanth Hegde *
136*27d47ebcSShrikanth Hegde * Must ensure:
137*27d47ebcSShrikanth Hegde * - at least one core is always kept as preferred
138*27d47ebcSShrikanth Hegde * - preferred is always subset of active.
139*27d47ebcSShrikanth Hegde */
decrease_preferred_cpus(void)140*27d47ebcSShrikanth Hegde static void decrease_preferred_cpus(void)
141*27d47ebcSShrikanth Hegde {
142*27d47ebcSShrikanth Hegde const struct cpumask *first_hk_core;
143*27d47ebcSShrikanth Hegde int target_cpu = nr_cpu_ids;
144*27d47ebcSShrikanth Hegde int cpu;
145*27d47ebcSShrikanth Hegde
146*27d47ebcSShrikanth Hegde guard(cpus_read_lock)();
147*27d47ebcSShrikanth Hegde cpu = cpumask_first_and(housekeeping_cpumask(HK_TYPE_KERNEL_NOISE),
148*27d47ebcSShrikanth Hegde cpu_preferred_mask);
149*27d47ebcSShrikanth Hegde if (cpu >= nr_cpu_ids)
150*27d47ebcSShrikanth Hegde return;
151*27d47ebcSShrikanth Hegde
152*27d47ebcSShrikanth Hegde /* Always leave first housekeeping core as preferred. */
153*27d47ebcSShrikanth Hegde first_hk_core = topology_sibling_cpumask(cpu);
154*27d47ebcSShrikanth Hegde cpu = cpumask_last(cpu_preferred_mask);
155*27d47ebcSShrikanth Hegde if (cpu >= nr_cpu_ids)
156*27d47ebcSShrikanth Hegde return;
157*27d47ebcSShrikanth Hegde
158*27d47ebcSShrikanth Hegde /* Find the last CPU which doesn't belong to that first hk_core. */
159*27d47ebcSShrikanth Hegde if (!cpumask_test_cpu(cpu, first_hk_core)) {
160*27d47ebcSShrikanth Hegde target_cpu = cpu;
161*27d47ebcSShrikanth Hegde } else {
162*27d47ebcSShrikanth Hegde for_each_cpu_andnot(cpu, cpu_preferred_mask, first_hk_core)
163*27d47ebcSShrikanth Hegde target_cpu = cpu;
164*27d47ebcSShrikanth Hegde }
165*27d47ebcSShrikanth Hegde
166*27d47ebcSShrikanth Hegde /* Only the first housekeeping core remains */
167*27d47ebcSShrikanth Hegde if (target_cpu >= nr_cpu_ids)
168*27d47ebcSShrikanth Hegde return;
169*27d47ebcSShrikanth Hegde
170*27d47ebcSShrikanth Hegde for_each_cpu_and(cpu, topology_sibling_cpumask(target_cpu),
171*27d47ebcSShrikanth Hegde cpu_preferred_mask)
172*27d47ebcSShrikanth Hegde set_cpu_preferred(cpu, false);
173*27d47ebcSShrikanth Hegde }
174*27d47ebcSShrikanth Hegde
175*27d47ebcSShrikanth Hegde /*
176*27d47ebcSShrikanth Hegde * Called when the steal governor detects no/low physical CPU contention.
177*27d47ebcSShrikanth Hegde * It finds the first active core outside of preferred mask and mark
178*27d47ebcSShrikanth Hegde * those CPUs as preferred.
179*27d47ebcSShrikanth Hegde *
180*27d47ebcSShrikanth Hegde * Must ensure preferred is subset of active.
181*27d47ebcSShrikanth Hegde */
increase_preferred_cpus(void)182*27d47ebcSShrikanth Hegde static void increase_preferred_cpus(void)
183*27d47ebcSShrikanth Hegde {
184*27d47ebcSShrikanth Hegde int first_cpu, cpu;
185*27d47ebcSShrikanth Hegde
186*27d47ebcSShrikanth Hegde guard(cpus_read_lock)();
187*27d47ebcSShrikanth Hegde first_cpu = cpumask_first_andnot(cpu_active_mask, cpu_preferred_mask);
188*27d47ebcSShrikanth Hegde
189*27d47ebcSShrikanth Hegde /* All CPUs are preferred. Nothing to increase further */
190*27d47ebcSShrikanth Hegde if (first_cpu >= nr_cpu_ids)
191*27d47ebcSShrikanth Hegde return;
192*27d47ebcSShrikanth Hegde
193*27d47ebcSShrikanth Hegde for_each_cpu_and(cpu, topology_sibling_cpumask(first_cpu),
194*27d47ebcSShrikanth Hegde cpu_active_mask)
195*27d47ebcSShrikanth Hegde set_cpu_preferred(cpu, true);
196*27d47ebcSShrikanth Hegde }
197*27d47ebcSShrikanth Hegde
preferred_cpus_valid(void)198*27d47ebcSShrikanth Hegde static bool preferred_cpus_valid(void)
199*27d47ebcSShrikanth Hegde {
200*27d47ebcSShrikanth Hegde if (cpumask_empty(cpu_preferred_mask)) {
201*27d47ebcSShrikanth Hegde pr_err("empty preferred mask. stopping\n");
202*27d47ebcSShrikanth Hegde return false;
203*27d47ebcSShrikanth Hegde }
204*27d47ebcSShrikanth Hegde
205*27d47ebcSShrikanth Hegde if (!cpumask_subset(cpu_preferred_mask, cpu_active_mask)) {
206*27d47ebcSShrikanth Hegde pr_err("preferred: %*pbl is not subset of active: %*pbl, stopping\n",
207*27d47ebcSShrikanth Hegde cpumask_pr_args(cpu_preferred_mask),
208*27d47ebcSShrikanth Hegde cpumask_pr_args(cpu_active_mask));
209*27d47ebcSShrikanth Hegde return false;
210*27d47ebcSShrikanth Hegde }
211*27d47ebcSShrikanth Hegde
212*27d47ebcSShrikanth Hegde return true;
213*27d47ebcSShrikanth Hegde }
214*27d47ebcSShrikanth Hegde
steal_governor_loop(struct work_struct * work)215*27d47ebcSShrikanth Hegde static void steal_governor_loop(struct work_struct *work)
216*27d47ebcSShrikanth Hegde {
217*27d47ebcSShrikanth Hegde u64 curr_steal, delta_steal, delta_ns, steal_ratio;
218*27d47ebcSShrikanth Hegde ktime_t now;
219*27d47ebcSShrikanth Hegde
220*27d47ebcSShrikanth Hegde now = ktime_get();
221*27d47ebcSShrikanth Hegde delta_ns = ktime_to_ns(ktime_sub(now, sg_ctx.time));
222*27d47ebcSShrikanth Hegde
223*27d47ebcSShrikanth Hegde if (unlikely(delta_ns < NSEC_PER_MSEC)) {
224*27d47ebcSShrikanth Hegde pr_err_ratelimited("work scheduled too soon delta_ns: %llu\n", delta_ns);
225*27d47ebcSShrikanth Hegde goto requeue_work;
226*27d47ebcSShrikanth Hegde }
227*27d47ebcSShrikanth Hegde
228*27d47ebcSShrikanth Hegde curr_steal = get_system_steal_time();
229*27d47ebcSShrikanth Hegde delta_steal = curr_steal > sg_ctx.steal ? curr_steal - sg_ctx.steal : 0;
230*27d47ebcSShrikanth Hegde sg_ctx.steal = curr_steal;
231*27d47ebcSShrikanth Hegde sg_ctx.time = now;
232*27d47ebcSShrikanth Hegde
233*27d47ebcSShrikanth Hegde /*
234*27d47ebcSShrikanth Hegde * steal_ratio = (delta_steal * 100*100)/(delta_ns * num_cpus())
235*27d47ebcSShrikanth Hegde * To avoid possible overflow, divide the denominator early.
236*27d47ebcSShrikanth Hegde * Note minimum interval is 100ms.
237*27d47ebcSShrikanth Hegde */
238*27d47ebcSShrikanth Hegde delta_ns = max_t(u64, div_u64(delta_ns * get_system_cpus(), 10000), 1);
239*27d47ebcSShrikanth Hegde steal_ratio = div64_u64(delta_steal, delta_ns);
240*27d47ebcSShrikanth Hegde
241*27d47ebcSShrikanth Hegde if (steal_ratio > sg_ctx.high_threshold)
242*27d47ebcSShrikanth Hegde decrease_preferred_cpus();
243*27d47ebcSShrikanth Hegde else if (steal_ratio <= sg_ctx.low_threshold)
244*27d47ebcSShrikanth Hegde increase_preferred_cpus();
245*27d47ebcSShrikanth Hegde /*
246*27d47ebcSShrikanth Hegde * else: steal ratio is within bounds. Still do design checks so that
247*27d47ebcSShrikanth Hegde * module restores to active if CPU hotplug breaks those assumptions.
248*27d47ebcSShrikanth Hegde */
249*27d47ebcSShrikanth Hegde if (!preferred_cpus_valid()) {
250*27d47ebcSShrikanth Hegde restore_preferred_to_active();
251*27d47ebcSShrikanth Hegde return;
252*27d47ebcSShrikanth Hegde }
253*27d47ebcSShrikanth Hegde
254*27d47ebcSShrikanth Hegde requeue_work:
255*27d47ebcSShrikanth Hegde schedule_delayed_work(&sg_ctx.work, sg_ctx.delay);
256*27d47ebcSShrikanth Hegde }
257*27d47ebcSShrikanth Hegde
steal_governor_init(void)2589a8e740eSShrikanth Hegde static int __init steal_governor_init(void)
2599a8e740eSShrikanth Hegde {
2609a8e740eSShrikanth Hegde #ifdef CONFIG_XEN
2619a8e740eSShrikanth Hegde if (xen_initial_domain()) {
2629a8e740eSShrikanth Hegde pr_err("Cannot load in Xen Dom0 (Host OS). Driver is for guests only.\n");
2639a8e740eSShrikanth Hegde return -ENODEV;
2649a8e740eSShrikanth Hegde }
2659a8e740eSShrikanth Hegde #endif
2669a8e740eSShrikanth Hegde
2674b9302d4SShrikanth Hegde if (sg_ctx.low_threshold >= sg_ctx.high_threshold) {
2684b9302d4SShrikanth Hegde pr_err("low_threshold (%u) must be less than high_threshold (%u)\n",
2694b9302d4SShrikanth Hegde sg_ctx.low_threshold, sg_ctx.high_threshold);
2704b9302d4SShrikanth Hegde return -EINVAL;
2714b9302d4SShrikanth Hegde }
2724b9302d4SShrikanth Hegde
2734b9302d4SShrikanth Hegde sg_ctx.delay = msecs_to_jiffies(sg_ctx.interval_ms);
274*27d47ebcSShrikanth Hegde INIT_DELAYED_WORK(&sg_ctx.work, steal_governor_loop);
275*27d47ebcSShrikanth Hegde sg_ctx.steal = get_system_steal_time();
276*27d47ebcSShrikanth Hegde sg_ctx.time = ktime_get();
277*27d47ebcSShrikanth Hegde schedule_delayed_work(&sg_ctx.work, sg_ctx.delay);
2784b9302d4SShrikanth Hegde pr_info("enabled. interval: %ums, high_threshold: %u, low_threshold: %u\n",
2794b9302d4SShrikanth Hegde sg_ctx.interval_ms, sg_ctx.high_threshold, sg_ctx.low_threshold);
2804b9302d4SShrikanth Hegde
2819a8e740eSShrikanth Hegde return 0;
2829a8e740eSShrikanth Hegde }
2839a8e740eSShrikanth Hegde
steal_governor_exit(void)2849a8e740eSShrikanth Hegde static void __exit steal_governor_exit(void)
2859a8e740eSShrikanth Hegde {
286*27d47ebcSShrikanth Hegde disable_delayed_work_sync(&sg_ctx.work);
2879a8e740eSShrikanth Hegde restore_preferred_to_active();
2889a8e740eSShrikanth Hegde pr_info("disabled\n");
2899a8e740eSShrikanth Hegde }
2909a8e740eSShrikanth Hegde
2919a8e740eSShrikanth Hegde module_init(steal_governor_init);
2929a8e740eSShrikanth Hegde module_exit(steal_governor_exit);
2939a8e740eSShrikanth Hegde
2949a8e740eSShrikanth Hegde MODULE_LICENSE("GPL");
2959a8e740eSShrikanth Hegde MODULE_AUTHOR("IBM Corporation");
2969a8e740eSShrikanth Hegde MODULE_DESCRIPTION("Virtualization Steal Time Governor");
297