RK3588 kernel-6.1 fs/ext4/ialloc.c inode 分配与回收分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/ialloc.c(inode 位图读写校验、新建 inode、释放 inode、Orlov/目录投递策略、orphan 校验、统计与 lazyinit inode 表清零)- 协作:
namei.c/inode.c(调用__ext4_new_inode)、ext4_jbd2(位图与组描述符日记)、xattr.c/acl.c/fscrypt(新 inode 初始化)、mballoc/flex(组内空闲簇统计与s_flex_groups)
2. 代码作用(一句话)
ialloc.c 用每个块组一张 inode 位图 + 组描述符中的空闲 inode/目录计数 管理 inode 号的分配与回收;新建时按 目录 Orlov、普通文件近父组+二次探测 选 block group,在位图中 find_next_zero_bit 并 事务内置位,顺带处理 BG_INODE_UNINIT/BG_BLOCK_UNINIT、flex_bg 计数与 配额/ACL/extent 树 初始化;释放时先 ext4_clear_inode 再清位图,避免 inode 号快速复用导致别名。
3. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 目录集中导致单块组 inode 或目录密度过大 | find_group_orlov:顶级/标记 TOPDIR 的目录可按 目录名 hash 或随机 分散到 flex_bg;一般子目录在 空闲 inode/簇不低于均值、目录数不过阈 的组中挑选,失败再 fallback 到「空闲 inode ≥ 组均值」的线性搜索 |
| 同一目录下普通文件尽量同组、又避免与邻目录过度混放 | find_group_other:优先 父组(flex 内扫描);不行则用 (parent_group + i_ino) % ngroups 为种子做 二次增量探测 + 最后线性扫 |
块组 inode 位图未初始化(INODE_UNINIT) |
ext4_read_inode_bitmap:在 gdt csum 下可将位图 内存填 0 并 ext4_mark_bitmap_end 标记已用尾部,避免读盘;新建 inode 时可能触发 block bitmap 首次初始化(BLOCK_UNINIT) |
| inode 位图损坏或未授权重放 | ext4_validate_inode_bitmap:CRC 校验、EXT4_SIM_*、EXT4_MB_GRP_IBITMAP_CORRUPT 时 拒绝使用 |
| 释放 inode 后立即复用同一编号与旧 dentry/缓存打架 | ext4_free_inode:注释强调须先 ext4_clear_inode 再 清位图;无 journal 时 find_inode_bit + recently_deleted 尽量避免 刚删未落盘的 inode 槽 |
| 组内最后一位被并发抢走 | __ext4_new_inode:ext4_test_and_set_bit 失败则 持锁重搜 或换 next_group 循环 |
goal/保留 inode 范围 |
goal 可强制 起始组与位偏移;EXT4_FIRST_INO 以下视为保留,若被清出则 报错并标位图损坏 |
| orphan 链表恢复 | ext4_orphan_get:位图须仍置位 表示 inode 仍被占用;检查 ext4_can_truncate/NEXT_ORPHAN 等,防止 坏 orphan 死循环 |
| lazyinit 未完成时 inode 表含垃圾 | ext4_init_inode_table:alloc_sem 写锁 阻塞同组 ext4_new_inode,对 未 INODE_ZEROED 的表 sb_issue_zeroout 并置 BG_INODE_ZEROED |
| ext4 与内核/调试统计 | ext4_count_free_inodes / ext4_count_dirs;DEBUG 下可 逐组对账位图与 gdp 计数 |
4. 核心流程
4.1 inode 位图读入:ext4_read_inode_bitmap
- 从 gdp 取 inode bitmap 块号,范围自检。
INODE_UNINIT:零填充 + 尾部 mark used,标 uptodate/verified,不读盘。- 否则 读盘 →
ext4_validate_inode_bitmap(ext4_inode_bitmap_csum_verify)。
4.2 新建:__ext4_new_inode
new_inode、owner/fscrypt_prepare_new_inode、dquot_initialize,按需累加 xattr 事务额度。- 选组:
goal→ 否则目录find_group_orlov,文件find_group_other;更新dir->i_last_alloc_group。 - 对 每个候选组:gdp 空闲计数为 0 或 ibitmap corrupt 则跳过;读 inode bitmap;
find_inode_bit找 0 bit。 - journal
get_write_access位图 → 置位(冲突则重找)→ dirty 位图。 - 若 BLOCK_UNINIT:读并 dirty block bitmap,清
EXT4_BG_BLOCK_UNINIT,初始化 free_clusters。 - 更新 gdp:
free_inodes--、目录则used_dirs++,维护bg_itable_unused/INODE_UNINIT,inode bitmap / gdp csum。 - 维护
percpu_counter与 flexfree_inodes/used_dirs。 - 填写
ext4_inode_info:i_block_group、i_csum_seed、EXTENT标志 +ext4_ext_tree_init(目录/ reg / symlink)、i_sync_tid等,insert_inode_locked→ ACL/security/encrypt xattr →ext4_mark_inode_dirty。
4.3 释放:ext4_free_inode
前置条件检查(i_count/i_nlink)→ dquot_free_inode → ext4_clear_inode → 算 group/bit → 读位图、journal 访问 → 清 bit(若已清则 报错 corrupt)→ 更新 gdp 空闲 inode、目录计数、flex、percpu → 重算 inode bitmap csum / gdp csum → dirty。
4.4 辅助出口
ext4_mark_inode_used:重放或工具路径 强制在位图中置位 并更新计数(与__ext4_new_inode不同,不创建 VFS inode)。ext4_orphan_get:挂载 orphan 处理。ext4_init_inode_table:lazyinit 后台 inode 表零填充。
5. 小结
ialloc.c 是 ext4 inode 编号空间 的核心:位图一致性(校验和 + corrupt 标记)、目录/文件的局部性与负载均衡(Orlov + flex)、与 lazyinit、日记、配额、extent 初始化 的衔接都在此收敛。分析 creat/mkdir/unlink 时,分配侧看 __ext4_new_inode 与 find_group_*,回收侧看 ext4_free_inode 与 ext4_evict_inode(在 inode.c)的调用关系。
正在加载留言…