RK3588 kernel-6.1 extents.c 与 extents_status.c 分析
1. 分析范围
| 文件 | 行规模(约) | 角色 |
|---|---|---|
fs/ext4/extents.c |
6000+ | 盘上 extent B+ 树的查找、插入、分裂/合并、截断拆洞、map_blocks、未写入区段转换、FIEMAP / iomap 等 |
fs/ext4/extents_status.c |
2300+ | 内存中的 extent 状态树(ES tree):延迟块、洞、已写/未写区间的按逻辑块序 RB 树,与 delalloc / bigalloc / fiemap / SEEK_DATA|HOLE 协作;shrinker 回收;pending reservation 子树 |
配套头文件与概念:ext4_extents.h(ext4_extent、ext4_extent_idx、ext4_ext_path 等)、ext4.h 中 struct extent_status / ext4_es_tree。
2. 两者关系(一句话)
extents.c 维护 inode 内持久化的 extent 索引树(决定块在磁盘上的归属与未写标记);extents_status.c 维护同一 inode 上不写入磁盘的 逻辑区间状态缓存,使延迟分配、大簇、映射查询和空洞语义不必反复扫 page cache 或全盘 extent 树。**
3. extents_status.c:作用与解决的问题
3.1 代码作用(一句话)
为每个 extent 类 inode 建一棵按逻辑起始块 es_lblk 排序的红黑树,节点表示一段连续逻辑块及其 状态(delayed / written / unwritten / hole 等)与可选物理映射;提供 插入、删除、查找、区间扫描、与预留簇计数,并在内存压力下通过 shrinker 丢弃除 delayed 外可丢弃节点。
3.2 解决的问题
| 问题 | 做法 |
|---|---|
| FIEMAP、SEEK_DATA/SEEK_HOLE 靠翻 page cache 识别延迟区 | 在 ES 树中可直接判断某逻辑范围是否为 delayed 或洞 |
| bigalloc 下是否要对簇做 quota 预留 | ext4_es_scan_clu / ext4_clu_mapped 等可查簇是否已有 delayed 或已映射,避免重复预留 |
| 写回路径为判断 delayed 扫整棵 page cache | 查 ES 树 O(log n) + cache_es 热路径近似 O(1) |
| 延迟分配与真实分配、截断、状态不同步 | ext4_es_insert_extent / ext4_es_remove_extent / ext4_es_insert_delayed_block 与 ext4_map_blocks、inode.c 等成对维护 |
| 碎片 extent 导致 ES 树过大 | ext4_es_register_shrinker:在压力下回收 written/unwritten/hole 类节点,不回收 delayed(注释:fiemap、bigalloc、seek 仍需要) |
| bigalloc + delalloc 的「待定簇预留」 | 另一棵 ext4_pending_tree(pending reservation),ext4_is_pending、__insert_pending / __revise_pending 与 ext4_es_insert_delayed_block(..., allocated) 协作,避免簇级预留重复或遗漏 |
| 从盘上读出的区间写入 ES(可选缓存) | ext4_es_cache_extent:若该范围尚无记录则插入,避免覆盖更新的内存状态 |
3.3 核心 API(概念)
ext4_es_init_tree/ext4_clear_inode_es:挂载/释放 inode 时树生命周期。ext4_es_insert_extent:覆盖式更新(先__es_remove_extent再__es_insert_extent),可合并相邻同类 extent;FC_REPLAY 时直接返回。ext4_es_lookup_extent:供ext4_map_blocks/ext4_da_map_blocks先查内存;维护 命中/未命中 统计。ext4_es_remove_extent:截断、转换、失效时撕掉区间;bigalloc 下配合init_rsvd/count_rsvd调整 reserved cluster 计数。ext4_es_insert_delayed_block:单逻辑块标 DELAYED;若allocated则挂 pending。ext4_es_delayed_clu:ext4_ext_map_blocks等在非 delalloc 分配路径上扣减「仅 delayed」簇的预留。- 并发:
EXT4_I(inode)->i_es_lock(读写锁)。
3.4 设计注释中的「Step1/Step2」
当前内核已完成「全状态可进树 + shrinker」方向;原文仍保留历史名称 delay extent tree 与向 extent 级加锁 等未来工作的叙述,阅读时以现实现为准。
4. extents.c:作用与解决的问题
4.1 代码作用(一句话)
实现 ext4 的 extent 特性下的盘上多级索引 B+ 树(根可在 inode 内或外部块):查找逻辑块对应的叶 ext4_extent、插入/分裂/提升树高、合并相邻 extent、删除与截断(ext4_ext_remove_space)、未写入 extent 与初始化 extent 的分裂与转换,并向 ext4_map_blocks 提供 ext4_ext_map_blocks;同时负责 extent 块校验和、损坏校验(ext4_ext_check_*)及 FIEMAP / collapse_range / insert_range 等高层次接口。
4.2 解决的问题
| 问题 | 做法 |
|---|---|
| 大文件用直接/间接块表浪费与低效 | 用 extent 描述 (逻辑起始, 物理起始, 长度),树高随范围数对数增长 |
| 写时分配、块边界与邻居 extent 对齐 | ext4_ext_find_goal、ext4_ext_search_left/right、ext4_mb_new_blocks 传入簇对齐后的 goal/logical |
| bigalloc 下逻辑块与簇 | get_implied_cluster_alloc、EXT4_LBLK_COFF/CMASK 等,在 ext4_ext_map_blocks 中复用已隐含簇 |
| 未写入(unwritten)extent 与落盘 | 查找命中 unwritten 时 ext4_ext_handle_unwritten_extents / ext4_ext_convert_to_initialized 等分裂、转换;direct I/O 结束 ext4_convert_unwritten_io_end_vec |
| truncate / punch hole / collapse / insert range | ext4_ext_remove_space 从叶向上删块、收缩索引;ext4_ext_truncate 更新 i_disksize 并与 remove 路径配合;文件操作层 collapse/insert |
事务额度与 i_data_sem 死锁 |
ext4_datasem_ensure_credits + ext4_ext_trunc_restart_fn:必要时放下 i_data_sem、丢弃预分配、重启事务 |
| 元数据块损坏 | ext4_extent_block_csum_*、ext4_valid_extent*、ext4_ext_check_inode |
| fast commit 重放等特殊路径 | ext4_ext_replay_update_ex、ext4_ext_clear_bb 等(与日志重放一致) |
4.3 典型调用链(映射)
ext4_ext_map_blocks:ext4_find_extent定位路径 → 若在叶ext4_extent内则返回 mapped 或走 unwritten 分支 → 若需创建且带 EXT4_GET_BLOCKS_CREATE 则ext4_mb_new_blocks后ext4_ext_insert_extent。- 插入失败时 释放刚分配的簇并
ext4_discard_preallocations。 - delalloc 成功路径可
ext4_da_update_reserve_space、ext4_es_delayed_clu与 ES 树侧预留对齐。
4.4 与 extents_status.c 的衔接(代码层面)
ext4_map_blocks(在inode.c)通常会先ext4_es_lookup_extent;盘上树路径在ext4_ext_map_blocks内仍通过ext4_find_extent访问。- 分配/转换/截断成功后,extent 代码路径会调用
ext4_es_insert_extent或ext4_es_remove_extent(具体分散在extents.c、inode.c、fast_commit.c等),保持 ES ↔ 盘树 一致。
4.5 extents.c 内部重要符号(导航)
| 类别 | 代表符号 |
|---|---|
| 路径与查找 | ext4_find_extent(本文件前部)、ext4_ext_search_left/right |
| 修改树形 | ext4_ext_insert_extent、ext4_ext_split、ext4_ext_grow_indepth、ext4_ext_try_to_merge_* |
| 删除 | ext4_ext_remove_space、ext4_remove_blocks、ext4_ext_rm_leaf |
| 映射入口 | ext4_ext_map_blocks |
| 用户可见 | ext4_fiemap、ext4_get_es_cache |
| 校验 | ext4_ext_check_inode、ext4_valid_extent |
5. 小结表
| 维度 | extents_status.c |
extents.c |
|---|---|---|
| 数据落盘 | 否(纯内存) | 是(inode 或 extent 块) |
| 主要结构 | struct extent_status RB 树 + pending 树 |
ext4_extent_header + index + leaf |
| 核心价值 | 延迟/洞/映射的快速查询与 bigalloc 预留逻辑 | 持久块映射与树形维护 |
| 典型读者 | delalloc、fiemap、seek、shrink | 分配器、截断、未写转换、fsck 相关检查 |
两篇合起来构成 ext4 “内存 extent 视图 + 盘上 extent 索引” 的完整拼图;分析 ext4_map_blocks 或 ext4_da_map_blocks 时应同时打开 inode.c 与上述两文件。
正在加载留言…