RK3588 kernel-6.1 fs/ext4/indirect.c 传统间接块映射分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/indirect.c(未启用 extent 时 inode 内i_data[]的 直接块 + 一/二/三级间接 树:查找、分配、截断、打洞)- 调用方:
inode.c中ext4_map_blocks(无EXT4_INODE_EXTENTS时走ext4_ind_map_blocks)、截断与 punch hole 路径 - 不覆盖:extent 树(见
extents.c)
2. 代码作用(一句话)
indirect.c 实现 classic ext2 式 「inode 根部指针 + 多级间接块」:把逻辑块号 ext4_block_to_path 解析为各级索引,ext4_get_branch** 读链;若缺块且允许创建则 ext4_alloc_branch 分配元数据与数据块并 ext4_splice_branch 挂回树;ext4_ind_truncate / ext4_ind_remove_space 通过 ext4_find_shared + ext4_free_branches / ext4_free_data 回收子树并配合日记。** 全程依赖 i_data_sem 与 ext4_jbd2。**
3. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 无 extent 时如何把逻辑块映射到物理块 | ext4_ind_map_blocks:路径深度 + ext4_get_branch;命中则尽量 向前合并连续指针(first_block + count)扩大 m_len |
| 逻辑块号超出三间接可表示范围 | ext4_block_to_path:越界 ext4_warning 并 depth=0 |
| 间接块中指针越界或损坏 | ext4_get_branch:key > ext4_blocks_count → -EFSCORRUPTED;读盘后 ext4_check_indirect_blockref |
| 分配新链时崩溃中间态 | 注释:先分配整条链再 splice,失败则 ext4_alloc_branch 内 ext4_free_blocks 释放,不从未完成链可达,减少恢复复杂度 |
| 块分配局部性 | ext4_find_near:向左找最近已分配块 → 否则靠近 间接块本体 → 否则 ext4_inode_to_goal_block(同组 + PID 着色减少并发冲突);ext4_find_goal 截断到 32 位物理块上限 |
| 单次创建需要多少数据块(跨 indirect 边界) | ext4_blks_to_allocate 与 blocks_to_boundary(来自 ext4_block_to_path) |
| bigalloc 与非 extent 不兼容 | ext4_ind_map_blocks 创建路径:ext4_has_feature_bigalloc → -EFSCORRUPTED(须 extent) |
| 截断到更短 i_size | ext4_ind_truncate:ext4_es_remove_extent 同步 ES 树;i_disksize = i_size;ext4_find_shared 找与 最后一个保留块 共享的路径,只释放 尾部;再按 offsets[0] 逐级 ext4_free_branches 拆掉 整棵一级/二级/三级 子树 |
| punch hole(中间挖洞) | ext4_ind_remove_space:对 [start,end) 分 同层深度 / 跨层 情况,组合 ext4_find_shared 与 ext4_free_branches,与 ext4_punch_hole 衔接 |
| 日记事务额度估算 | ext4_ind_trans_blocks:~N/每块指针数 + 4(上界经验公式) |
事务重启与 i_data_sem 死锁 |
ext4_ind_trunc_restart_fn / ext4_ind_truncate_ensure_credits:与 extents.c 中 ext4_ext_trunc_restart_fn 同类——放下 i_data_sem、ext4_discard_preallocations |
4. 核心数据结构:Indirect
1 | typedef struct { |
p:当前层 指向下一层块号的槽位(inode 内或某间接块 buffer)。key:该槽位上的 块号(le32)。bh:本层若为间接块则非 NULL。
ext4_get_branch 从 inode->i_data 起沿 offsets[] 填充 chain[],遇 0 指针 返回 残缺链 供分配。
5. 分配路径概要
ext4_block_to_path→depth、offsets、blocks_to_boundary。ext4_get_branch→ 若完整:映射成功(可扫连续块)。- 若 无 CREATE:按 子树空洞大小 填
m_len(hole)。 - 若 CREATE:
indirect_blks= 从断点到叶子还差几层元数据;ext4_alloc_branch:ext4_new_meta_blocks建中间层、ext4_mb_new_blocks建数据;ext4_splice_branch写回父指针、更新i_blocks等。 delalloc:EXT4_GET_BLOCKS_DELALLOC_RESERVE→ext4_da_update_reserve_space。
6. 锁定约定(与注释一致)
- 仅查找:
down_read(i_data_sem)。 - 可能分配:
down_write(i_data_sem)(由inode.c在调用ext4_ind_map_blocks前保证)。
7. 小结
indirect.c 是 ext4 对 传统 bitmap+i_block[] 布局 的完整实现;现代默认 extent 文件 主要走 extents.c,本文件仍用于 未转换的老布局、特殊场景或 tests。阅读 ext4_map_blocks 时:EXT4_INODE_EXTENTS 置位 → extent 路径;否则 → ext4_ind_map_blocks。与 s_bitmap_maxbytes(file.c/truncate 与本文 max_block)共同约束 非 extent 文件最大逻辑长度。
正在加载留言…