RK3588 kernel-6.1 fs/ext4/block_validity.c 元数据块保护区分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/block_validity.cext4.h中struct ext4_system_blocks、s_system_blks- 挂载选项:
block_validity/noblock_validity(super.c) - 调用方:
inode.c中__check_block_validity,以及ext4_check_blockref
2. 代码作用(一句话)
block_validity.c 在挂载时建立一棵 红黑树,记录全文件系统中所有 仅允许作元数据使用 的物理块区间(超级块备份、GDT、位图、inode 表、以及日记 inode 所映射的数据块等);之后当 inode 的块映射被解释时,若某物理块落在这些区间内且不属于「被允许的 inode」(如 journal inode),则判定为 无效引用,报 损坏 并阻止继续使用错误映射。
文件头注释:
1 | * Track which blocks in the filesystem are metadata blocks that |
3. 解决了什么问题
3.1 元数据被误当「文件数据块」
磁盘或软件缺陷可能导致:
- extent / 间接块中写入的 物理块号 落在 块组位图、inode 表、超级块备份 等区域;
- 若不做检查,缓冲写或 DIO 可能 覆写元数据,造成 整卷损坏,且比单文件损坏更难恢复。
block validity 在 ext4_map_blocks 等路径更新映射前(见 inode.c),对即将采用的 m_pblk 区间 调用检查,重叠系统区则 拒绝(-EFSCORRUPTED / ext4_error_inode),把问题 限制在发现点。
3.2 日记 inode 的合法占用
Journal 以普通 inode 形式存放,其 文件块 在物理上仍是数据区,但 从「普通文件」视角 这些块应视为 由该保留 inode 专用。因此树节点带 ino:
1 | else { |
- 重叠系统区且
entry->ino == 0:一律 无效(普通文件不得占用)。 - 重叠且
entry->ino == inode->i_ino:允许(例如正在访问的是 journal inode 自身映射)。
ext4_protect_reserved_inode 把 journal inode 的 ext4_map_blocks 结果逐段 add_system_zone(..., ino),与固定元数据区一起进入同一棵树。
3.3 重叠检测与布局自洽
add_system_zone 在插入 RB 树时若发现新区间与已有区间 几何重叠(非简单相邻合并),返回 -EFSCORRUPTED。这能发现 系统区描述自相矛盾 或 错误重复登记 等异常(例如 ext4_protect_reserved_inode 中 journal 块与已有区重叠会打错误日志)。
4. 核心数据结构
1 | struct ext4_system_zone { |
start_blk+count:半开或闭区间意义上的 连续物理块(实现里判断用start_blk + count)。ino == 0:通用元数据(位图、inode 表等),任何文件 inode 不可占用。ino != 0:该 inode 专属 的保留块(当前实际用于 journal inode)。
struct ext4_system_blocks(在 ext4.h):根为 rb_root,挂到 sbi->s_system_blks(RCU 指针),读后路径 只读树 + rcu_read_lock。
5. 主要流程
5.1 ext4_setup_system_zone(挂载时)
- 分配新的
ext4_system_blocks,对每个块组:ext4_num_base_meta_blocks:超级块备份 + GDT 等 基座元数据 连续块;- 块位图、inode 位图 各 1 块;
- inode 表
s_itb_per_group块。
- 若有 journal inode 号:
ext4_protect_reserved_inode——iget后ext4_map_blocks扫出所有物理块,add_system_zone(..., journal_ino)。 - 整棵树建完后
rcu_assign_pointer(sbi->s_system_blks, system_blks),避免与ext4_inode_block_valid并发读旧指针。
注释强调:在 sb->s_umount 保护下换人指;读者侧仅用 RCU。
5.2 ext4_release_system_zone(卸载或 noblock_validity)
rcu_assign_pointer(..., NULL) 后 call_rcu 延迟释放整棵树(ext4_destroy_system_zone → release_system_zone),保证 RCU 读者退出后再 kfree。
5.3 ext4_sb_block_valid / ext4_inode_block_valid
- 先做 边界:相对
s_first_data_block、ext4_blocks_count、无溢出。 - 若
s_system_blks == NULL(未启用 validity),直接视为合法(返回 1)。 - 否则在 RB 树中查找
[start_blk, start_blk+count)是否与某系统区 相交:- 不相交 → 合法;
- 相交 → 默认 不合法(
ret = 0),除非inode非空且entry->ino == inode->i_ino。
5.4 ext4_check_blockref
遍历 __le32 * 块指针数组(最多 max 个),对每个非零块号调用 ext4_inode_block_valid。
特例:若当前 inode 即 journal inode,整段跳过(日记内部结构自行维护块引用)。
用于 indirect 块、extent 索引块 等结构中 批量校验子块号。
5.5 模块 init/exit
ext4_init_system_zone / ext4_exit_system_zone:为 ext4_system_zone 分配 kmem_cache(super.c 的 ext4_init_fs 路径注册)。
6. 挂载选项与默认行为
block_validity:默认 开启(super.c注释:默认启用,可用noblock_validity关闭)。- 关闭后
s_system_blks为空,ext4_sb_block_valid恒通过 → 略少 CPU / 锁开销,但 失去这类交叉引用检查。
7. 与其它模块的关系
1 | 挂载(super.c) |
balloc.c 的 ext4_num_base_meta_blocks 等用于 计算每组基座元数据块数,与 本文件登记的区间 一致,形成「布局算出来」与「运行期校验」的衔接。
8. RK3588 / 工程注意点
- 嵌入式上 一般保持默认
block_validity,用较小代价换 更早发现元数据交叉引用。 - 若 profiling 证明热点在极密集小块 IO,可评估
noblock_validity(需接受 失去该层防护)。 - 出现
invalid block/overlap system zone类ext4_error_inode时,多属 磁盘损坏、旧 bug、或非法离线改动,应 完整备份 + fsck,而非仅忽略。
9. 小结
| 维度 | 结论 |
|---|---|
| 作用 | 维护 系统块区 RB 树,在映射块号时校验 不得将元数据区当作普通文件块(journal inode 除外) |
| 解决问题 | 防止损坏或错误的 inode 指向元数据物理块 导致 元数据被数据写覆盖;尽早报 EFSCORRUPTED |
| 并发 | RCU 切换 s_system_blks;释放延迟到 grace period |
源码:rk3588/kernel-6.1/fs/ext4/block_validity.c。文档:linuxDoc/fs/ext4/。
正在加载留言…