RK3588 kernel-6.1 fs/ext4/move_extent.c 在线 extent 迁移(defrag)分析

RK3588 kernel-6.1 fs/ext4/move_extent.c 在线 extent 迁移(defrag)分析

1. 分析范围

  • rk3588/kernel-6.1/fs/ext4/move_extent.cEXT4_IOC_MOVE_EXTe4defrag 等工具触发的 两文件间逻辑区间与底层块映射交换
  • 协作:extents.c 中的 ext4_swap_extentsioctl.c(命令分发与 bigalloc/DAX 拒绝)、inode.cext4_get_block

2. 代码作用(一句话)

同一 superblock均为 extent 普通文件非 data=journal / 非加密 等前提下,ext4_move_extents** 按 原文件逻辑块范围 遍历 extent,逐 调用 move_extent_per_page加日记双文件页锁 → 若仍为 unwritten 可仅 ext4_swap_extents;否则 把原页数据读 uptodatefilemap_release_folio 尝试丢旧映射ext4_swap_extents 交换两文件在该区间的物理映射ext4_get_block + block_commit_write 把旧数据写回 原 inode 上新的物理块ext4_jbd2_inode_add_write;失败时 repair_branches 尝试 再 swap 回去 以防元数据与数据不一致。**


3. 解决了什么问题

问题 处理方式
大文件 extent 碎片严重,顺序读性能差 用户态用 卷上空白 donor 文件 预占位,再通过 move extent原文件某段 映射 换到 donor 已持有的连续物理区,从而减少碎片(在线 defrag 核心思路之一)
两 inode 同时改 i_data_sem 死锁 ext4_double_down_write_data_sem:按 inode 指针地址顺序 加锁;ext4_double_up_write_data_semorig/donor 实参顺序 释放(与加锁顺序配对)
journal_start、页fault、read_folio 交叉死锁 进入 move_extent_per_page 的事务后 抬起 i_data_sem(注释 a/b),只用 页锁 与 extent 路径.serialize
unwritten extent 在换映射前被其它路径初始化 持页锁后 mext_check_coverage 再扫一遍;若 已非全 unwritten 则走 数据拷贝路径mext_page_mkuptodate)而非纯 swap
swap 成功但 block_commit_write 失败 repair_branches:再 ext4_swap_extents 试图复原;若仍失败 ext4_error_inode_block
ENOSPC / 缓冲区被 journal 钉住 -EBUSY ext4_should_retry_allocjbd2_journal_force_commit_nested重试
非法参数与安全 mext_check_arguments仅 extent同 FSregular非 swapfiledonor 非 immutable/append/suid 组合限制逻辑起点按页对齐len 按 EOF 截断
与 DIO、截断并发 外层 lock_two_nondirectoriesinode_dio_waitext4_double_down_write_data_sem 包住整段循环
预分配与旧 goal 干扰 循环结束后若 moved_len!=0ext4_discard_preallocationsorig/donor

不支持(由 ioctl.c/ext4_move_extents 组合):bigallocDAXdata=journalingfscrypt(本文件内 IS_ENCRYPTED 检查)。


4. 调用链简图

1
2
3
4
5
6
7
8
9
10
11
12
ioctl: EXT4_IOC_MOVE_EXT
→ ext4_move_extents(o_filp, d_filp, orig_blk, donor_blk, len, moved_len)
→ lock_two_nondirectories + inode_dio_wait
→ ext4_double_down_write_data_sem
→ mext_check_arguments
→ while (逻辑块):
get_ext_path / 跳过洞 / 对齐页内 cur_len
ext4_double_up_write_data_sem
move_extent_per_page(...)
ext4_double_down_write_data_sem
→ discard preallocations
→ unlock

5. 与 ext4_swap_extents 的关系

move_extent.c 负责 页级 I/O、日记额度、锁升降序extents.cext4_swap_extents事务 handle改写两棵 extent 树 中对应逻辑区间的 物理指针(并处理 脏元数据)。两者必须 一致使用,分析实现时需 extents.c 对照阅读


6. 小结

move_extent.c 实现 内核级「换腱接骨」:在 不扩大用户可见文件布局语义 的前提下,交换两段文件的块映射并保证数据落到新映射上,供 e4defragonline defrag。正确性依赖 extent 类型再检查失败回滚 swapjbd2 inode ranged write 标记。

若需补充 用户态 e4defrag 典型步骤( donor 分配策略等),属于 工具链文档,超出本内核文件范围。

文章互动

阅读 --

留言

0 条留言

正在加载留言…