RK3588 kernel-6.1 fs/ext4/block_validity.c 元数据块保护区分析

RK3588 kernel-6.1 fs/ext4/block_validity.c 元数据块保护区分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/block_validity.c
  • ext4.hstruct ext4_system_blockss_system_blks
  • 挂载选项:block_validity / noblock_validitysuper.c
  • 调用方:inode.c__check_block_validity,以及 ext4_check_blockref

2. 代码作用(一句话)

block_validity.c 在挂载时建立一棵 红黑树,记录全文件系统中所有 仅允许作元数据使用 的物理块区间(超级块备份、GDT、位图、inode 表、以及日记 inode 所映射的数据块等);之后当 inode 的块映射被解释时,若某物理块落在这些区间内且不属于「被允许的 inode」(如 journal inode),则判定为 无效引用,报 损坏 并阻止继续使用错误映射。

文件头注释:

1
2
* Track which blocks in the filesystem are metadata blocks that
* should never be used as data blocks by files or directories.

3. 解决了什么问题

3.1 元数据被误当「文件数据块」

磁盘或软件缺陷可能导致:

  • extent / 间接块中写入的 物理块号 落在 块组位图、inode 表、超级块备份 等区域;
  • 若不做检查,缓冲写或 DIO 可能 覆写元数据,造成 整卷损坏,且比单文件损坏更难恢复。

block validityext4_map_blocks 等路径更新映射前(见 inode.c),对即将采用的 m_pblk 区间 调用检查,重叠系统区则 拒绝-EFSCORRUPTED / ext4_error_inode),把问题 限制在发现点

3.2 日记 inode 的合法占用

Journal 以普通 inode 形式存放,其 文件块 在物理上仍是数据区,但 从「普通文件」视角 这些块应视为 由该保留 inode 专用。因此树节点带 ino

1
2
3
4
5
6
else {
ret = 0;
if (inode)
ret = (entry->ino == inode->i_ino);
break;
}
  • 重叠系统区且 entry->ino == 0:一律 无效(普通文件不得占用)。
  • 重叠且 entry->ino == inode->i_ino允许(例如正在访问的是 journal inode 自身映射)。

ext4_protect_reserved_inodejournal inodeext4_map_blocks 结果逐段 add_system_zone(..., ino),与固定元数据区一起进入同一棵树。

3.3 重叠检测与布局自洽

add_system_zone 在插入 RB 树时若发现新区间与已有区间 几何重叠(非简单相邻合并),返回 -EFSCORRUPTED。这能发现 系统区描述自相矛盾错误重复登记 等异常(例如 ext4_protect_reserved_inode 中 journal 块与已有区重叠会打错误日志)。


4. 核心数据结构

1
2
3
4
5
6
struct ext4_system_zone {
struct rb_node node;
ext4_fsblk_t start_blk;
unsigned int count;
u32 ino;
};
  • 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_blksRCU 指针),读后路径 只读树 + rcu_read_lock


5. 主要流程

5.1 ext4_setup_system_zone(挂载时)

  1. 分配新的 ext4_system_blocks,对每个块组:
    • ext4_num_base_meta_blocks:超级块备份 + GDT 等 基座元数据 连续块;
    • 块位图、inode 位图 各 1 块;
    • inode 表 s_itb_per_group 块。
  2. 若有 journal inode 号ext4_protect_reserved_inode —— igetext4_map_blocks 扫出所有物理块,add_system_zone(..., journal_ino)
  3. 整棵树建完后 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_zonerelease_system_zone),保证 RCU 读者退出后再 kfree

5.3 ext4_sb_block_valid / ext4_inode_block_valid

  • 先做 边界:相对 s_first_data_blockext4_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_cachesuper.cext4_init_fs 路径注册)。


6. 挂载选项与默认行为

  • block_validity:默认 开启super.c 注释:默认启用,可用 noblock_validity 关闭)。
  • 关闭后 s_system_blks 为空ext4_sb_block_valid 恒通过略少 CPU / 锁开销,但 失去这类交叉引用检查

7. 与其它模块的关系

1
2
3
4
5
6
7
8
9
10
11
12
挂载(super.c)
|
v
ext4_setup_system_zone(block_validity.c)
|
v
s_system_blks RCU 指针

ext4_map_blocks / 写路径(inode.c)
|
v
check_block_validity → ext4_inode_block_valid

balloc.cext4_num_base_meta_blocks 等用于 计算每组基座元数据块数,与 本文件登记的区间 一致,形成「布局算出来」与「运行期校验」的衔接。


8. RK3588 / 工程注意点

  • 嵌入式上 一般保持默认 block_validity,用较小代价换 更早发现元数据交叉引用
  • 若 profiling 证明热点在极密集小块 IO,可评估 noblock_validity(需接受 失去该层防护)。
  • 出现 invalid block / overlap system zoneext4_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/

文章互动

阅读 --

留言

0 条留言

正在加载留言…