RK3588 kernel-6.1 fs/ext4/inline.c 内联数据与内联目录分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/inline.c(small file / symlink / directory 在 inode 体内 存放数据或目录项;与 xattr.c、inode.c、dir.c、iomap.c 协作)- 盘上表示:
EXT4_INODE_INLINE_DATA;内容分为ext4_inode->i_block前一段固定长度 + 可选 system xattr"data"的值域(见EXT4_XATTR_SYSTEM_DATA)
2. 代码作用(一句话)
inline.c 在 i_extra_isize 与 inode 内 xattr 空间 允许时,把 小文件数据、快符号链接文本或整块小目录 塞进 inode 块;通过 ext4_find_inline_data_nolock 在 iget 时定位 "data" xattr 与 i_inline_off/i_inline_size;读路径 readpage/iomap 把 inline 映射为 IOMAP_INLINE 或填充第 0 页;写/增长若超出 ext4_get_max_inline_size 则 ext4_convert_inline_data_to_extent 等形式 转 extent + 普通块;目录在 i_block 里编码 .. 与线性 ext4_dir_entry_2,满则 ext4_convert_inline_data_nolock 落盘为传统目录块。
3. 解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 极小文件仍占一整数据块,浪费空间与元数据 I/O | inline data:数据在 i_block[] + xattr 扩展区;ext4_try_to_write_inline_data 优先扩 xattr,失败再 转换 |
| 符号链接目标短却占块 | ext4_read_inline_link 等从 inline 区域读 快链 内容 |
| 空/小目录仅含少量项却要数据块 | inline dir:i_block 头存 .. 的 ino,后继 ext4_dir_entry_2;ext4_try_add_inline_entry 先塞 inode 内槽,再尝试 xattr 扩容,再 ext4_convert_inline_data_nolock |
| 与 extent/块映射路径统一 | 超过容量或 ext4_prepare_inline_data 返回 -ENOSPC → ext4_convert_inline_data_to_extent:ext4_destroy_inline_data_nolock → __block_write_begin + ext4_get_block 写道 → 置 extent;失败走 orphan + ext4_truncate_failed_write |
| 延迟分配与 inline 交织 | ext4_da_write_inline_data_begin / ext4_da_convert_inline_data_to_extent;ext4_convert_inline_data 在 MAY_INLINE 已清 时 filemap_flush 收尾中途转换 |
readdir/htree 与 inline 目录 |
ext4_read_inline_dir、ext4_inlinedir_to_tree:合成 ./.. 假项,校验 ext4_check_dir_entry,向 fname 树 填哈希项(与 dir.c 一致) |
| iomap/DIO 需知道数据在 inode 里 | ext4_inline_data_iomap:IOMAP_INLINE,addr 指向 盘上 inode 缓冲区内 i_block 偏移 |
| 截断缩短 inline 文件 | ext4_inline_data_truncate:同步缩 xattr 值 与 i_block 尾部;必要时 ext4_es_remove_extent 清 stale ES |
| xattr 与 inline 空间竞争 | get_max_inline_xattr_value_size:在 ibody xattr 布局下算 剩余可用值大小;ext4_get_max_inline_size = 该值 + EXT4_MIN_INLINE_DATA_SIZE |
| 外部 xattr inode 不允许挂 inline | ext4_find_inline_data_nolock:e_value_inum 非 0 → -EFSCORRUPTED |
| 加密目录名等 | inline 路径仍走 ext4_filename / fscrypt 与上层 namei(本文件侧重 存储格式) |
4. 存储布局(概念)
| 区域 | 含义 |
|---|---|
raw_inode->i_block 前 EXT4_MIN_INLINE_DATA_SIZE |
文件/链接:内联数据前段;目录:前 EXT4_INLINE_DOTDOT_* 为 .. parent ino,后方为目录项区起始 |
system xattr "data" |
额外内联字节;i_inline_off 指向 xattr 项在 inode 映像中的偏移;i_inline_size = 固定头 + xattr 值长 |
ext4_has_inline_data:EXT4_INODE_INLINE_DATA 等标志(定义见 ext4.h)。
5. 核心入口(导航)
| 符号 | 用途 |
|---|---|
ext4_get_max_inline_size / ext4_find_inline_data_nolock |
容量与 iget 时发现 inline |
ext4_create_inline_data / ext4_update_inline_data / ext4_prepare_inline_data |
新建或扩容 inline |
ext4_destroy_inline_data* |
去掉 "data" xattr 与标志,恢复 extent 可建 |
ext4_readpage_inline / ext4_read_inline_data |
读页/内存拷贝 |
ext4_try_to_write_inline_data / ext4_write_inline_data_end |
缓冲写路径 |
ext4_convert_inline_data_to_extent |
同步转块 |
ext4_try_add_inline_entry / ext4_delete_inline_entry / empty_inline_dir |
目录项增删与空目录判断 |
ext4_try_create_inline_dir |
创建时尝试 inline 目录 |
ext4_read_inline_dir / ext4_inlinedir_to_tree |
遍历 / htree 装树 |
ext4_inline_data_iomap / ext4_inline_data_truncate / ext4_convert_inline_data |
iomap、截断、强制转换 |
6. 锁与兼容性
xattr_sem:读 保护 inline 元数据与 xattr 布局;ext4_write_lock_xattr在修改 xattr/inline 时用。i_data_sem:截断 等与 extent 共享逻辑时使用。EXT4_STATE_MAY_INLINE_DATA:尚允许继续 inline;ENOSPC或 DIO 路径(见file.c)可 清除,迫使 走块设备。
feature:需 inline_data 等与 足够大的 s_inode_size/extra isize;bigalloc 与旧式 indirect 组合由其它文件约束,inline 转换后统一走 extent 映射。
7. 小结
inline.c 把 「元数据即数据」 做到极致,显著减少 小文件/小目录/短 symlink 的 块分配与碎片;代价是实现 与 xattr、日记、page cache、extent、目录哈希、加密 的多路径桥接。分析某 inode 是否 inline,看 EXT4_INODE_INLINE_DATA 与 ext4_has_inline_data;分析读写失败是否 触发转换,搜索 ext4_convert_inline 调用链。
正在加载留言…