RK3588 kernel-6.1 fs/ext4/file.c 普通文件 VFS 层分析
1. 分析范围
rk3588/kernel-6.1/fs/ext4/file.c(普通文件的file_operations/ 部分inode_operations配套、读写、mmap、open/release、llseek)- 强依赖:
iomap.c(ext4_iomap_ops、ext4_iomap_overwrite_ops、ext4_iomap_report_ops)、inode.c(ext4_map_blocks)、extents.c(未写区转换等)
2. 代码作用(一句话)
file.c 把 ext4 常规文件接到 Linux VFS:在 forced shutdown、加密、verity、DAX 等前提下分流 缓冲读/写、iomap_dio_rw 直读直写** 与 DAX;用 i_rwsem 共享/独占 与 inode_dio_wait 协调 未对齐 DIO、扩展写、unwritten extent 填洞 的安全性;open 里采样挂载路径写超块、挂 journal inode;mmap 区分 DAX 大页缺页 与普通 filemap_fault / ext4_page_mkwrite。**
3. 解决了什么问题
| 问题 | file.c 中的处理方式 |
|---|---|
| 直 I/O 与特性组合(对齐、inline、加密等) | ext4_should_use_dio:用 ext4_dio_alignment 判定;不成立则清 IOCB_DIRECT 走 generic_file_read_iter,或写路径走 ext4_buffered_write_iter,保持 「不适用 DIO 时回落缓冲」 的历史语义 |
| 错对齐 DIO + unwritten extent 填洞并发 | 注释 ext4_unaligned_io:同一块上两路 dio_zero 可能互清零。做法:未对齐 DIO 必须独占 i_rwsem,且 inode_dio_wait 串行化在途 DIO |
扩展写 DIO 与 i_size / i_disksize / orphan |
扩展写前 ext4_orphan_add;ext4_dio_write_end_io 里 ext4_convert_unwritten_extents;仅在超过当前 disk/逻辑 size 时 ext4_handle_inode_extension;失败 ext4_inode_extension_cleanup + ext4_truncate_failed_write |
| 纯覆盖写可减少锁竞争 | ext4_overwrite_io 用 ext4_map_blocks(..., 0)** 判断是否已映射且非洞;覆盖路径可走 **共享 i_rwsem** + **ext4_iomap_overwrite_ops` |
| DIO 只完成部分长度 | 末尾 ext4_buffered_write_iter 吃剩余 iov_iter,再 filemap_write_and_wait_range + invalidate_mapping_pages,尽量保持 「要么全 DIO 要么语义一致」 |
| 非 extent(bitmap)布局的最大文件 | ext4_generic_write_checks:EXT4_INODE_EXTENTS 未置时用 s_bitmap_maxbytes 截断 iov_iter |
| 不可变文件 | IS_IMMUTABLE → -EPERM |
| 关闭文件时把延迟分配落盘 / 释放预分配 | ext4_release_file:EXT4_STATE_DA_ALLOC_CLOSE 时 ext4_alloc_da_blocks;最后一个写者且无 i_reserved_data_blocks 时 ext4_discard_preallocations |
| 管理员排查「盘从哪挂载过」 | ext4_sample_last_mounted:首次写挂载采样 d_path 写入 s_last_mounted(带日志与校验和) |
| journal ordered 模式与写 fd | ext4_file_open 写打开时 ext4_inode_attach_jinode |
SEEK_HOLE / SEEK_DATA |
ext4_llseek:iomap_seek_hole / iomap_seek_data + ext4_iomap_report_ops |
maxbytes 随布局变化 |
ext4_llseek:无 EXTENTS 标志用 s_bitmap_maxbytes,否则 s_maxbytes |
| DAX 共享映射写 vs COW | ext4_dax_huge_fault:仅 VM_SHARED 真写 才 ext4_journal_start;避免只读挂载上无意义 journal 失败 |
4. 核心组件
4.1 ext4_file_read_iter
ext4_forced_shutdown→ -EIO- DAX:
ext4_dax_read_iter→dax_iomap_rw IOCB_DIRECT:ext4_dio_read_iter→iomap_dio_rw(..., ext4_iomap_ops);否则generic_file_read_iter
4.2 ext4_buffered_write_iter
IOCB_NOWAIT→ -EOPNOTSUPP(缓冲路径)inode_lock+ext4_write_checks(内含ext4_generic_write_checks与file_modified)generic_perform_write+generic_write_sync
4.3 ext4_dio_write_iter
- 初判 未对齐 或 可能扩展 → 独占锁;否则先 共享锁
ext4_dio_write_checks:扩展写extend=true;若 共享锁下需要改安全配置、扩展、或非覆盖写 → 升级为 独占 并重算- 扩展写:
ext4_orphan_add(短事务) iomap_dio_rw:覆盖用ext4_iomap_overwrite_ops;未对齐或扩展 带IOMAP_DIO_FORCE_WAIText4_dio_write_ops.end_io:unwritten →ext4_convert_unwritten_extents;按需ext4_handle_inode_extension
4.4 ext4_file_write_iter
三分流:forced shutdown → DAX → DIRECT → 缓冲。
4.5 DAX 写路径(CONFIG_FS_DAX)
ext4_dax_write_iter:ext4_write_checks → orphan(若越 i_disksize)→ dax_iomap_rw → ext4_handle_inode_extension / cleanup。
4.6 ext4_file_mmap
daxdev_mapping_supported:不支持MAP_SYNC等则 -EOPNOTSUPP- DAX:
ext4_dax_vm_ops(VM_HUGEPAGE);否则ext4_file_vm_ops(filemap_fault/ext4_page_mkwrite)
4.7 ext4_file_open / ext4_release_file
- open:
ext4_sample_last_mounted、fscrypt_file_open、fsverity_file_open、写打开 attach jinode、FMODE_NOWAIT | FMODE_BUF_RASYNC、dquot_file_open - release:DA 块刷盘、预分配丢弃;若存在
is_dx且private_data则ext4_htree_free_dir_info(多数普通文件不命中;与 htree 私有数据生命周期相关的兜底)
4.8 ext4_file_operations / ext4_file_inode_operations
1 | const struct file_operations ext4_file_operations = { |
5. 小结
file.c 不负责盘上 extent/块分配细节(在 inode.c / extents.c / iomap.c),而是 VFS 入口策略:何时 DIO/DAX/缓冲、如何 加锁与 orphan 以保证 ext4 直写 + unwritten + 延迟块 组合下的正确性,并挂载 加密、verity、OSyncError、配额、seek 洞/数据。阅读 write(2)/read(2)/mmap 在 ext4 上的行为时,应与本文件及 ext4_iomap_* 实现对照。