sched discussion
[PATCH v8 01/11] sched/docs: Document cpu_preferred_mask and Preferred CPU concept
LLM 分析
CPU Scheduler:cpu_preferred_mask 与 steal_governor 动态 vCPU backoff
系列概况
- 标题:[PATCH v8 00/11] sched, steal_governor: Introduce cpu_preferred_mask and steal-driven vCPU backoff
- 作者:Shrikanth Hegde sshegde@linux.ibm.com
- 版本:v8,11 个 patch(1 篇 cover letter + 11 篇正文)
- 规模:净增约 390 行(不含文档/sysfs/Kconfig);新增子目录 drivers/virt/steal_governor/
- 修改文件:Documentation/scheduler/sched-arch.rst、Documentation/ABI/testing/sysfs-devices-system-cpu、Documentation/driver-api/steal-governor.rst、include/linux/cpumask.h、kernel/Kconfig.preempt、kernel/cpu.c、kernel/sched/{core,fair,debug,sched.h}.c、include/linux/sched.h、drivers/base/cpu.c、drivers/virt/Kconfig、drivers/virt/Makefile、drivers/virt/steal_governor/{Kconfig,Makefile,core.c,core.h}、MAINTAINERS
- Message-ID:20260720172250.2257582-1-sshegde@linux.ibm.com
- 完整性:本帖仅含 v8 首发 + 6 篇 Yury/Shrikanth 互动的回复;v8 之后未见新版本。
补丁目的
在半虚拟化环境中,多个 VM 同时高负载会触发 hypervisor 抢占 vCPU;持有锁或中断关闭区间的 vCPU 一旦被抢占,前向进度会被破坏。本系列在 guest 内核里新增"Preferred CPU"概念:通过 cpu_preferred_mask 把可用 vCPU 收成一个动态子集,让 scheduler 在 wakeup/tick/load balance 三个时机主动折叠负载;策略则由独立的 steal_governor 驱动按 steal time 周期性地逐核收缩或扩张,实现不破坏用户 affinity 的协作式降载。
旧流程的问题
- CPU hotplug / isolated cpuset 改动大、需要重建 topology、破坏 affinity。
- 显式 task affinity 难维护,无法跟随负载自适应。
- 缺乏内核侧快速、可逆的 backoff,所有压力卸载由 hypervisor 抢占解决,代价高。
新流程
- 注册
cpu_preferred_mask(PREFERRED_CPU开启时存在,关闭时别名到cpu_active_mask),保证preferred ⊆ active ⊆ online ⊆ present ⊆ possible。 select_fallback_rq与is_cpu_allowed在非 preferred CPU 上优先把任务搬到 preferred 集合。- tick 路径上若当前 CPU 非 preferred,由 stopper 主动把运行任务迁走(仅 FAIR class)。
- load balance 把 sched_domain span 限制在 preferred mask 内,避免向 non-preferred 倒灌。
steal_governor周期计算steal_ratio,按高/低阈值加或减一颗 core 的 preferred 状态。
Patch 概览
- 01/11 docs:Documentation/scheduler/sched-arch.rst 增加 Preferred CPU 章节
- 02/11 cpumask:
__cpu_preferred_mask、set_cpu_preferred、cpu_preferred()、PREFERRED_CPUKconfig - 03/11 sysfs:
/sys/devices/system/cpu/preferred只读文件 - 04/11 core:
is_cpu_allowed()增加task_can_sched_on_preferred()短路 - 05/11 fair:
sched_balance_rq、_nohz_idle_balance、sched_balance_newidle限定 preferred 集 - 06/11 core:tick 路径
sched_push_current_non_preferred_cpu()+ stopper work - 07/11 debug:新增
nr_migrations_cpu_non_preferred计数与 proc 输出 - 08/11 virt:驱动骨架、模块出入口、restore_preferred_to_active()
- 09/11 virt:module_param_cb
interval_ms、high_threshold,low_threshold - 10/11 virt:delayed work 主循环、
decrease/increase_preferred_cpus()等辅助 - 11/11 Kconfig/Makefile:注册
STEAL_GOVERNOR,selectPREFERRED_CPU
关键实现
/* cpumask:preferred ⊆ active */
#ifdef CONFIG_PREFERRED_CPU
extern struct cpumask __cpu_preferred_mask;
#else
#define __cpu_preferred_mask __cpu_active_mask
#endif
#define cpu_preferred_mask ((const struct cpumask *)&__cpu_preferred_mask)
/* tick 路径推任务 */
void sched_tick(void)
{
...
if (!cpu_preferred(cpu))
sched_push_current_non_preferred_cpu(rq);
}
/* steal_governor 主循环 */
static void compute_preferred_cpus_work(struct work_struct *work)
{
...
delta_steal = curr_steal - sg_core_ctx.steal;
delta_ns = max_t(u64,
div_u64(delta_ns *
get_num_cpus_steal_ratio(), 100 * 100), 1);
steal_ratio = div64_u64(delta_steal, delta_ns);
if (steal_ratio > sg_core_ctx.high_threshold)
decrease_preferred_cpus();
if (steal_ratio <= sg_core_ctx.low_threshold)
increase_preferred_cpus();
...
schedule_delayed_work(&sg_core_ctx.work,
msecs_to_jiffies(sg_core_ctx.interval_ms));
}
+------------------------------+
| userspace affinity (cpuset) |
+--------------+---------------+
v
+---------------------------------------+
| cpu_preferred_mask |
| (subset of cpu_active_mask) |
+---+---------------+---------+---------+
v v v
wakeup path tick push load balance
(is_cpu_allowed)(stopper) (span limited)
|
v
+---------------------+
| steal_governor |
| periodic work |
| collect steal time |
| compute ratio |
| high -> -1 core |
| low -> +1 core |
+---------------------+
类比
把写字楼里的几间共享会议室当作 pCPU,每家公司(VM)领到的门卡(vCPU)就是它们能用的会议室。当走廊变得拥挤(steal time 上升),物业(steal_governor)通知大家"暂时把几张门卡交还前台",只留关键业务使用;走廊一空,再把门卡发回。整过程租户不可换公司(affinity 不动),但每间会议室利用率自动收敛。
Highlight:风险与注意点
- Yury 指出
get_system_steal_time()与hd_calculate_steal_percentage()重复,建议抽到kernel_stat.h提供kcpustat_steal_time()或更通用的kcpustat_field_total()。 - Yury 建议 init 时把毫秒换算成 jiffies 存入 struct,避免每次调度都做
msecs_to_jiffies;维护者已认可将在 v9 加上delay字段。 - Yury 还质疑
struct steal_governor提到独立 core.h 是否必要,提示可收敛。 restore_preferred_to_active()后驱动只是"不再 requeue work",进程不会被显著提示,存在"quiet failure"风险。- 协作前提是所有 VM 都加载模块;懒 VM 仍按 active mask 占用,会拉低整体收益,需 ops 通过 sysfs 核对。
- 仅 FAIR class 受影响;kthread 在 pin-only non-preferred 时会被保留,需关注边界用例。
- 下推/上推粒度按"core/siblings"为单位,与 powerVM 等 core-scheduling hypervisor 对齐,但与非 core 调度器混跑时要验证一跳性。
版本变化
本帖仅 v8。Cover letter 中提到系列自早期 arch-specific RFC 经多轮重设计形成;社区 review(尤其是 Yury Norov 的反复审查)显著推动了 driver 形态从 scheduler 内嵌策略改为可加载模块。
一句话总结
把 cpu_preferred_mask 作为 scheduler 与策略解耦的协作接口,把"按 steal time 收/扩 vCPU 子集"放进独立的 steal_governor 模块,让 guest 在不破坏 affinity 的前提下主动减少 vCPU preemption。