RK3588 kernel-6.1 fs/ext4/fsmap.c 与 fs/ext4/fsync.c 分析
1. 分析范围
| 文件 | 作用定位 |
|---|---|
fs/ext4/fsmap.c |
FS_IOC_GETFSMAP(getfsmap)在 ext4 上的实现:把盘上 物理块区间 归类为 所有者(空闲 / 固定元数据 / 日志 /「未知」缝隙等) 并返回给用户态 |
fs/ext4/fsync.c |
fsync / fdatasync / msync 触发的 ext4_sync_file:先刷 用户数据页,再按 是否启用 journal、data 模式 提交元数据(含 fast commit 路径),必要时 blkdev_issue_flush |
通用 VFS 的
linux/fs/fsync.c(如do_fsync)不在本分析范围内;用户所问的fsync.c指 ext4 子目录下的实现。
2. fsmap.c:代码作用(一句话)
在 块组 buddy / 空闲查询(ext4_mballoc_query_range)与 预先枚举的固定元数据块列表(超级块、GDT、位图、inode 表等)之间做 物理地址序上的归并,通过 ext4_getfsmap_helper 输出连续的 fsmap/ext4_fsmap 记录(含 洞状「未知所有者」 区间);支持 数据设备 + 外置 journal 设备 两段 handler,并对 大簇(bigalloc) 以簇为单位查询后再换算成块区间。
3. fsmap.c:解决了什么问题
| 问题 | 处理方式 |
|---|---|
用户态需要「整块设备上谁占了哪一段」的物理视图(调试、迁移、审计、xfs_io fsmap 等) |
实现 ext4_getfsmap,对接 struct fsmap_head 与 formatter 回调,填 fmh_entries / fmh_oflags |
| 空闲区来自 mballoc,元数据位置来自组描述符 | 固定元数据先 ext4_getfsmap_find_fixed_metadata 聚合成 有序链表;每个块组对 空闲簇 调 ext4_mballoc_query_range → ext4_getfsmap_datadev_helper,与 gfi_next_fsblk 同步推进,中间 空隙 用 EXT4_FMR_OWN_UNKNOWN 报告 |
| 块组边界处空闲 extent 断裂 | gfi_lastfree:块组末尾的 free extent 暂存,与下一组 邻接则合并,否则先 ext4_getfsmap_helper 吐出 |
| 外置 journal 占用独立设备 | ext4_getfsmap_logdev:在 设备号匹配 且 查询起点为物理 0 时,构造一条 EXT4_FMR_OWN_LOG、j_blk_offset / j_total_len 的合成记录 |
| 用户态用字节、内核 ext4 用文件系统块 | ext4_fsmap_from_internal / ext4_fsmap_to_internal 按 s_blocksize_bits 换算 |
| 跨调用分页遍历 | fmh_keys 的 低 key 可带非零 fmr_length:ext4_getfsmap 将其 折到 dkeys[0].fmr_physical += length,避免重复返回同一段 |
| 仅统计映射条数而不拷贝缓冲区 | fmh_count == 0 时 ext4_getfsmap_helper 只增 fmh_entries,用于 计数模式 |
所有者常量(节选,见 fs/ext4/fsmap.h):EXT4_FMR_OWN_FREE、UNKNOWN、FS(超级块等静态元数据)、GDT / RESV_GDT / BLKBM / INOBM / INODES、LOG。
4. fsmap.c:核心流程摘要
ext4_getfsmap:校验fmh_iflags、设备号(s_bdev或s_journal_bdev);组装handlers[]并 排序,逐设备调gfd_fn。ext4_getfsmap_datadev:把 keys 裁到[s_first_data_block, blocks_count);对每个相关 块组 设置gfi_low/gfi_high(簇边界);ext4_mballoc_query_range拉 空闲;最后用gfi_last=true再跑一次 helper 收口尾部缝隙。ext4_getfsmap_helper:跳过 低于起始 key 的记录;rec_fsblk > gfi_next_fsblk时先 填 UNKNOWN 间隙;再输出 当前记录;维护gfi_next_fsblk;支持 信号中断返回-EINTR。
5. fsync.c:代码作用(一句话)
ext4_sync_file 在 只读/FS_ABORTED/强制关机 等早退之后,先 file_write_and_wait_range 刷数据页;无日志走 sync_mapping_buffers + sync_inode_metadata + 新目录项链上的父目录同步;有日志且非 data=journal 走 ext4_fsync_journal → ext4_fc_commit(按 datasync 选用 i_datasync_tid 或 i_sync_tid);data=journal 则 ext4_force_commit;最后按 barrier / journal 情况 可能 blkdev_issue_flush,并 检查 writeback 错误序号。
6. fsync.c:解决了什么问题
| 问题 | 处理方式 |
|---|---|
| 无日志时新建文件只靠回写,崩溃窗口大 | ext4_sync_parent:沿 EXT4_STATE_NEWENTRY dentry 链 向上 sync_mapping_buffers + sync_inode_metadata,把 新建路径上的目录 尽快落盘 |
| 有 journal 时 fsync 与事务 TID 对齐 | ext4_fsync_journal:提交 包含该 inode 已记录更改的事务(通过 i_sync_tid / i_datasync_tid),并纳入 fast commit(ext4_fc_commit) |
| data=journal 时数据在 journal 里 | 依赖 ext4_force_commit 将 文件数据随事务 处理(注释说明 filemap_fdatawait 可能遇到 commit 弄脏的页,但块已在日志中) |
| 设备乱序写需要屏障 | needs_barrier:无 journal 且 BARRIER 挂载 或 journal 带 JBD2_BARRIER 且当前 commit 未带 data barrier 时 blkdev_issue_flush |
| 静默写失败 | file_check_and_advance_wb_err |
只读挂载与 EXT4_MF_FS_ABORTED |
sb_rdonly + mount flag 返回 -EROFS |
7. fsync.c:函数关系简图
1 | ext4_sync_file |
8. 小结
fsmap.c面向 「物理块 → 类别/所有者」 的 整卷查询,与 mballoc、组描述符、外置日志 紧耦合,是 Oracle getfsmap / xfs_io 生态在 ext4 上的后端。fsync.c面向 与应用契约的持久化:数据先等页缓存,元数据(及可选 fast commit)再等 journal,并处理 无日志目录新建 与 barrier/写错。
分析 ioctl 入口时可在 ext4_ioctl.c 中搜索 FS_IOC_GETFSMAP/ext4_getfsmap;分析 fsync 与 fast commit 细节可对照 fast_commit.c 与 ext4_jbd2.h 中的 ext4_update_inode_fsync_trans。
正在加载留言…