btrfs 空闲空间管理(四) fstrim
源文件
util-linux-<version>/sys-utils/fstrim.c
linux/fs/btrfs/ioctl.c
linux/fs/btrfs/extent-tree.c
linux/fs/btrfs/free-space-cache.c
用户态工具 fstrim
fstrim 是 util-linux 软件包中提供的工具:
discard unused blocks on a mounted filesystem
该工具有 3 个选项跟要 discard 的范围有关:
-o, --offset offset Byte offset in filesystem from which to begin searching for free blocks to discard. Default value is zero, starting at the beginning of the filesystem. -l, --length length Number of bytes after starting point to search for free blocks to dis‐ card. If the specified value extends past the end of the filesystem, fstrim will stop at the filesystem size boundary. Default value extends to the end of the filesystem. -m, --minimum minimum-free-extent Minimum contiguous free range to discard, in bytes. (This value is inter‐ nally rounded up to a multiple of the filesystem block size). Free ranges smaller than this will be ignored. By increasing this value, the fstrim operation will complete more quickly for filesystems with badly fragmented freespace, although not all blocks will be discarded. Default value is zero, discard every free block.
查看 fstrim.c 的源文件,可以看到,fstrim 将 3 个范围参数封装到结构 struct fstrim_range 中,
struct fstrim_range { uint64_t start; uint64_t len; uint64_t minlen; };
然后通过 ioctl 命令 FITRIM 来完成实际的 discard 操作:
ioctl(fd, FITRIM, &range)
ioctl 接收 FITRIM 命令
btrfs/ioctl.c 对 FITRIM 命令进行处理:
case FITRIM: return btrfs_ioctl_fitrim(file, argp);
ioctl.c 文件中也定义了函数 btrfs_ioctl_fitrim,该函数对参数进行检验调整后(比如调整 minlen 为文件系统设备所支持的最小 discard 粒度,如果这个粒度比命令指定的 minlen 小的话,调整范围不会超出文件末尾),转而由 btrfs_trim_fs(fs_info->tree_root, &range) 来完成实际的工作。
btrfs_trim_fs 在 extent-tree.c 中定义,其工作就是从指定的起始地址对应的 block group 开始,对指定范围内所覆盖的 bg,逐个执行btrfs_trim_block_group。这就是工作的分解:
fstrim 整个文件系统 -> 逐个 trim 文件系统内的块组
btrfs_trim_block_group 实现在 free-space-cache.c 中。
btrfs_trim_block_group
int btrfs_trim_block_group(struct btrfs_block_group_cache *block_group, u64 *trimmed, u64 start, u64 end, u64 minlen) { int ret; *trimmed = 0; ret = trim_no_bitmap(block_group, trimmed, start, end, minlen); if (ret) return ret; ret = trim_bitmaps(block_group, trimmed, start, end, minlen); return ret; }
可以看出,trim bg 实际上是对 bg 下面的 extent/bitmap 空闲空间记录进行 trim。
trim_no_bitmap 和 trim_bitmaps 都会调用 btrfs_error_discard_extent,而 btrfs_error_discard_extent 是对 btrfs_discard_extent 的简单封装。btrfs_discard_extent 会调用 btrfs_issue_discard,最终调用 blkdev_issue_discard 向块设备发送 Discard/TRIM 命令。
对于 free-space-cache 来说,trim 就是把对应的空闲空间记录给销毁。
小结
fstrim 的执行流程:
fstrim -> FITRIM -> btrfs_ioctl_fitrim -> btrfs_trim_fs -> btrfs_trim_block_group -> trim_no_bitmap / trim_bitmaps -> btrfs_error_discard_extent -> btrfs_discard_extent -> btrfs_issue_discard -> blkdev_issue_discard