sched discussion
[PATCH v2 0/5] kthread: convert remaining users to kthread_create_worker
LLM 分析
kthread:将剩余用户迁移到 kthread_create_worker*
系列概况
- 标题: [PATCH v2 0/5] kthread: convert remaining users to kthread_create_worker
- 作者: Bradley Morgan brads@mainlining.org
- 版本: v2(5 patches)
- 规模: 6 文件改动,+50 / -67
- 修改文件:
- drivers/media/pci/ivtv/ivtv-driver.c / .h
- drivers/net/ethernet/microchip/encx24j600.c
- drivers/tty/serial/sc16is7xx.c
- kernel/sched/cpufreq_schedutil.c
- kernel/kthread.c
- Message-ID: cover.1788513591.git.brads@mainlining.org
- 完整性: 系列本体 + maintainer 反馈(Kicinski、Greg KH)+ Sashiko AI bot + kernel test robot 真实编译错误,结构完整。
补丁目的
kthread_worker_fn() 里有一段 FIXME 挂了将近十年:
worker->task = current;
历史上必要的原因是:旧 API kthread_run(kthread_worker_fn, worker, ...) 在 worker 启动前并不会告诉 worker 它的 task_struct 是谁,所以 worker 必须自赋值。新 API kthread_create_worker*() 在 worker 启动之前就把 worker->task 填好,这一行就成了死代码。
本系列把最后 4 个还用旧模式的驱动(ivtv / encx24j600 / sc16is7xx / cpufreq_schedutil)迁过去,然后删除自赋值,保留一个 WARN_ON() 当 OOT 用户的兜底 sanity check。
旧流程的问题
- 老 API 把
kthread_worker和task_struct拆成两个字段,调用方要自己维护; worker->task = current自我赋值长期存在,掩盖了 API 的真正责任划分;- 关闭路径
kthread_flush_worker(&x)+kthread_stop(task)容易被忘记一半; - 散落的
&priv->kworker取地址调用遍布多个驱动。
新流程
- 4 个驱动统一换成
kthread_run_worker()/kthread_create_worker(); - worker 改成指针
struct kthread_worker *kworker,不再保留*_task副本; - 关闭路径统一
kthread_flush_worker(p)+kthread_destroy_worker(p); kernel/kthread.c删除自赋值,WARN_ON(worker->task != current)作为 OOT 兜底。
Patch 概览
| # | 子系统 | 关键改动 |
|---|---|---|
| 1/5 | media: ivtv | 删除 irq_worker_task,kthread_run_worker() 替换 |
| 2/5 | net: encx24j600 | 同上模式 |
| 3/5 | tty: sc16is7xx | 9 处 &s->kworker 改成 s->kworker |
| 4/5 | cpufreq: schedutil | kthread_create_worker(),调度属性改走 worker->task |
| 5/5 | kernel/kthread | 删除自赋值,保留 WARN_ON |
关键实现
以 patch 1 为代表的核心 hunk:
/* before */
kthread_init_worker(&itv->irq_worker);
itv->irq_worker_task = kthread_run(kthread_worker_fn,
&itv->irq_worker,
"%s", itv->v4l2_dev.name);
sched_set_fifo(itv->irq_worker_task);
/* after */
itv->irq_worker = kthread_run_worker(0, "%s", itv->v4l2_dev.name);
sched_set_fifo(itv->irq_worker->task);
kthread_create_worker*() 内部就把 worker->task 装好,再把 worker 线程跑进 kthread_worker_fn,调用方不再需要单独保留 task 指针。
patch 5:
/* before */
WARN_ON(worker->task && worker->task != current);
worker->task = current;
/* after */
WARN_ON(worker->task != current);
注释同步改成「所有 worker 都用新 API 创建了,这里 WARN 说明调用方坏了」。
类比
把 worker 想象成公司前台:
- 旧流程:你(驱动)雇了一个前台(kthread),但没告诉前台他的工号。前台上岗第一件事是把「工号」贴到自己工牌上(
worker->task = current)。如果忘了这一步,分派任务的人就找不到他。 - 新流程:人事部门(
kthread_create_worker())在雇前台之前就把工号印到工牌上。前台上岗时只用核对「这工号是我的」(WARN_ON),不需要再贴一次。
流程图
旧 API 与新 API 的差异:
Old API New API
(kthread_run) (kthread_create_worker*)
| |
driver -> kthread_run(fn, &worker) driver -> kthread_create_worker()
| |
v v
kthread_worker_fn() kthread_worker_fn()
| |
worker->task = current <-- DEAD CODE worker->task preset by API
| |
v v
scheduler sees worker->task scheduler sees worker->task
driver must keep task_struct* single pointer is enough
生命周期配对:
init : kthread_init_worker + kthread_run(fn, &worker)
cleanup: kthread_flush_worker(&worker) + kthread_stop(task)
||
\/
init : kthread_run_worker() / kthread_create_worker()
cleanup: kthread_flush_worker(p) + kthread_destroy_worker(p)
整体 5 patch 作用关系:
patch 1/5 ivtv ----+
patch 2/5 encx24j600 ----+--> migrate 4 drivers off kthread_run(kthread_worker_fn)
patch 3/5 sc16is7xx ----+
patch 4/5 schedutil --+
|
v
patch 5/5 kthread.c: drop worker->task self-assign
(only safe AFTER all tree users migrated)
Highlight:风险与注意点
- 真实编译失败:kernel test robot 在 riscv-allmodconfig + clang 下抓到
drivers/media/pci/ivtv/ivtv-irq.c:1053:22: incompatible pointer types passing 'struct kthread_worker **' to parameter of type 'struct kthread_worker *'; remove &。patch 1 没把ivtv-irq.c里残留的&itv->irq_worker全部改干净,v3 必须补;loongarch-allmodconfig 也复现同一错误。 - UAF 隐患:Sashiko 提示 patch 1 中
kthread_destroy_worker()在free_irq()之前调用,可能与中断处理路径形成 use-after-free;patch 3 的 worker 也早于 devm-managed threaded IRQ 释放。需要确认 IRQ handler 是否还会kthread_queue_work()到已释放的 worker。 - 维护流程摩擦:Jakub Kicinski / Greg KH 都明确指出系列跨多个子系统,patch 5/6 不该打包让 kthread 维护者直接收。Bradley 计划 v3 把 4 个驱动 patch 拆成独立 series 分别发给 media / net / tty / cpufreq 维护者,等合入后再单独发 patch 5。
- OOT deadlock 风险:删掉
worker->task = current之后,tree 外的旧式调用方会立刻因缺wake_up路径而永久死锁——WARN_ON()只能提示,不能阻止。 - pre-existing 缺陷:sc16is7xx 多 port 共用
poll_work的 cancel 行为、encx24j600priv->tx_skb未在 TX timeout / remove 路径清理、SPI 字节序未转换——这些都跟本系列无关,但 review 过程中被一起暴露出来。
版本变化
- v1 → v2(来自 cover letter):
- 保留
WARN_ON()作为 sanity check(呼应 Frederic / Peter 的反馈); - 给
WARN_ON加注释解释为什么留它; - 修正 commit subject 与 changelog 措辞。
- 保留
一句话总结
5 patch 系列把最后 4 个 kthread_run(kthread_worker_fn) 用户迁到 kthread_create_worker*(),顺势删掉 kthread_worker_fn() 里挂了 10 年的 worker->task = current 自赋值并保留 WARN_ON 兜底,但 v2 已被 maintainer 要求拆 series 分头发,并被 test robot 抓到 patch 1 在 ivtv-irq.c 遗漏一处 &itv->irq_worker 的编译错误,v3 必须修。