sched discussion
[PATCH 4/5] cpufreq: schedutil: convert to kthread_create_worker
LLM 分析
cpufreq schedutil:迁移到 kthread_create_worker 统一 API
系列概况
- 标题: [PATCH 4/5] cpufreq: schedutil: convert to kthread_create_worker
- 作者: Bradley Morgan brads@mainlining.org
- 版本: v1(系列第 4/5 帖,整系列 5 patch)
- 规模: 1 文件,14 行新增 / 19 行删除
- 修改文件:
kernel/sched/cpufreq_schedutil.c - 代码统计: +14 / -19
- Message-ID: 20260829160001.6245-1-include@grrlz.net
- 完整性: 仅含本 patch + diffstat,diff 完整可读;系列其他 4 个 patch 未在本帖给出
补丁目的
sugov(schedutil governor)原本在 struct sugov_policy 里就地嵌入一个 kthread_worker,再单独 kthread_create() 一个内核线程来跑 kthread_worker_fn。本 patch 把这两步合并成新的 kthread_create_worker(),让 worker 自己管理 task 指针;同时删除多余的 task_struct *thread 字段。
这不是 RFC,也不是修复具体 bug,而是清理型重构 —— 属于上游"移除 kthread_worker_fn() 中 worker->task 自赋值"系列工作的一环,schedutil 作为 kernel/sched/ 下受影响最大的使用者必须跟进重写。
旧流程的问题
旧 API 等于把"工作线程"拆成三个手动步骤:
kthread_init_worker(&sg_policy->worker)在结构体里原地初始化 worker;kthread_create(kthread_worker_fn, &sg_policy->worker, ...)单独起一个task_struct;- 在
kthread_worker_fn内部,把当前 task 写回worker->task(自赋值副作用)。
这套用法既冗余(多管一个 thread 指针)、又依赖 kthread_worker_fn 内部那行隐藏的 worker->task = current;。一旦上游把这行自赋值拿掉,所有手动嵌入 worker 的驱动都要重写,schedutil 首当其冲。
新流程
kthread_create_worker() 一次性创建 worker + 配套 task,并把 task 直接挂在 worker->task 上;调用者只需要:
sg_policy->worker = kthread_create_worker(0, "sugov:%d",
cpumask_first(policy->related_cpus));
清理路径配套换成 kthread_destroy_worker(),替代原先 kthread_flush_worker() + kthread_stop() 两步走。
Patch 概览
整系列 5 patch,本帖是第 4 个,专门负责 schedutil 这一个文件。
关键实现
struct sugov_policy 结构调整:
struct sugov_policy {
/* 旧:值类型 worker + 单独 thread 指针 */
/* struct kthread_worker worker; */
/* struct task_struct *thread; */
/* 新:指针类型 worker,task 嵌在 worker 里 */
struct kthread_worker *worker;
/* ... 其它字段保持不变 ... */
};
sugov_kthread_create() 关键替换:
/* 旧
* kthread_init_worker(&sg_policy->worker);
* thread = kthread_create(kthread_worker_fn,
* &sg_policy->worker,
* "sugov:%d", cpumask_first(...));
* sched_setattr_nocheck(thread, &attr);
*/
sg_policy->worker = kthread_create_worker(0, "sugov:%d",
cpumask_first(policy->related_cpus));
ret = sched_setattr_nocheck(sg_policy->worker->task, &attr);
sugov_kthread_stop() 关键替换:
/* 旧
* kthread_flush_worker(&sg_policy->worker);
* kthread_stop(sg_policy->thread);
*/
kthread_flush_worker(sg_policy->worker);
kthread_destroy_worker(sg_policy->worker);
散落在文件里的 sg_policy->thread 引用(set_cpus_allowed_ptr、kthread_bind_mask、wake_up_process、sugov_irq_work 排队等位置)全部改写成 sg_policy->worker->task 或 sg_policy->worker。
类比
旧 API 像自己开夫妻店:要自己租铺面(嵌入的 worker 字段)、雇一个售货员(kthread_create)、再让售货员自己登记上岗(worker->task = current 的隐式自赋值)。新 API 像加盟连锁:挂个招牌,kthread_create_worker() 一次把铺面 + 售货员都配齐,售货员信息直接写在 worker 这张工牌上(worker->task)。离职时也只需要给连锁总部打一个电话(kthread_destroy_worker)。少管一个 thread 指针,就少一处"售货员走了但铺面没退"的风险。
Highlight:风险与注意点
- 过渡期一致性:整系列是上游"移除
kthread_worker_fn()自赋值"清理的一部分,需要确认上游补丁先合入或同步落地,否则本 patch 等于在 worker API 还不稳定时抢跑,cherry-pick 时容易断链。 - 错误路径收尾:旧代码在
sched_setattr_nocheck失败时调用kthread_stop(thread);新代码必须改用kthread_destroy_worker(),并且要注意 flush 在 destroy 之前是否仍必要。 - 亲和性时机:
set_cpus_allowed_ptr、kthread_bind_mask改到worker->task上,需要确认kthread_create_worker()返回的 worker 已经持有可用的 task;否则亲和性调用可能命中尚未初始化的指针。 - SCHED_DEADLINE 路径:
sched_setattr_nocheck(sg_policy->worker->task, &attr)把 schedutil 工作线程绑定到 SCHED_DEADLINE,是 sugov 实时性保证的关键,必须在 cpufreq governor 切换、S3 唤醒、CPU 热插拔等场景下重新覆盖测试。 - 用户可见 API 未变:governor 对外交付(policy 属性、
fast_switch_enabled等)保持不变,影响面理论上局限在 cpufreq 内部,仍建议 cpufreq + thermal + rt-tests 全套跑一遍。
版本变化
只有 v1,本帖就是系列第 4/5 patch。系列其余 patch 标题信息未在本帖给出,只能从 cover letter 描述推断是同一清理工作的多个驱动使用者迁移。
一句话总结
schedutil 把嵌入式的 kthread_worker 改成指针式 kthread_create_worker(),省掉独立 thread 字段,让 worker 生命周期统一由 worker API 管理。
+----------------------------+ +----------------------------+
| Old pattern (two-step) | | New pattern (one-shot) |
+----------------------------+ +----------------------------+
| sugov_policy { | | sugov_policy { |
| kthread_worker worker; | | kthread_worker *worker; -|--+
| task_struct *thread; | | } | |
| } | +----------------------------+ |
| | | | kthread_create_worker(0,..)|<-+
| v | | -> worker + task in one |
| kthread_init_worker(&w) | | | |
| kthread_create(worker_fn,..)| | v |
| | | | worker->task is set inside |
| v | | the helper (no self-assign)|
| worker->task = current | | | |
| (implicit self-assignment) | | v |
| | | | set_cpus_allowed_ptr( |
| v | | worker->task, ...) |
| set_cpus_allowed_ptr(thread)| | kthread_bind_mask( |
| kthread_bind_mask(thread) | | worker->task, ...) |
| wake_up_process(thread) | | wake_up_process(worker->t.)|
+----------------------------+ +----------------------------+
| Cleanup: | | Cleanup: |
| kthread_flush_worker(&w) | | kthread_flush_worker(w) |
| kthread_stop(thread) | | kthread_destroy_worker(w) |
+----------------------------+ +----------------------------+