RK3588 kernel-6.1 fs/ext4/ext4_jbd2.c 与 JBD2 对接层分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/ext4_jbd2.c(ext4 ↔ jbd2 的运行时胶水层,约 392 行)- 配套声明与宏、事务额度、回调、
ext4_handle_valid等:**ext4_jbd2.h**(本文件大量逻辑依赖其中的宏与 inline)
2. 代码作用(一句话)
ext4_jbd2.c 把 ext4 的元数据修改统一接到 JBD2:开启/结束事务、在「无日志 / fast commit 回放」下用伪 handle** 走同一套 API;封装 get_write_access / get_create_access / dirty_metadata / forget,并在启用 metadata_csum 时为缓冲块挂 jbd2 triggers;同时处理日志已中止、整盘只读、块设备异步写失败 等边界,保证崩溃一致性与错误可恢复路径一致。**
3. 解决了什么问题
| 问题 | ext4_jbd2.c 中的处理方式 |
|---|---|
| ext4 与 jbd2 API 分散、错误路径不统一 | 集中实现 __ext4_journal_* / __ext4_forget / __ext4_handle_dirty_metadata,上层通过 ext4_jbd2.h 宏传入 __FILE__/__LINE__ 便于定位 |
| 无外部 journal 或 fast commit 回放仍需同一控制流 | EXT4_FC_REPLAY 或无 s_journal 时 ext4_get_nojournal():用 current->journal_info 存引用计数(伪装 handle_t *),ext4_handle_valid 据此区分真/假 handle |
| 日志已在后台 abort(如 commit 线程 I/O 错) | ext4_journal_check_start:若 is_journal_aborted,ext4_abort 把整个 FS 只读拉齐,返回 -EROFS,避免在已坏日志上继续提交脏元数据 |
| 强制关机 / 只读挂载 | ext4_forced_shutdown → -EIO;sb_rdonly → -EROFS;并对 SB_FREEZE_COMPLETE 打 WARN_ON |
| 释放块后日志里仍可能重放旧内容(重放死元数据) | __ext4_forget:在 ordered/writeback + 元数据或应对 data 记日志 时走 jbd2_journal_revoke;full data journal 或非记日志数据块用 jbd2_journal_forget;无日志则 bforget |
| 元数据校验和在事务提交时需重算 | __ext4_journal_get_write_access / get_create_access 在 ext4_has_metadata_csum 且 trigger_type != EXT4_JTR_NONE 时 jbd2_journal_set_triggers,与 s_journal_triggers[] 协作 |
| 块设备曾对元数据 buffer 异步写失败,再读旧页写回导致磁盘不一致 | ext4_check_bdev_write_error:比较 mapping->wb_err 与 sbi->s_bdev_wb_err,发现新进错误则 ext4_error_err |
| 事务额度不足 | __ext4_journal_ensure_credits:无效 handle 直接 0;否则与 ext4_journal_extend(见头文件)配合补足 buffer / revoke 额度 |
| 数据日志模式与 delayed allocation、加密的组合 | ext4_inode_journal_mode:无 journal 为 writeback;delalloc 开启时 不对普通文件强行 data journal(避免不支持的组合);加密普通文件 固定 ordered,避免 data journal 语义问题 |
| 无 journal 路径上 inode 需同步落盘 | __ext4_handle_dirty_metadata:无效 handle 时 mark_buffer_dirty,若 inode_needs_sync 则 sync_dirty_buffer 并检查 EIO |
4. 核心函数说明
4.1 ext4_inode_journal_mode
根据 s_journal、挂载 DATA_FLAGS、inode EXT4_INODE_JOURNAL_DATA / EA_INODE、DELALLOC、IS_ENCRYPTED 等,返回三类模式之一:journal data、ordered、writeback。
供 ext4_should_journal_data 等 inline(在 ext4_jbd2.h)判断 __ext4_forget、额度宏等分支。
4.2 ext4_get_nojournal / ext4_put_nojournal
- 将
current->journal_info当作 无符号计数(递增/递减),上限EXT4_NOJOURNAL_MAX_REF_COUNT。 - 与
ext4_handle_valid(计数值小于阈值视为“假 handle”)配对,使 无日志 代码不必遍地if (journal)。
4.3 ext4_journal_check_start
在 jbd2_journal_start 类操作前统一检查:强制关机、只读、已冻结、日志 abort。失败则不让新事务开始,避免错误扩大。
4.4 __ext4_journal_start_sb / __ext4_journal_stop
- start:校验通过后,有 journal 且非 FC_REPLAY →
jbd2__journal_start(带GFP_NOFS、类型、调用行号等);否则 假 handle。 - stop:假 handle 走
ext4_put_nojournal;真 handlejbd2_journal_stop,合并handle->h_err与 stop 返回值,错误走__ext4_std_error。
4.5 __ext4_journal_start_reserved
对已 jbd2 预留额度 的 handle 再 jbd2_journal_start_reserved;若 ext4_journal_check_start 失败则 jbd2_journal_free_reserved 并返回错误指针。
4.6 __ext4_journal_ensure_credits
若当前 buffer credits / revoke credits 不够目标值,计算差额并 ext4_journal_extend(扩展事务占位)。
4.7 ext4_journal_abort_handle
printk 错误上下文后 jbd2_journal_abort_handle,用于 get_*_access / forget / dirty 等失败路径,避免在已失败事务上继续误用。
4.8 ext4_check_bdev_write_error
在 __ext4_journal_get_write_access 里,若 bh->b_bdev->bd_super 存在,则检查该 bdev 映射上的 写回错误序号,防止脏元数据页与磁盘真相割裂时再次写盘扩大损坏。
4.9 __ext4_journal_get_write_access
- 可选
ext4_check_bdev_write_error - 有效 handle:
jbd2_journal_get_write_access,失败则 abort handle - 按需
jbd2_journal_set_triggers(metadata checksum)
4.10 __ext4_forget
释元数据或数据块时的日志语义核心:
- 无有效 handle:
bforget(bh) - journal data 模式,或 数据块且不该 journal data:
jbd2_journal_forget(注释:V1 superblock 不支持 revoke 等历史原因) - 否则(元数据,或应对 data journal 的数据):
jbd2_journal_revoke(blocknr, bh),失败ext4_error bh可为 NULL:仅撤销 journal 中仍存在的块号记录
4.11 __ext4_journal_get_create_access
新分配块的 jbd2_journal_get_create_access + 与写访问相同的 trigger 逻辑。
4.12 __ext4_handle_dirty_metadata
- 有效 handle:
jbd2_journal_dirty_metadata,设置buffer_meta/prio/uptodate;异常时 abort 并ext4_error_inode(或pr_err无 inode) - 无效 handle:
mark_buffer_dirty(_inode);若inode_needs_sync则同步写 buffer 并检查 I/O 错误
5. 与 ext4_jbd2.h 的分工(阅读代码时建议一起看)
| 内容 | 位置 |
|---|---|
ext4_journal_start / ext4_journal_stop 等宏 |
ext4_jbd2.h |
ext4_handle_valid、ext4_journal_extend、ext4_journal_restart |
头文件 inline |
事务块数估算宏(EXT4_SINGLEDATA_TRANS_BLOCKS 等) |
头文件 |
ext4_journal_callback_add 与 commit 后回调 |
头文件 |
| 本文件 | 上述行为的 .c 实现体(start/stop/forget/dirty/get_access/ensure_credits 与 inode journal mode、bdev 写错检测) |
6. 小结
**ext4_jbd2.c** 自身不包含完整日志布局或 recovery 实现(那是 jbd2 与 ext4 super/inode 挂载路径的职责),它负责:在 ext4 元数据路径上正确调用 JBD2(journal_start / get_*_access / dirty_metadata / stop 等)、在有无 journal 下统一抽象、在释放块时选择 revoke/forget、在 metadata checksum 下挂 commit 前触发器、并把 I/O/abort 类失败收敛为 ext4 错误处理。阅读任意 ext4_journal_* / ext4_forget / ext4_handle_dirty_metadata 调用点时,应以本文件与 ext4_jbd2.h 为权威语义说明。
正在加载留言…