摘要
Linux 6.19 内核为其写回(writeback)机制带来了两项关键改进:允许文件系统自定义最小写回块大小,并增强了慢路径的可观测性。这些改动显著提升了在分区设备(zoned devices)上的I/O性能,减少了不必要的文件碎片。同时,新增的慢写回日志机制与唤醒等待任务的优化,极大地增强了Linux内核在运维/DevOps场景下的可观测性,能帮助管理员快速定位因写回阻塞导致的系统响应延迟问题。该补丁还对写回接口进行了清理,移除了低级别接口并统一到高层调用,并为XFS在zoned场景下自动设置了适配的最小写回块大小。
背景:为什么要动写回(writeback)策略?
内核写回子系统负责将页缓存(pagecache)中的“脏页”数据写回底层存储。长期以来,内核为“单次写回操作要写多少数据”设置了一个固定的下限,即最小写回块大小,其默认值为4MiB。这个较小的最小粒度在SSD或高带宽设备上影响不大,但在机械硬盘或zoned(分区化)存储设备上,则可能导致频繁在不同inode或zone之间切换写入位置,从而产生额外的寻道操作和异常碎片。尤其是在缓存的文件数远超可用写回带宽时,这种机制会将本可顺序的写入变为随机或碎片化写入,严重拖累性能并影响设备寿命。
因此,赋予文件系统根据设备特性(例如将最小写回块设为zone大小)来指定最小写回块的权限,可以从源头上减少不必要的寻道和碎片,提升I/O效率。
核心改动思路
本次优化主要围绕以下几点展开:
- 允许文件系统覆盖最小写回块:在
super_block结构体中新增long s_min_writeback_pages字段。写回子系统在计算单次写入量时,会优先使用文件系统通过此字段指定的值,而非内核固定的4MiB。例如,XFS在挂载zoned设备时就会将此值设置为zone的大小。
- 增强慢写回路径的可观测性:当写回进程的等待时间超过
sysctl_hung_task_timeout_secs(系统配置的僵死任务超时时间)时,内核会记录起始时间戳并打印告警信息,便于网络/系统层面的管理员排查因慢写回导致的系统延迟问题。
- 优化等待机制:在完成某个写回块(chunk)后,主动唤醒正在等待该写回操作完成的任务,从而减少轮询和长时间阻塞的风险。
- 清理与统一接口:移除了
filemap_fdatawrite_wbc、__filemap_fdatawrite_range等低层且不再必要的接口,统一使用更高层的filemap_writeback、filemap_fdatawrite_range、filemap_flush_range等API,减少了代码冗余和潜在的接口误用风险。
补丁要点
1) 在 include/linux/fs.h 增加 s_min_writeback_pages
struct super_block {
...
long s_min_writeback_pages; /* new: allow FS to override min writeback pages */
} __randomize_layout;
2) 定义保底值(历史上的 4MiB)
/* 4MiB minimal write chunk size */
#define MIN_WRITEBACK_PAGES (4096UL >> (PAGE_SHIFT - 10))
3) 写回块大小计算(将 sb 的值加入计算)
原来:
pages = round_down(pages + MIN_WRITEBACK_PAGES, MIN_WRITEBACK_PAGES);
调整后:
pages = min(wb->avg_write_bandwidth / 2,
global_wb_domain.dirty_limit / DIRTY_SCOPE);
pages = min(pages, work->nr_pages);
return round_down(pages + sb->s_min_writeback_pages,
sb->s_min_writeback_pages);
(说明:计算时使用sb->s_min_writeback_pages替代了全局的MIN_WRITEBACK_PAGES,从而可由文件系统按需指定最小单位。)
4) XFS 在挂载 zoned 设备时设置 s_min_writeback_pages
/* inside xfs_mount_zones() */
mp->m_super->s_min_writeback_pages =
XFS_FSB_TO_B(mp, min(zone_blocks, XFS_MAX_BMBT_EXTLEN)) >> PAGE_SHIFT;
(即把最小写回页数设置为zone的大小,旨在避免产生跨zone的碎片化写入。)
5) 慢写回日志与等待者唤醒
/* record start time for hung-checking */
done->wait_start = jiffies;
/* in completion wait callback */
if (waited_secs > sysctl_hung_task_timeout_secs)
pr_info("INFO: The task %s:%d has been waiting for writeback completion for more than %lu seconds.",
current->comm, current->pid, waited_secs);
/* wake up waiting tasks if progress detected */
if (work->done && work->done->progress_stamp &&
(jiffies - work->done->progress_stamp) > HZ * sysctl_hung_task_timeout_secs / 2)
wake_up_all(work->done->waitq);
(这套机制有助于在写回操作卡住或长期阻塞时,为事后分析留下关键调试信息,并主动尝试解除任务的阻塞状态。)
应用场景与实际收益
- Zoned / SMR 磁盘(如某些大容量HDD):将最小写回块调整为zone大小,可以保持写操作的顺序性,避免因跨区写入引发的垃圾回收开销和碎片化问题,从而提升吞吐量并减少设备磨损。
- 混合存储架构(如SSD存储元数据,HDD存储数据):针对作为数据盘的HDD设置更大的最小写回块,不仅不会损害性能,反而在数据盘主要承担顺序写入负载时更加友好。
- 云与存储服务:减少在维护窗口或高峰时段I/O操作之间的相互干扰。与
dm-pcache等缓存层配合时,能进一步提升整体数据库/中间件等存储栈的I/O效率。
- 系统诊断与可观测性:新增的慢写回日志和任务唤醒机制,使得定位长期等待写回的任务变得更加容易,显著缩短了以往如同“黑匣子”般的故障调查时间。
深入分析与洞察
- 为何优于“全局固定值”? 4MiB是一个历史折中值,但现代硬件特性千差万别。将最小写回粒度的决策权上交给文件系统或设备驱动是更合理的工程实践,因为它们最了解底层物理设备的特性(如zone大小、写入粒度)。这种设计遵循了正确的分层思想。
- 风险权衡:如果对非zoned设备盲目设置过大的最小写回块,可能会增加小规模写工作负载的延迟,或延长写回的时间窗口,导致内存占用增加和系统响应性下降。因此,补丁选择将此配置放在
super_block中,由文件系统按需设置,是一种稳妥的渐进式优化策略。
- 实现细节利弊:补丁同步清理了陈旧的写回接口,有利于长期代码维护。但短期内需要确保所有调用方(如Btrfs)都已迁移到新接口,以防出现功能倒退(regressions)。该补丁已对Btrfs进行了相应适配。
- 可观测性提升的价值:引入慢写回日志并记录起始时间戳,对于事后崩溃分析(post-mortem)极具价值。当在vmcore中看到“等待写回超时”的记录时,能快速定位到卡住的任务和具体时间点,极大简化了I/O挂起(IO hang)类问题的排查流程。
总结
- 对于系统用户而言,如果在使用zoned设备或传统机械硬盘(HDD)作为数据盘,升级到Linux 6.19内核将直接带来写回效率的提升,并显著降低由碎片化写入导致性能波动的风险(特别是使用XFS文件系统时,其已为zoned设备自动设置了优化的最小写回块)。
- 对于文件系统开发者,建议尽早适配新的高层写回接口(如
filemap_fdatawrite_range),并在合适的挂载路径上,根据设备特性审慎设置s_min_writeback_pages字段。
- 对于内核调试与运维人员,新增的慢写回日志与唤醒机制是强大的诊断工具,能帮助定位长时间阻塞的写回任务。同时,也需注意审查日志,避免将短暂的性能波动误判为严重问题。
参考链接: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ebaeabfa5ab711a9b69b686d58329e258fdae75f