sched discussion
[PATCH v9 00/11] sched, steal_governor: Introduce preferred CPUs and steal-driven vCPU backoff
LLM 分析
sched/steal_governor 系列:Preferred CPU 与基于 steal time 的 vCPU 退避
系列概况
- 标题: [PATCH v9 00/11] sched, steal_governor: Introduce preferred CPUs and steal-driven vCPU backoff
- 作者: Shrikanth Hegde (sshegde@linux.ibm.com, IBM)
- 版本: v9, 共 11 个 patch
- 规模: 跨 scheduler core、cpumask、sysfs、virt 驱动四大子系统
- 修改文件: Documentation/scheduler/sched-arch.rst、include/linux/cpumask.h、kernel/cpu.c、kernel/sched/core.c、kernel/sched/fair.c、kernel/sched/sched.h、kernel/sched/debug.c、include/linux/sched.h、drivers/base/cpu.c、drivers/virt/Kconfig、drivers/virt/Makefile、drivers/virt/steal_governor.c(新)、MAINTAINERS、Documentation/driver-api/steal-governor.rst(新)
- 代码统计: 约 350+ 行新增(不含 docs);核心驱动 steal_governor.c 约 230 行
- Message-ID: 20260724140732.2683314-1-sshegde@linux.ibm.com
- 完整性: 完整 11 patch 系列;reviewer 主要为 Yury Norov,另有 Mete Durlu 反馈;已收 v10 改动清单
补丁目的
在虚拟化(paravirt)环境下,提供一个内核级、协作式、可动态伸缩的 vCPU 压缩机制。
- 当 VM 部署在 pCPU 过载的 hypervisor 上时,steal time 上升意味着频繁 vCPU 抢占。
- 现有方案(CPU hotplug、isolated cpusets、显式 affinity)要么破坏用户亲和性、要么管理成本高。
- 本系列引入 scheduler 一等公民 "preferred CPU" 状态,并配套 steal_governor 策略驱动,让 guest 在检测到高 steal time 时主动 fold 到一个较小的 preferred 子集上,从而降低 vCPU 抢占。
旧流程的问题
- CPU hotplug / isolated cpusets:重型管理操作,需要拓扑重建,破坏 userspace CPU 亲和性。
- 显式 task affinity:用户难以管理大量 vCPU 的 affinity。
- 缺乏快速、协作式的内核级反应机制,无法在不影响亲和性的前提下动态伸缩。
新流程
- Layer A(调度器机制):cpu_preferred_mask 严格 ⊆ cpu_active_mask。wakeup 路径 is_cpu_allowed 检查并通过 select_fallback_rq 选 preferred;tick 路径在当前 CPU 非 preferred 时用 stopper 推任务;load balance 仅在 cpu_preferred_mask 上进行。
- Layer B(steal_governor 驱动):周期性采样系统 steal time;> high_threshold(默认 5%)preferred 减 1 核;< low_threshold(默认 2%)preferred 加 1 核;至少保留 1 个 preferred;preferred ⊆ active 必须成立。
- 严格遵守用户 affinity:若用户把任务 exclusive 绑定到 non-preferred CPU,调度器不会破坏。
Patch 概览
- 01/11 文档:把概念写进 sched-arch.rst(后续挪到新建 sched-paravirt.rst)。
- 02/11 基础设施:cpu_preferred_mask + set_cpu_preferred() + cpu_preferred() + CONFIG_PREFERRED_CPU。
- 03/11 sysfs:/sys/devices/system/cpu/preferred 导出 cpumask。
- 04/11 wakeup 改造:is_cpu_allowed 调用 task_can_sched_on_preferred()。
- 05/11 load balance 改造:sched_balance_rq 仅扫描 preferred;_nohz_idle_balance 和 sched_balance_newidle 早退。
- 06/11 push 机制:sched_tick 检测非 preferred 时调度 stopper 迁出当前任务。
- 07/11 统计:nr_migrations_cpu_non_preferred 暴露到 proc_sched_show_task。
- 08/11 驱动骨架:drivers/virt/steal_governor.c + steal-governor.rst + MAINTAINERS。
- 09/11 模块参数:interval_ms、high_threshold、low_threshold 校验。
- 10/11 策略循环:compute_preferred_cpus_work() + decrease/increase_preferred_cpus()。
- 11/11 Kconfig 启用:CONFIG_STEAL_GOVERNOR 选 CONFIG_PREFERRED_CPU,默认 m。
关键实现
/* is_cpu_allowed: 优先选 preferred CPU */
static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
{
if (!(p->flags & PF_KTHREAD)) {
if (task_can_sched_on_preferred(cpu, p))
return false;
return cpu_active(cpu);
}
if (task_can_sched_on_preferred(cpu, p))
return false;
return cpu_online(cpu);
}
static inline bool task_can_sched_on_preferred(int cpu, struct task_struct *p)
{
if (cpu_preferred(cpu))
return false;
if (unlikely(p->sched_class != &fair_sched_class))
return false;
return cpumask_intersects(p->cpus_ptr, cpu_preferred_mask);
}
/* stopper: 基于 select_fallback_rq 选 preferred CPU */
static int sched_non_preferred_cpu_push_stop(void *arg)
{
...
if (cpu_preferred(rq->cpu)) {
rq->push_task_work_done = false;
put_task_struct(p);
return 0;
}
cpu = select_fallback_rq(rq->cpu, p);
...
if (task_rq(p) == rq && task_on_rq_queued(p) && !is_migration_disabled(p)) {
struct 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;
}
}
/* steal_governor 策略循环 */
static void compute_preferred_cpus_work(struct work_struct *work)
{
curr_steal = get_system_steal_time();
delta_steal = curr_steal > sg_ctx.steal ? curr_steal - sg_ctx.steal : 0;
sg_ctx.steal = curr_steal;
delta_ns = max_t(u64, div_u64(delta_ns * get_system_cpus(), 10000), 1);
steal_ratio = div64_u64(delta_steal, delta_ns);
if (steal_ratio > sg_ctx.high_threshold)
decrease_preferred_cpus();
else if (steal_ratio <= sg_ctx.low_threshold)
increase_preferred_cpus();
if (!preferred_cpus_valid())
restore_preferred_to_active();
schedule_delayed_work(&sg_ctx.work, sg_ctx.delay);
}
wakeup tick load balance
| | |
is_cpu_allowed sched_tick() sched_balance_rq()
| | |
task_can_sched_on_ if !cpu_preferred cpumask_and(sd span,
preferred() sched_push_curr_ cpu_preferred_mask)
| non_preferred_cpu
v |
select_fallback_rq v
-> preferred CPU stopper thread
-> __migrate_task
steal_governor (delayed_work, interval_ms)
|
v
get_system_steal_time() ---> delta_steal
| |
+--> steal_ratio ------------+
|
+----------+-----------+----------+
| | |
steal > high_th steal <= low_th other
| | |
decrease_preferred increase_preferred no-op
CPUs CPUs
| |
v v
set_cpu_preferred set_cpu_preferred
(cpus, false) (cpus, true)
类比
自助餐厅:80 张桌子(pCPU),4 个旅行团各想占 60 张(vCPU 总 240 > 80)。走廊太挤(steal 飙升)时,协调员让每团自愿让出几张,于是通行变顺。steal_governor 就是吧台后定期巡视、协调桌位的服务员;push 机制则是把坐错桌的客人请到正确桌,避免已经迁出桌位的人又走回头路。
Highlight:风险与注意点
- 命名误导:Yury 指出 series 已从 arch-specific RFC 演变为通用机制,sched-arch.rst 与 arch 无关;建议改名或新建 sched-paravirt.rst。
- 代码去重遗留:v8 时 Yury 已指出 get_system_steal_time() 与 s390 hiperdispatch 中的 hd_calculate_steal_percentage() 重复,v9 仍未合并;Shrikanth 将通过 kcpustat_field_total() 统一抽象,并同步修改 fs/proc/uptime.c。
- 复杂度退化:单任务与全集都仅落在 non-preferred 集合时,wakeup 路径会变成 O(N^2),目前仅靠"罕见"为论据,缺乏上界估计。
- 协作成败:方案前提是所有 VM 都启用;若一台 VM 不启用,可能"吃掉"更多 CPU,需运维侧保证。
- NUMA 简化:策略未做 NUMA 切片,依赖用户侧假设 CPU 均匀分布在大节点间。
- 版本节奏:v9 在 v8 之后几天内发出,Yury 提醒应给 reviewer 更多时间;Shrikanth 解释是因为即将失去测试机。
- 只读模块参数 + 模块化:参数加载后不可改,作者强烈建议 =m;一旦所有 vCPU 都 pinned 到 non-preferred,load balance 完全消失,需要运维侧知晓。
版本变化(v8 -> v9)
v9 本身无大功能新增,明确为 v8 修订重发。后续 v10 计划项已公开:
- 文档挪到新建 sched-paravirt.rst。
- _nohz_idle_balance 移除 cpu_preferred 检查,让 nohz.next_balance 自然推进。
- find_new_ilb 增加 cpu_preferred 选择(基于 Andrea Righi 系列)。
- 新增 kcpustat_field_total() 公共函数,统一 s390 hiperdispatch 与本驱动的汇总。
- 增加阈值限制说明、文档 nit。
一句话总结
本系列把 "preferred CPU" 作为 scheduler 一等 cpumask 状态,让 steal_governor 驱动按 steal time 动态收缩/扩展 preferred 集合,并在 wakeup、tick push、load balance 三处主动 fold 工作负载,既不破坏 userspace affinity 又能缓解 paravirt 下的 vCPU 抢占噪声。