rk3588/kernel-6.1 io_uring 模块架构与实现原理

rk3588/kernel-6.1 io_uring 模块架构与实现原理

1. 模块定位与编译入口

io_uring 是 Linux 6.1 中高性能异步 I/O 框架,核心思想是:

  • 用户态与内核态共享 SQ/CQ Ring 内存,减少系统调用与拷贝成本;
  • 通过统一 SQE opcode 抽象,覆盖文件、网络、超时、poll、资源注册等多类异步操作;
  • task_workio-wqSQPOLLIOPOLL 等机制平衡时延与吞吐。

在本内核树中:

  • 配置入口:init/Kconfigconfig IO_URING(默认 y,并 select IO_WQ);
  • IO_WQ 定义位于 fs/Kconfig
  • 构建入口:io_uring/Makefile,由 io_uring.o + opdef.o + 各 opcode 子模块(rw.cnet.ctimeout.cpoll.c 等)组成。

2. 目录分层(按职责)

io_uring/ 目录可分为 5 层:

  1. 核心控制层

    • io_uring.c:syscall 入口、ring 创建/销毁、提交流程、完成队列提交、等待逻辑。
    • io_uring.h:核心内联与跨文件函数声明。
    • include/linux/io_uring_types.h:核心结构定义(io_ring_ctxio_kiocbio_rings)。
  2. opcode 分发表与调度层

    • opdef.c/.hio_op_defs[],定义每个 opcode 的 prep/issue/cleanup/fail 能力与约束。
  3. 执行后端层

    • io-wq.c/.h:内核 worker 池(阻塞/慢路径异步执行)。
    • sqpoll.c/.h:SQ 轮询线程(减少用户提交 syscall)。
    • poll.c:fast poll 与轮询唤醒链路。
  4. 功能子模块层(按 opcode 家族)

    • 文件/元数据:rw.cfs.copenclose.cstatx.cxattr.csync.csplice.cadvise.c
    • 网络:net.cmsg_ring.curing_cmd.c
    • 时间与取消:timeout.ccancel.c
    • 资源管理:rsrc.ckbuf.cfiletable.ctctx.c
    • 观测支持:fdinfo.cnotif.c
  5. 共享工具层

    • alloc_cache.hrefs.hslist.h 等通用基础设施。

3. 核心数据结构

3.1 struct io_rings(用户态共享 ring 元数据)

  • 包含 sq.head/tailcq.head/tail、mask、entries、sq_droppedcq_overflowcqes[]
  • 职责边界清晰:用户写 SQ tail / 读 CQ tail内核写 SQ head / CQ tail

3.2 struct io_ring_ctx(每个 ring fd 一个上下文)

  • 提交面:uring_locksq_arraysq_sqessubmit_state、固定文件/缓冲资源。
  • 完成面:completion_lock、CQ 缓存指针、cq_wait、overflow list。
  • 异步面:iopoll_listwork_llistsq_data(SQPOLL)、io_wq 关联。
  • 资源面:file_tableuser_bufsrsrc_nodepersonalityrestrictions

3.3 struct io_kiocb(单请求对象)

  • 绑定 opcode 与请求态:opcodeflagscqefiletasklink
  • 支持多态命令区:cmd.data[56] 供各子模块覆盖使用。
  • 维护生命周期与异步状态:refspoll_refsasync_dataio_task_workwork

3.4 io_op_defs[](操作语义表)

  • 每个 opcode 定义:
    • 是否需要文件、是否支持 iopoll、是否支持 buffer select;
    • prep() 参数解析;
    • issue() 执行;
    • cleanup()/fail() 兜底路径。
  • 使 opcode 扩展呈“声明式分发表 + 独立模块实现”结构。

4. 三条主调用链

4.1 创建链:io_uring_setup -> io_uring_create

  1. SYSCALL_DEFINE2(io_uring_setup) 进入;
  2. 校验 io_uring_params.flags
  3. io_uring_create()
    • 计算并规范化 sq_entries/cq_entries(默认 cq=2*sq);
    • io_ring_ctx_alloc() 创建上下文;
    • io_allocate_scq_urings() 分配共享 ring 与 SQE 区;
    • io_sq_offload_create() 建立 SQPOLL/附加 WQ;
    • 初始化 sq_off/cq_off/features 返回给用户态;
    • anon_inode_getfile_secure() 创建 ring file;
    • io_uring_install_fd() 安装 fd。

实现要点:通过一个匿名 fd 对外暴露 ring,用户再通过 mmap 映射 SQ/CQ 与 SQE 区。

4.2 提交链:io_uring_enter -> io_submit_sqes

  1. SYSCALL_DEFINE6(io_uring_enter) 获取 ctx;
  2. 非 SQPOLL 场景下加 uring_lock 并进入 io_submit_sqes(ctx, to_submit)
  3. io_get_sqe() 从共享 SQ 拉取一个 SQE(用 READ_ONCE 保证稳定读);
  4. io_submit_sqe()
    • io_init_req() 解析 SQE(flags/限制/文件/cred/link/drain 等);
    • 根据 link / force_async 决定 inline issue 或 fallback 到异步;
  5. io_queue_sqe() -> io_issue_sqe()
    • io_op_defs[opcode].issue() 分发到具体模块;
    • 成功后进入完成流程,失败走 async/poll/iowq 兜底;
  6. 批处理结束 io_commit_sqring() 推进 sq.head

实现要点

  • 支持 IORING_SETUP_SUBMIT_ALL:部分 SQE 出错不立即中断整批;
  • 支持 link/hardlink/drain:表达跨请求依赖;
  • 支持 personality:按请求临时切换 cred。

4.3 完成链:io_req_complete_post -> CQ ring

  1. 请求完成后写入 req->cqe
  2. __io_fill_cqe_req() 获取 CQE 槽(优先走缓存 cqe_cached);
  3. io_commit_cqring() 以 release 语义更新 cq.tail
  4. io_cqring_wake() 唤醒 cq_wait 上等待线程;
  5. 如果 CQ 满,进入 overflow 路径并更新 cq_overflow/check_cq

实现要点

  • CQ 写入与 tail 更新严格内存序;
  • 通过 overflow + dropped 标志向用户态传达背压与丢失信息。

5. 执行模型(四种路径)

5.1 inline(最短路径)

  • 提交线程直接 issue(),适用于可立即完成或非阻塞成功的请求。

5.2 task_work defer

  • 将完成或后续动作挂到 task_work,在返回用户态前/合适时机运行。
  • IORING_SETUP_DEFER_TASKRUN 可将本地 work 延后到提交者线程统一处理。

5.3 io-wq worker pool(阻塞慢路径)

  • io-wq.c 提供每 NUMA 节点 io_wqe 池;
  • 分 bounded/unbounded 两类账户;
  • 对可能阻塞的请求转交 worker 执行,避免卡住提交线程。

5.4 SQPOLL / IOPOLL(轮询路径)

  • SQPOLL:专用 io_sq_thread() 轮询 SQ,减少提交 syscall;
  • IOPOLL:适用于支持轮询完成的设备 I/O,请求挂入 iopoll_list 并由轮询收割。

6. 资源管理机制

6.1 固定文件与固定缓冲

  • rsrc.c + filetable.c + kbuf.c 管理注册资源;
  • 固定资源减少每请求 fget/pin 成本,提升高 QPS 稳定性。

6.2 内存记账与限制

  • __io_account_mem()RLIMIT_MEMLOCK 绑定;
  • 同步维护 user->locked_vmmm->pinned_vm,防止无限 pin 页。

6.3 资源异步释放

  • io_rsrc_put_work() 通过 workqueue 回收;
  • 可选给 tag 发送辅助 CQE,通知用户资源释放完成。

7. 并发与内存序设计

关键并发策略:

  • 提交主路径由 uring_lock 串行化;
  • 完成面由 completion_lock + CQ 缓存批量化降低锁开销;
  • ring head/tail 更新使用 smp_store_release / smp_load_acquire
  • 用户共享内存读取关键字段使用 READ_ONCE,避免被用户态并发改写导致重读不一致。

核心目标:在“用户共享内存 + 内核并发执行”模型下保证可见性与一致性。


8. opcode 扩展方法(实现视角)

新增一个 opcode 的标准步骤:

  1. 新增 xxx.c/.h,实现 prep/issue/(cleanup)
  2. opdef.cio_op_defs[] 填入能力位与函数指针;
  3. Makefile 加入目标文件;
  4. 若需要 register 能力、资源或 fdinfo 展示,再接入 io_uring_register / fdinfo 路径。

这套模式让核心调度保持稳定,具体语义在子模块独立演进。


9. 常见问题定位建议

  1. 提交了但没完成

    • sq.head/tailcq.head/tail 是否推进;
    • 检查 cq_overflowsq_dropped 是否增长;
    • 核查 IORING_SETUP_DEFER_TASKRUN 下 task_work 是否被及时运行。
  2. 高负载下延迟抖动

    • 检查是否频繁 fallback 到 io-wq(说明 inline 非阻塞命中率低);
    • 评估固定文件/固定缓冲是否启用;
    • 观察 SQPOLL/IOPOLL 与实际设备能力是否匹配。
  3. 资源注册失败或 ENOMEM

    • 关注 RLIMIT_MEMLOCKpinned_vm、固定缓冲大小;
    • 检查注册参数和索引范围是否越界。
  4. 链式请求行为异常

    • 核对 IOSQE_IO_LINKIOSQE_IO_HARDLINKIOSQE_IO_DRAIN 组合;
    • 关注失败后 REQ_F_FAIL 与 link 断链策略。

10. 一句话总结

io_uring 在该 6.1 内核中的架构本质是:
“共享 ring + opcode 分发表 + 多后端执行引擎(inline/task_work/io-wq/sqpoll)+ 资源注册体系”,通过细粒度并发控制与内存序保证,实现高吞吐、低 syscall 成本的统一异步 I/O 平台。

文章互动

阅读 --

留言

0 条留言

正在加载留言…