RK3588 kernel-6.1 fs/ext4/orphan.c 孤儿 inode 处理分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/orphan.c- 磁盘与内存概念:超级块
s_last_orphan(单链表头)、可选orphan_filefeature(专用s_orphan_file_inum文件内多块槽位)、**内存链表s_orphan+s_orphan_lock - 协作:
ext4_jbd2(事务、EXT4_JTR_ORPHAN_FILE)、super.c(挂载/卸载、ext4_init_orphan_info/ext4_release_orphan_info)、inode.c(ext4_orphan_get、truncate、evict路径上 add/del)
2. 代码作用(一句话)
**orphan.c 维护「崩溃后仍须收尾的 inode」:**对已 unlink 但仍被打开、或 大截断跨事务 的 inode,在 有 journal 时把它们 登记到盘上(老式 s_last_orphan + 盘上 i 链表 或 新式 orphan 文件槽位),以便 ext4_orphan_cleanup 在 下次 rw 挂载 时 删无链接 inode 或 按记录尺寸截断仍有链接 inode;正常关闭/截断完成时 ext4_orphan_del 从盘上与内存链表移除。
3. 两套盘上表示
| 机制 | 盘上结构 | 内存标记 |
|---|---|---|
| 经典孤儿链表 | es->s_last_orphan 指向链表头;每个 inode 盘上 i_links_count 邻域的 next 指针(NEXT_ORPHAN)链成单链表 |
list_head i_orphan 挂在 sbi->s_orphan |
| orphan 文件(feature) | s_orphan_file_inum 指向普通 inode,其数据块为 __le32 ino 槽数组 + 块尾 ext4_orphan_block_tail(magic、checksum) |
EXT4_STATE_ORPHAN_FILE + i_orphan_idx(线性下标) |
ext4_orphan_add:若 s_orphan_info.of_blocks != 0 先 ext4_orphan_file_add;仅当返回 -ENOSPC 时 回退 超级块链表路径(不扩 orphan 文件,避免每件 orphan 操作预留过多 credits)。
4. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 崩溃时文件已 unlink 仍被 FD 占用,须最终释放数据与 inode | 进 orphan 表;恢复时 i_nlink==0 → ext4_process_orphan 走 iput 触发真正删除 |
| 截断很大,跨多个已提交事务,中途崩溃导致元数据与数据不一致 | 截断过程中把 inode 挂 orphan;恢复时 i_nlink!=0 → truncate_inode_pages + ext4_truncate 收到 i_size/i_disksize 已记录的安全目标 |
| 超级块只存一个链表头,高并发 unlink 争用同一块磁盘中超级块/头 inode | orphan 文件:多块、按 CPU 简单散列选起始块 atomic_dec_if_positive(ob_free_entries) 找空槽,cmpxchg 占槽,减少 所有 orphan 都改 s_last_orphan 的热点(槽满仍回退链表) |
| orphan 文件块损坏难检测 | 块尾 magic;metadata_csum 时 ext4_orphan_file_block_csum_verify;jbd2 ext4_orphan_file_block_trigger 写时更新 tail checksum |
| 内存与盘上链表一致性 | ext4_orphan_add:改 sb 或前驱 inode 的 NEXT_ORPHAN + ext4_handle_dirty_metadata;失败则从 内存 i_orphan 摘除(注释:避免卸载 panic) |
ext4_orphan_del 无 handle(错误路径) |
仍 list_del_init 更新 内存链表;跳过 盘上更新 |
| 只读挂载但需要清孤儿 | ext4_orphan_cleanup 可 临时清 SB_RDONLY;CONFIG_QUOTA 下必要时 开 quota 再 off |
| 文件系统带错/只读设备 | 只读 bdev 或 未知 rocompat 则 跳过;EXT4_ERROR_FS 时策略性 清 s_last_orphan 或 跳过恢复(见代码分支) |
| 卸载释放 orphan 文件缓冲 | ext4_release_orphan_info:brelse 各 ob_bh、kfree(of_binfo) |
5. 主要函数摘要
5.1 ext4_orphan_add / ext4_orphan_del
- 前提:
s_journal存在 且 inode 正常;无 journal 直接 0(无孤儿恢复契约)。 - 加锁约定:注释 须在
i_rwsem下调用(I_NEW/I_FREEING例外)。 - del:若
EXT4_STATE_ORPHAN_FILE走ext4_orphan_file_del(清槽、atomic_inc(ob_free_entries));否则链表路径上 可能是 sb 头或前驱 inode 改写 next。
5.2 ext4_process_orphan
dquot_initializei_nlink > 0:加锁 → 截断页缓存 →ext4_truncate;失败ext4_orphan_del(NULL, ...)清内存链防泄漏i_nlink == 0:计数为 deleteiput(inode):触发 实际 evict/释放
5.3 ext4_orphan_cleanup
- 若
s_last_orphan与 orphan 文件块数 皆为 0 则返回 - 按
es->s_last_orphan循环ext4_orphan_get→ 挂s_orphan→ext4_process_orphan - 再 双重循环 扫 orphan 文件 各块非零 ino(设置
EXT4_STATE_ORPHAN_FILE与i_orphan_idx) - 打 日志:删除/截断数量
- 恢复
sb->s_flags(只读 原样)
5.4 ext4_init_orphan_info
- feature
orphan_file时ext4_iget(s_orphan_file_inum),按i_size得块数 - 每块
ext4_bread,校验 tail magic 与 checksum - 统计每块 空槽数 →
atomic_set(ob_free_entries) of_csum_seed来自 orphan inode 的i_csum_seed
5.5 ext4_orphan_file_empty
- 若 feature 未开 返回 1(空)
- 否则所有块的
ob_free_entries均等于 每块槽位数 才 空(用于 fast commit 等判断,见其它文件调用点)
6. 小结
orphan.c 解决的是 日志文件系统下「逻辑上的删除/截断」与「最终盘上状态」在崩溃边界上的缺口:用 持久化的孤儿集合 记住 哪些 inode 仍需删或收尾截断,并在 挂载恢复 时 幂等地 处理。orphan 文件 是对 经典超级块头链表 的 可扩展、可校验 扩展;空间满 时 透明回退 旧机制。
ext4_orphan_get 实现在其它源文件(通常为 inode.c),orphan_cleanup 依赖其 安全 iget(坏/已删 inode 可中止链表)。
正在加载留言…