RK3588 kernel-6.1 fs/ext4/fast_commit.c 快速提交(Fast Commit)分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/fast_commit.c(Ext4 细粒度日志 与 JBD2 fast commit 对接:跟踪、落盘、恢复)- 盘上 TLV 布局与 tag 定义:
fs/ext4/fast_commit.h(注释要求与 e2fsprogs 同源头文件 字节一致) - JBD2 侧:
jbd2_fc_begin_commit/jbd2_fc_end_commit/ fallback、replay 回调注册等(本文件只实现 ext4 策略与 j_fc_replay_callback)
2. 代码作用(一句话)
在挂载选项允许时,把一次事务里对元数据的改动记成 TLV(tag-length-value)增量日志 追加进 journal 的 fast commit 区;fsync/事务提交时优先只刷这些增量与用户数据,再写 TAIL(CRC + TID) 保证原子可见性;恢复阶段按 TLV 重放目录项、inode 体、extent 增删等 终态,无法覆盖的操作则 ext4_fc_mark_ineligible 回退 完整 jbd2 commit。
3. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 传统 full commit 对小块元数据也要整块/大批 buffer 记账,延迟与 I/O 放大 | 对 符合条件 的事务只写 紧凑 TLV 序列 + 相关 inode 数据刷盘,减少相对 full commit 的工作量(具体收益依赖负载) |
| 仍需与现有 jbd2 事务模型兼容 | ext4_fc_commit 在 jbd2_fc_begin_commit 栅栏内工作;失败走 jbd2_fc_end_commit_fallback → 等价于完成 full transaction |
| 部分 VFS/元数据操作难以表达为安全增量 | ext4_fc_mark_ineligible 设置 EXT4_MF_FC_INELIGIBLE 与 s_fc_ineligible_tid,此后直到对应 TID 的提交只做 full commit(见 fast_commit.h 中 EXT4_FC_REASON_*) |
| 崩溃后如何认定一段 fast commit 有效 | 每条 fast commit 以 EXT4_FC_TAG_TAIL 结束,内含 事务 TID 与 从头到尾 CRC;replay 仅当校验通过;日志里可存在 多个 TAIL(多次 fsync 的示意见文件头注释) |
| 重放须幂等 | 不记「过程」而记 可重复的终态(如 rename 记成 link/unlink + inode 片段);文件开头用 rm/mv 例子说明 |
| CREATE 需先有 inode 再有目录项 | ext4_fc_commit_dentry_updates 对 CREAT 先 ext4_fc_write_inode / ext4_fc_write_inode_data 再写 dentry TLV,恢复时可当「无名 inode 再链接」 |
| ADD_RANGE 重放时分配器可能占掉将被指派的块 | SCAN 阶段 ext4_fc_record_regions 收集 (ino, lblk, pblk, len);ext4_fc_replay_check_excluded 在回放分配路径中排除这些物理块 |
| 与并发更新的竞态 | ext4_fc_start_update / ext4_fc_stop_update:某 inode 若在本次 fast commit 列表中,通过 EXT4_STATE_FC_COMMITTING + 等待队列,避免 提交过程中继续改同一 inode;ext4_fc_track_template 用 i_sync_tid 区分同一事务内首次/更新跟踪 |
| 日志与数据设备分离 | ext4_fc_perform_commit 在写 fast commit 块前对 journal->j_fs_dev blkdev_issue_flush,避免数据未落盘而元数据日志已可见 |
| inode / extent / ES 树在重放时的语义 | 重放期间 **`s_mount_state |
4. TLV 类别(与 fast_commit.h 一致)
| Tag | 含义(概要) |
|---|---|
| HEAD | feature 掩码 + 本段 fast commit 起始 TID |
| UNLINK / LINK / CREAT | 目录项删除、添加、创建(值体 ext4_fc_dentry_info:父 ino、子 ino、名字) |
| ADD_RANGE | 某 inode 新增 extent(盘上 12 字节 ext4_extent 嵌入) |
| DEL_RANGE | 逻辑块区间删除 |
| INODE | 需回放的 raw inode(iblocks 等由重算而非直接信任) |
| PAD | 对齐填充 |
| TAIL | CRC + TID,标识本段 fast commit 完整结束 |
5. 提交路径概要(ext4_fc_perform_commit)
文件头注释给出的顺序与实现对应关系:
ext4_fc_submit_inode_data_all/ext4_fc_wait_inode_data_all:参与本次提交的 inode 用户数据先下发并等待(ordered 语义相关)。- 外挂 journal 时 flush 文件系统设备。
- 若本 TID 下首次 fast commit,写 HEAD TLV。
ext4_fc_commit_dentry_updates:目录项队列刷成 TLV(CREAT 先 inode 再 dentry)。- 遍历
s_fc_q[FC_Q_MAIN]中FC_COMMITTING的 inode:数据再 inode TLV。 ext4_fc_write_tail:写 TAIL,固化 CRC。
入口 ext4_fc_commit:jbd2_fc_begin_commit → 检查 ineligible → ext4_fc_perform_commit → jbd2_fc_wait_bufs → jbd2_fc_end_commit;任一步失败则 jbd2_fc_end_commit_fallback 并更新统计为 FAILED/INELIGIBLE。
队列 staging: 若在 full / fast commit 正在进行(JBD2_FULL_COMMIT_ONGOING / JBD2_FAST_COMMIT_ONGOING),新跟踪的 inode 与 dentry 进入 FC_Q_STAGING,在 ext4_fc_cleanup 里 splice 回 MAIN。
6. 跟踪入口(与 namei/写路径的关系)
ext4_fc_track_link/track_unlink/track_create:目录操作时入s_fc_dentry_q;加密目录名导致-EOPNOTSUPP并EXT4_FC_REASON_ENCRYPTED_FILENAME。ext4_fc_track_inode:inode 元数据变化。ext4_fc_track_range:extent 层逻辑块区间增删。- 以上经
ext4_fc_track_template可将 inode 挂入s_fc_q,并与 当前handle的 TID 绑定。
7. 恢复路径(ext4_fc_replay)
PASS_SCAN:ext4_fc_replay_scan顺序解析 TLV,维护 滚动 CRC,遇到合法 TAIL 且 TID/CRC 匹配 则确定fc_replay_num_tags;ADD_RANGE 同时ext4_fc_record_regions。异常短包/未知 tag 等返回 STOP 或错误码,由 JBD2 决定是否中止本段。- 重放 pass:逐 tag 调用
ext4_fc_replay_*(unlink/link/create/add_range/del_range/inode)。计数耗尽后ext4_fc_set_bitmaps_and_counters等收尾。 ext4_fc_init:即使 未启用 fast commit 挂载选项,仍注册j_fc_replay_callback,以便挂载 旧日志 时仍能重放曾写入的 fast commit 块。
清理与 EXT4_FC_REPLAY 复位见 ext4_fc_replay_cleanup(在文件后部,与卸载/重放结束配合)。
8. 文件内记载的 TODO / 限制(阅读源码时值得注意)
- Replay 路径若全面用 journal handle 做原子化、以及在重放前持久化 FC_REPLAY 超块状态,可进一步加固 重放中途崩溃 的场景(注释 TODO 0)。
- Fast commit 提交路径曾注释 全文件系统级锁 的代价;后续可更多把
ext4_fc_start/stop_update与journal_start/stop对齐以缩小锁粒度(TODO 1)。 - 更多 ineligible 场景 待覆盖(TODO 2)。
9. 小结
fast_commit.c 不是替代 ext4 的 extent/目录 实现,而是在 JBD2 之上增加一条 「记录终态增量」 的快速提交通道:目录与 inode 变更 + 块区间增删 可在一次 fsync 相关提交中以较少日志体积落盘;通过 TAIL CRC、幂等 TLV 设计、回放前 SCAN 与 块排除列表 控制正确性;不适用的操作统一 ext4_fc_mark_ineligible 回退 full journal commit。分析某条元数据路径是否走 fast commit,应在业务代码中搜索 ext4_fc_track_* 与 ext4_fc_mark_ineligible,并与本文件的 commit/replay 流程对照。
正在加载留言…