尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

第七章:GPU Scheduler 分析:7.6 调度循环与流控 — sched_main 核心流程

第七章:GPU Scheduler 分析:7.6 调度循环与流控 — sched_main 核心流程
📅 发布时间:2026/7/2 16:20:53

1. 引言:一切在此串联

前面五篇分别介绍了设计目标(7.1)、scheduler 实例(7.2)、entity(7.3)、job(7.4)和双 fence(7.5)。本篇将它们串联成一个运转中的系统——分析sched_main.c中的调度循环如何选取 entity、弹出 job、检查 credit、调用run_job()、以及完成后的free_job()流程。

核心问题:从用户态 push 一个 job 到它被硬件执行完毕,scheduler 内部经历了哪些步骤?

2. 总体架构:两个 Work Item

GPU scheduler不使用独立内核线程,而是基于 workqueue 的两个 work item 驱动:

submit_wq (ordered workqueue) ├── work_run_job → 选 entity → pop job → credit 检查 → run_job() └── work_free_job → 取 finished job → free_job() → 归还 credit

为什么用 ordered workqueue?

  • 保证同一 scheduler 的 run_job 和 free_job 不会并发执行
  • 省去大量锁竞争——选取 entity、pop job、run_job 调用天然串行
  • 但不同 scheduler(不同 ring)之间完全并行

触发时机:

事件触发的 work
drm_sched_entity_push_job()work_run_job(通过drm_sched_wakeup())
drm_sched_job_done()work_free_job(完成后需要释放)
work_free_job执行完毕work_run_job(credit 归还后可能可以跑新 job)

3. 调度循环主函数:drm_sched_run_job_work()

drm_sched_run_job_work()——整个调度器的心脏:

staticvoiddrm_sched_run_job_work(structwork_struct*w){structdrm_gpu_scheduler*sched=container_of(w,...);structdrm_sched_entity*entity;structdrm_sched_job*sched_job;structdma_fence*fence;// ① 选取 entityentity=drm_sched_select_entity(sched);if(!entity)return;// 无就绪 entity,退出// ② 弹出 job(含依赖检查)sched_job=drm_sched_entity_pop_job(entity);if(!sched_job){complete_all(&entity->entity_idle);drm_sched_run_job_queue(sched);// 重新排队自己return;}// ③ 记账 credit + 加入 pending_listatomic_add(sched_job->credits,&sched->credit_count);drm_sched_job_begin(sched_job);// ④ 调用驱动回调:发射到硬件fence=sched->ops->run_job(sched_job);complete_all(&entity->entity_idle);// ⑤ 信号 scheduled fence + 注册完成回调drm_sched_fence_scheduled(s_fence,fence);if(!IS_ERR_OR_NULL(fence)){dma_fence_add_callback(fence,&sched_job->cb,drm_sched_job_done_cb);dma_fence_put(fence);}else{drm_sched_job_done(sched_job,IS_ERR(fence)?PTR_ERR(fence):0);}// ⑥ 唤醒等待者 + 重新排队自己(处理下一个 job)wake_up(&sched->job_scheduled);drm_sched_run_job_queue(sched);}

注意最后的drm_sched_run_job_queue(sched)——这让 work_run_job自我重入(重新入队 submit_wq),形成一个"循环"。只要有就绪的 entity 和足够的 credit,它就会持续处理 job。

4. 步骤详解

4.1 选取 Entity:drm_sched_select_entity()

staticstructdrm_sched_entity*drm_sched_select_entity(structdrm_gpu_scheduler*sched){for(i=DRM_SCHED_PRIORITY_KERNEL;i<sched->num_rqs;i++){entity=(policy==FIFO)?drm_sched_rq_select_entity_fifo(sched,sched->sched_rq[i]):drm_sched_rq_select_entity_rr(sched,sched->sched_rq[i]);if(entity)break;// 找到就不再看低优先级 rq}returnIS_ERR(entity)?NULL:entity;}

关键规则:

  1. 严格优先级:从 KERNEL → HIGH → NORMAL → LOW 遍历,高优先级 rq 有就绪 entity 就选中,不看低优先级
  2. 两种策略:由模块参数drm_sched_policy控制
FIFO 策略
staticstructdrm_sched_entity*drm_sched_rq_select_entity_fifo(structdrm_gpu_scheduler*sched,structdrm_sched_rq*rq){for(rb=rb_first_cached(&rq->rb_tree_root);rb;rb=rb_next(rb)){entity=rb_entry(rb,structdrm_sched_entity,rb_tree_node);if(drm_sched_entity_is_ready(entity)){if(!drm_sched_can_queue(sched,entity))returnERR_PTR(-ENOSPC);// credit 不够reinit_completion(&entity->entity_idle);break;}}returnrb?entity:NULL;}
  • 使用rb_tree_root(红黑树),按oldest_job_waiting时间排序
  • 全局最老 job 优先:哪个 entity 最早入队的 job 等得最久,就先调度它
  • 保证公平性——不会因为某个 entity 提交量大就饿死其他 entity
Round Robin 策略
staticstructdrm_sched_entity*drm_sched_rq_select_entity_rr(structdrm_gpu_scheduler*sched,structdrm_sched_rq*rq){entity=rq->current_entity;// 从 current_entity 之后开始找list_for_each_entry_continue(entity,&rq->entities,list){if(drm_sched_entity_is_ready(entity))gotofound;}// 绕回从头找list_for_each_entry(entity,&rq->entities,list){if(drm_sched_entity_is_ready(entity))gotofound;if(entity==rq->current_entity)break;}returnNULL;found:rq->current_entity=entity;// 记录位置,下次从这之后继续returnentity;}
  • 维护current_entity指针,每次从上次位置继续
  • 每个 entity 轮流获得一次执行机会
Entity "就绪"的定义
staticinlinebooldrm_sched_entity_is_ready(structdrm_sched_entity*entity){if(!spsc_queue_count(&entity->job_queue))returnfalse;// 队列为空if(READ_ONCE(entity->dependency))returnfalse;// 有未解决的依赖returntrue;}

4.2 弹出 Job:drm_sched_entity_pop_job()

选中 entity 后,从它的 job_queue 取出队首 job:

structdrm_sched_job*drm_sched_entity_pop_job(structdrm_sched_entity*entity){sched_job=drm_sched_entity_queue_peek(entity);// 检查所有依赖while((entity->dependency=drm_sched_job_dependency(sched_job,entity))){if(drm_sched_entity_add_dependency_cb(entity,sched_job))returnNULL;// 依赖未就绪,注册回调后返回}// guilty 检查if(entity->guilty&&atomic_read(entity->guilty))dma_fence_set_error(&sched_job->s_fence->finished,-ECANCELED);// 更新 last_scheduledrcu_assign_pointer(entity->last_scheduled,&sched_job->s_fence->finished);spsc_queue_pop(&entity->job_queue);sched_job->entity=NULL;// 断开 job→entity 指针returnsched_job;}

如果依赖未就绪,drm_sched_entity_add_dependency_cb()会注册 fence 回调,回调触发时重新唤醒 scheduler(通过drm_sched_wakeup())。此时pop_job()返回 NULL,run_job_work 重新入队等待。

4.3 Credit 检查与记账

Credit 检查发生在选取 entity 阶段(drm_sched_can_queue()),而非 pop_job 之后:

staticbooldrm_sched_can_queue(structdrm_gpu_scheduler*sched,structdrm_sched_entity*entity){s_job=drm_sched_entity_queue_peek(entity);// 安全阀:超过 credit_limit 的 job 被截断到 limitif(s_job->credits>sched->credit_limit)s_job->credits=sched->credit_limit;returndrm_sched_available_credits(sched)>=s_job->credits;}staticu32drm_sched_available_credits(structdrm_gpu_scheduler*sched){returnsched->credit_limit-atomic_read(&sched->credit_count);}

Credit 生命周期:

credit_count ─────────────────┬────────────────────────────────── pop_job 后 │ atomic_add(job->credits) ↑ 增加 run_job 成功 │ (已计入) hw_fence 信号 │ drm_sched_job_done() │ atomic_sub(job->credits) ↓ 归还 ─────────────────┴──────────────────────────────────

关键设计:credit 在run_job()之前就计入(atomic_add在drm_sched_job_begin()之前),确保并发安全。归还发生在drm_sched_job_done()中,此时 work_free_job 被触发,它在最后调用drm_sched_run_job_queue()重新激活 run_job_work——形成流控反馈环路。

4.4 发射到硬件:run_job()

fence=sched->ops->run_job(sched_job);

这是驱动的 backend 回调。语义:

  • 将 job 的命令写入硬件 ring buffer
  • 返回一个dma_fence*(硬件 fence),当硬件执行完命令时信号
  • 返回 NULL:job 同步完成(无需等待)
  • 返回 ERR_PTR:job 失败

紧随其后:

drm_sched_fence_scheduled(s_fence,fence);

这会设置 parent fence 并信号 scheduled fence(详见 X.5)。

4.5 完成回调注册

if(!IS_ERR_OR_NULL(fence)){r=dma_fence_add_callback(fence,&sched_job->cb,drm_sched_job_done_cb);if(r==-ENOENT)drm_sched_job_done(sched_job,fence->error);// 已信号}
  • 正常路径:注册drm_sched_job_done_cb到硬件 fence
  • 硬件 fence 已经信号(-ENOENT):直接调用 done
  • fence 为 NULL/ERR:立即 done

4.6 Pending List 与 TDR 计时

drm_sched_job_begin()将 job 加入pending_list并启动超时定时器:

staticvoiddrm_sched_job_begin(structdrm_sched_job*s_job){spin_lock(&sched->job_list_lock);list_add_tail(&s_job->list,&sched->pending_list);drm_sched_start_timeout(sched);spin_unlock(&sched->job_list_lock);}

pending_list 中的 job 按提交顺序排列(FIFO)。TDR 只监视队首 job——如果它超时未完成,则认为 GPU 挂了。

5. 完成与释放:drm_sched_free_job_work()

drm_sched_free_job_work():

staticvoiddrm_sched_free_job_work(structwork_struct*w){structdrm_gpu_scheduler*sched=container_of(w,...);bool have_more;job=drm_sched_get_finished_job(sched,&have_more);if(job){sched->ops->free_job(job);// 驱动释放 job 资源if(have_more)drm_sched_run_free_queue(sched);// 还有完成的 job}drm_sched_run_job_queue(sched);// credit 归还了,尝试跑新 job}

drm_sched_get_finished_job()

staticstructdrm_sched_job*drm_sched_get_finished_job(structdrm_gpu_scheduler*sched,bool*have_more){spin_lock(&sched->job_list_lock);job=list_first_entry_or_null(&sched->pending_list,...);if(job&&dma_fence_is_signaled(&job->s_fence->finished)){list_del_init(&job->list);cancel_delayed_work(&sched->work_tdr);// 更新下一个 job 的 timestamp(更准确的 TDR 起点)next=list_first_entry_or_null(...);if(next){next->s_fence->scheduled.timestamp=dma_fence_timestamp(&job->s_fence->finished);*have_more=dma_fence_is_signaled(&next->s_fence->finished);drm_sched_start_timeout(sched);// 为下一个 job 重启 TDR}}else{job=NULL;}spin_unlock(&sched->job_list_lock);returnjob;}

关键点:

  1. 只取 pending_list 的队首job(FIFO 顺序释放)
  2. 取出后取消 TDR 定时器,为下一个 job 重新启动
  3. 更新下一个 job 的 scheduled timestamp——让 TDR 超时从前一个 job 完成时开始计算

6. 完整数据流时序

用户态: ioctl submit → drm_sched_job_init() → 分配 s_fence → drm_sched_job_arm() → 初始化 fence,选择 scheduler → dma_resv_add_fence(bo, &s_fence->finished) → drm_sched_entity_push_job() → spsc_queue_push() → job 入队 entity → drm_sched_wakeup() → queue_work(work_run_job) work_run_job (submit_wq): drm_sched_select_entity() → 按优先级遍历 rq[] → FIFO: rb_tree 最老优先 / RR: 轮转 → drm_sched_can_queue(): credit 检查 → 返回就绪 entity drm_sched_entity_pop_job() → 遍历 dependencies, 检查每个 fence → 依赖未就绪? 注册 cb, return NULL → 等待 → 所有依赖就绪? pop job, 断开 entity 指针 atomic_add(credits) → credit 记账 drm_sched_job_begin() → 加入 pending_list, 启动 TDR sched->ops->run_job() → 驱动发射到硬件 ring drm_sched_fence_scheduled() → ★ scheduled fence 信号 dma_fence_add_callback(hw_fence, done_cb) → 注册完成回调 drm_sched_run_job_queue() → 自我重入,处理下一个 job 硬件完成: hw_fence 信号 → drm_sched_job_done_cb() → atomic_sub(credits) → 归还 credit → drm_sched_fence_finished() → ★ finished fence 信号 → drm_sched_run_free_queue() → queue_work(work_free_job) work_free_job (submit_wq): drm_sched_get_finished_job() → 从 pending_list 取队首完成的 job sched->ops->free_job() → 驱动释放 job 资源 drm_sched_run_job_queue() → credit 归还后尝试新 job

7. 流控机制深度分析

7.1 为什么需要流控

硬件 ring buffer 容量有限。如果 scheduler 无限制地调用run_job(),ring buffer 溢出会导致:

  • 驱动必须在run_job()中阻塞(违反 workqueue 语义)
  • 或者丢弃 job(不可接受)

Credit 机制让 scheduler在软件层面背压:当已发射但未完成的 job 总 credit 达到上限时,停止发射新 job。

7.2 流控反馈环路

┌──────────────────────────────────────────────────────────────┐ │ │ │ run_job_work: │ │ can_queue()? ──No──→ 返回 ERR_PTR(-ENOSPC) │ │ │ select_entity 返回 NULL │ │ Yes run_job_work 退出(不自我重入) │ │ │ │ │ ▼ │ │ run_job() │ │ │ │ │ ├──→ credit_count += job.credits │ │ │ │ │ ▼ (等待硬件完成) │ │ job_done() │ │ │ │ │ ├──→ credit_count -= job.credits │ │ │ │ │ └──→ run_free_queue() ──→ free_job_work │ │ │ │ │ └──→ run_job_queue() ────┘ │ (重新激活 run_job) └──────────────────────────────────────────────────────────────┘

当 credit 不够时,drm_sched_select_entity()返回 NULL(因为drm_sched_can_queue()返回ERR_PTR(-ENOSPC),被转为 NULL)。此时 run_job_work不会自我重入——调度器进入"等待"状态。

当硬件完成某个 job,drm_sched_job_done()归还 credit 并触发work_free_job。work_free_job最后调用drm_sched_run_job_queue()重新激活work_run_job——此时 credit 已释放,新 job 可以执行。

7.3 安全阀:超大 Job

if(s_job->credits>sched->credit_limit){dev_WARN(sched->dev,"Jobs may not exceed the credit limit, truncate.\n");s_job->credits=sched->credit_limit;}

如果某个 job 的 credit 超过 scheduler 的 credit_limit,强制截断到 limit。这保证了前向进展——否则这个 job 永远无法被调度(因为 available_credits 永远不够)。

8. 暂停与恢复

Scheduler 支持两种暂停机制:

8.1 pause_submit(轻量暂停)

staticvoiddrm_sched_run_job_queue(structdrm_gpu_scheduler*sched){if(!READ_ONCE(sched->pause_submit))queue_work(sched->submit_wq,&sched->work_run_job);}

当pause_submit = true时,drm_sched_run_job_queue()和drm_sched_run_free_queue()都不会入队新 work。效果:

  • 不会调度新 job
  • 不会释放已完成的 job
  • 已在硬件上执行的 job 不受影响

8.2 drm_sched_wqueue_stop/start(完全停止)

voiddrm_sched_wqueue_stop(structdrm_gpu_scheduler*sched){WRITE_ONCE(sched->pause_submit,true);cancel_work_sync(&sched->work_run_job);// 等待正在执行的 work 完成cancel_work_sync(&sched->work_free_job);}voiddrm_sched_wqueue_start(structdrm_gpu_scheduler*sched){WRITE_ONCE(sched->pause_submit,false);queue_work(sched->submit_wq,&sched->work_run_job);queue_work(sched->submit_wq,&sched->work_free_job);}

用于 TDR 恢复流程:

  1. drm_sched_stop()→drm_sched_wqueue_stop()→ 等待 work 完成
  2. 驱动执行 GPU reset
  3. drm_sched_start()→ 重新提交 pending jobs →drm_sched_wqueue_start()

8.3 TDR 超时控制

unsignedlongdrm_sched_suspend_timeout(structdrm_gpu_scheduler*sched){// 将 timeout 延长到 MAX_SCHEDULE_TIMEOUT(等效暂停 TDR)mod_delayed_work(sched->timeout_wq,&sched->work_tdr,MAX_SCHEDULE_TIMEOUT);returnremaining;}voiddrm_sched_resume_timeout(structdrm_gpu_scheduler*sched,unsignedlongremaining){if(list_empty(&sched->pending_list))cancel_delayed_work(&sched->work_tdr);elsemod_delayed_work(sched->timeout_wq,&sched->work_tdr,remaining);}

用于长时间操作(如 VM page table 更新)期间暂停 TDR 监控,避免误触超时。

9. 自我驱动循环的完整触发链

Scheduler 没有一个永远运行的线程。它是事件驱动的:

触发事件触发链结果
用户 push jobpush_job()→drm_sched_wakeup()→queue_work(run_job)开始调度
run_job_work 完成一个 job末尾drm_sched_run_job_queue()继续调度下一个
依赖 fence 信号cb →drm_sched_wakeup()→queue_work(run_job)被阻塞的 entity 解除
硬件 fence 信号job_done_cb()→drm_sched_run_free_queue()释放 + credit 归还
free_job_work 完成末尾drm_sched_run_job_queue()credit 归还后继续调度

停止条件:当所有 entity 都空或都被依赖阻塞,且 credit 耗尽时,run_job_work 不再自我入队——调度器安静等待外部事件。

10. 与其他组件的交互图

用户态 ioctl │ ▼ drm_sched_entity_push_job() │ ▼ drm_sched_wakeup() ┌──────────────────────────────────┐ │ work_run_job │ │ │ │ select_entity() │ │ │ │ │ ▼ │ │ pop_job() │ │ │ │ │ ├── dep fence unsignaled ───┼──→ 注册 cb, 退出 │ │ │ (fence 信号后重新激活) │ ▼ │ │ can_queue()? │ │ │ │ │ ├── No (credit 不够) ───────┼──→ 退出 │ │ │ (free_job 归还后重新激活) │ ▼ │ │ run_job() ──→ 硬件 ring │ │ │ │ │ ▼ │ │ scheduled fence 信号 │ │ │ │ │ └──→ 自我重入 ──────────────┘ │ │ work_free_job │ │ │ │ get_finished_job() │ │ │ │ │ ▼ │ │ free_job() │ │ │ │ │ └──→ run_job_queue() ───────┘ │ └──────────────────────────────────┘ submit_wq

11. 小结

维度要点
执行模型基于 ordered workqueue 的两个 work item,事件驱动
选取策略严格优先级 + 同优先级内 FIFO(红黑树)或 Round Robin
流控credit_count/credit_limit,满时不调度,释放后自动恢复
自驱动run_job_work 末尾自我入队 + free_job_work 末尾激活 run_job
停止条件无就绪 entity、credit 满、或 pause_submit=true
暂停/恢复pause_submit(轻量)或 wqueue_stop/start(完全,含 sync)
pending_list已发射未完成的 job 队列,TDR 监控对象
串行保证ordered workqueue 保证同 scheduler 的 run/free 不并发

下一篇将深入 TDR 机制——当 pending_list 队首 job 超时未完成时,drm_sched_job_timedout()如何触发驱动的 GPU 恢复流程。

相关新闻

  • 上海GEO优化贴牌主体爱搜索GEO,重塑AI时代品牌曝光新路径 - 品牌报告
  • Gemini多模态原生架构:从胶水层到共生训练的技术范式迁移
  • 2026年南昌K金回收怎么选?5个关键考察点与专业机构推荐,看这篇就够了 - 本地品牌推荐

最新新闻

  • AI Runtime 重构:会话即事件日志的工程实践
  • 15A大电流BLDC电机FOC控制实战解析
  • 3D虚拟打版成本节约测算程序,对比传统实体样衣耗材,工时,节省总额。
  • IMU传感器与6DoF姿态解算在无人机飞控中的应用
  • HyperFlex 架构(1):介绍与设计摘要
  • Tabby终极指南:现代开发者的全能终端解决方案

日新闻

  • Python Playwright录制功能:从零到一构建自动化测试脚本
  • 如何用开源工具永久保存你心爱的小说:novel-downloader全攻略
  • In-Context Learning不是教知识,而是模式对齐:从5个示例到100个工业级样本的真相

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号