HC620 上 Btrfs zoned 两次故障复盘:锁死、只读与内核排查

记录 HC620 在 Ubuntu 26.04、Linux 7.0.0-28 下出现 Btrfs cleaner 锁死与事务中止只读的完整排查过程,包含日志分析、应急命令、数据校验和内核测试方案。

这是一篇真实故障复盘。环境是一块 Western Digital HC620 Host-Managed SMR 硬盘,使用 Btrfs zoned,挂载在 /mnt/disk1,系统为 Ubuntu 26.04 LTS,内核为:

1
Linux data-server 7.0.0-28-generic

故障不是普通的硬盘速度下降,而是先后出现了两种严重异常:

  1. btrfs-cleanerbtrfs-transaction 进入不可中断的 D 状态,文件系统无法卸载;
  2. Btrfs 事务以 -11 中止,内核主动把文件系统切换成只读。

两次故障都发生在数 TB 级删除、复制和 zoned 空间回收附近。最终没有重装系统,也没有执行 btrfs check --repair。本文尽可能保留现场日志、判断过程、实际命令和没有被证实的部分,供遇到相似问题的人参考。

注意:这不是“HC620 上所有 Btrfs 都必然出错”的证明,也不能仅凭一段调用栈确定具体内核补丁。文中内核和 Ubuntu 软件包版本是 2026 年 7 月 24 日的现场信息,实际操作前应重新查询当前仓库。

环境与触发过程

这块盘主要保存备份和冷数据。第一次故障前的操作大致是:

1
2
3
4
删除约 3TB 旧数据
→ 从另一块盘复制约 3TB 新数据
→ Btrfs 后台清理与新写入重叠
→ btrfs-cleaner 锁死

HC620 是 Host-Managed SMR。磁盘按 zone 管理,现场显示每个 zone 为:

1
Device zone size: 256.00MiB

在这类设备上,已经写过的 zone 不能像普通 CMR 硬盘一样任意覆盖。删除文件只是解除数据块引用,其中的无效空间需要等 block group 回收、有效数据迁移以及 zone reset 后才能重新利用。Btrfs 会把这部分空间显示为:

1
Device zone unusable

因此,“删除 3TB 后立即再写 3TB”不只是一次删除加一次复制。后台可能同时发生:

1
2
3
4
5
6
7
提交删除事务
删除空 block group
回收旧 chunk
finish/reset zone
分配新 chunk
激活新 zone
持续写入新数据

这成为后续分析调用栈的重要背景。

第一次故障:btrfs-cleaner 长时间阻塞

最初日志包含:

1
2
3
INFO: task btrfs-cleaner:1488 blocked for more than 368 seconds.
Not tainted 7.0.0-28-generic #28-Ubuntu
task:btrfs-cleaner state:D pid:1488

关键调用链是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
__mutex_lock_slowpath
mutex_lock
btrfs_chunk_alloc
btrfs_inc_block_group_ro
do_zone_finish
btrfs_zone_finish_one_bg
btrfs_zoned_activate_one_bg
reserve_chunk_space
check_system_chunk
btrfs_remove_chunk
btrfs_delete_unused_bgs
cleaner_kthread

把它从下往上读,过程是:

1
2
3
4
5
6
7
btrfs-cleaner 清理未使用的 block group
→ 删除旧 chunk
→ 检查并预留 system chunk 空间
→ 激活 zoned block group
→ finish 一个 zone
→ 再次进入 chunk 分配
→ 等待 mutex

日志还提示 mutex 很可能由同一个 btrfs-cleaner 线程持有。结合调用链,现场判断是:清理线程在 block-group 删除、zone finish 和新 chunk 分配交叠时进入了锁等待。随后 btrfs-transaction 和等待 Btrfs flush 的 kworker 也被连带阻塞。

这是对现场调用栈的解释,不等于已经找到了唯一对应的上游 bug。能确定的是:

  • 阻塞发生在 Btrfs zoned 的 block-group/chunk 清理路径;
  • 线程已在内核态进入 D 状态;
  • 普通 kill -9 不能结束内核线程;
  • 继续执行依赖事务提交的操作可能一起卡住。

第一时间做了哪些检查

先停止 rsnapshot、rsync、Docker 容器或其他写盘任务,然后检查不可中断进程:

1
2
ps -eo state,pid,ppid,comm,wchan:35,args | \
awk 'NR==1 || $1 ~ /^D/'

保存本次启动的内核日志:

1
2
3
4
5
sudo journalctl -k -b \
  > /root/btrfs-hang-$(date +%F-%H%M).log

sudo dmesg -T | \
grep -iE 'btrfs|hung task|I/O error|ata|blk_update'

保存相关线程堆栈:

1
2
sudo cat /proc/1488/stack
sudo cat /proc/1489/stack

查询文件系统时加超时,避免诊断命令本身也永久阻塞:

1
2
sudo timeout 15s btrfs device stats /mnt/disk1
sudo timeout 15s btrfs filesystem usage /mnt/disk1

随后尝试一次正常卸载:

1
2
cd /
sudo timeout 60s umount /mnt/disk1

结果是:

1
umount: /mnt/disk1: target is busy.

target is busy 与内核锁死不是一回事

target is busy 通常表示仍有以下引用:

  • 进程的当前工作目录位于 /mnt/disk1
  • 文件仍被打开;
  • 备份或复制程序仍在运行;
  • /mnt/disk1 下存在子挂载点;
  • 用户进程已经因文件系统锁死进入 D 状态。

先保证所有终端退出挂载目录:

1
2
cd /
pwd

检查子挂载和占用者:

1
2
3
4
sudo findmnt -R /mnt/disk1
sudo timeout 15s fuser -vmM /mnt/disk1

pgrep -af 'rsnapshot|rsync|borg|restic|tar|cp|mv|docker'

普通用户进程可以先正常终止,再考虑强制终止:

1
2
3
sudo kill -TERM 1234 5678
sleep 5
sudo kill -KILL 1234 5678

如果是 rsnapshot 或 rsync:

1
2
3
4
5
sudo pkill -TERM -x rsnapshot
sudo pkill -TERM -x rsync
sleep 5
sudo pkill -KILL -x rsnapshot
sudo pkill -KILL -x rsync

但不要试图杀死:

1
2
btrfs-cleaner
btrfs-transaction

它们是内核线程。现场检查显示阻塞者主要就是 Btrfs 内核线程,继续 killumount 或全局 sync 已经无法解决根因。

为什么没有使用 lazy unmount 或强制卸载

当时没有执行:

1
2
sudo umount -l /mnt/disk1
sudo umount -f /mnt/disk1

umount -l 只是先从目录树摘除挂载点,真正的文件系统引用仍要等不再繁忙后才能清理。Btrfs transaction 已经锁死时,它可能只隐藏挂载点,并不代表数据已提交或文件系统已完成卸载。

umount -f 也不是解除本地 Btrfs 内核死锁的可靠手段。它更常用于部分网络或用户态文件系统。

同样没有运行:

1
2
3
4
5
6
sync
btrfs balance start /mnt/disk1
btrfs scrub start /mnt/disk1
btrfs filesystem defragment -r /mnt/disk1
mount -o remount,ro /mnt/disk1
btrfs check --repair /dev/sda

这些操作可能等待已经锁住的事务,或者再次进入出问题的 block-group 回收路径。

重启前增加 noauto

为了避免机器重启后立刻用同一个内核自动读写这块盘,先检查 /etc/fstab

1
grep -n '/mnt/disk1' /etc/fstab

备份配置:

1
2
3
4
sudo cp -a /etc/fstab \
  /etc/fstab.bak-$(date +%F-%H%M%S)

sudoedit /etc/fstab

例如原来是:

1
UUID=xxxx /mnt/disk1 btrfs defaults 0 0

临时改成:

1
UUID=xxxx /mnt/disk1 btrfs defaults,noauto 0 0

noauto 的意思只是开机不自动挂载,不是禁用文件系统。系统启动后仍可手动只读或读写挂载。

从正常重启到最后手段

现场先执行了正常重启:

1
sudo systemctl reboot

这一次正常完成,重启后文件系统也能重新挂载。若正常重启因 Btrfs 卡住,才按风险逐级增加强制程度:

1
sudo systemctl reboot --force

最后手段才是:

1
sudo systemctl reboot --force --force

两个 --force 不再正常停止服务和卸载文件系统,效果接近硬复位,可能丢失尚未提交的数据。它只能用于系统已经无法正常收尾的情况,不能当作普通重启命令。

重启后的检查

先确认实际启动的内核和磁盘:

1
2
3
uname -a
lsblk -o NAME,SIZE,MODEL,FSTYPE,MOUNTPOINTS
sudo smartctl -x /dev/sda

确认没有自动挂载:

1
findmnt /mnt/disk1

第一次先只读挂载:

1
sudo mount -o ro /dev/sda /mnt/disk1

实际环境应优先使用 UUID 或 /dev/disk/by-id/,不要默认设备永远是 /dev/sda

检查内核日志、Btrfs 设备计数和空间状态:

1
2
3
4
5
sudo dmesg -T | \
grep -iE 'btrfs|ata|I/O error|medium error|uncorrect|reset'

sudo btrfs device stats /mnt/disk1
sudo btrfs filesystem usage /mnt/disk1

现场 btrfs device stats 全部为 0:

1
2
3
4
5
[/dev/sda].write_io_errs    0
[/dev/sda].read_io_errs     0
[/dev/sda].flush_io_errs    0
[/dev/sda].corruption_errs  0
[/dev/sda].generation_errs  0

这说明 Btrfs 没有记录到设备读写、flush、校验或 generation 错误,是好现象。但它不能证明内核锁死已经修复,也不能排除尚未读取的数据问题。

scrub 做什么,不能做什么

系统稳定后安排了一次 scrub:

1
2
sudo btrfs scrub start /mnt/disk1
sudo btrfs scrub status /mnt/disk1

如果希望前台等待结果:

1
sudo btrfs scrub start -B /mnt/disk1

取消任务:

1
sudo btrfs scrub cancel /mnt/disk1

Scrub 会读取已存储的数据和元数据,并核对 Btrfs 保存的校验和。它用于发现读错误、数据或元数据校验错误,不是:

1
2
3
4
5
6
碎片整理
空间回收
balance
坏道修复
完整 fsck
内核死锁修复

如果结果为:

1
Error summary: no errors found

它只能说明本次读取和校验没有发现异常,不能证明 btrfs-cleaner 的 zoned 清理路径以后不会再次锁死。

保存结果:

1
2
sudo btrfs scrub status /mnt/disk1 | \
sudo tee /root/btrfs-scrub-$(date +%F).log

Scrub 期间不要同时运行 rsync、rsnapshot、balance 或大量删除。对这类备份盘,可以按实际使用强度每一至三个月执行一次。

为什么没有为了“回收空间”运行 balance

现场曾出现:

1
Data,single: 98.67%

这个数字只表示已经分配的 Data block group 使用率,并不代表整块盘只剩 1.33%。当时仍有数 TiB Device unallocated,并不缺少可继续分配的空间。

因此没有执行:

1
2
sudo btrfs balance start /mnt/disk1
sudo btrfs balance start -dusage=0 /mnt/disk1

Balance 会重新定位 block group,而第一次故障正位于:

1
2
3
4
btrfs_delete_unused_bgs
btrfs_remove_chunk
btrfs_zoned_activate_one_bg
do_zone_finish

在问题内核上主动 balance,可能再次进入相近的 block-group 删除和 zone 回收路径。

如何处理数 TB 删除与写入

最有效的工作流调整是:有足够空间时,先复制并校验新数据,再删除旧数据。

1
2
3
4
先复制新数据
→ 校验复制结果
→ 暂停新写入并观察
→ 分批删除旧数据

复制:

1
2
3
rsync -aHAX --info=progress2 \
  /来源目录/ \
  /mnt/disk1/目标目录/

比较容量:

1
du -sh /来源目录 /mnt/disk1/目标目录

用内容校验方式演练,不修改目标:

1
2
3
rsync -aHAXnc \
  /来源目录/ \
  /mnt/disk1/目标目录/

必须先删除时,不要一次删完数 TB 后马上写入。可以按日期、子目录或容量分为 200~500GiB 一批:

1
2
rm -rf /mnt/disk1/旧数据/第一批
sudo btrfs filesystem sync /mnt/disk1

btrfs filesystem sync 会提交文件系统事务并唤醒相关清理工作,但命令返回不表示所有 block group 和 zone reclaim 已全部完成。

如果删除的是 Btrfs 子卷或快照,还可以执行:

1
sudo btrfs subvolume sync /mnt/disk1

它等待已提交删除请求的子卷完成清理,不适用于普通 rm -rf 删除。

每批后检查:

1
2
3
4
5
6
7
8
sudo btrfs filesystem usage /mnt/disk1
sudo btrfs device stats /mnt/disk1

ps -eo state,pid,comm,wchan:35,args | \
awk 'NR==1 || $1 ~ /^D/'

sudo journalctl -k --since "30 minutes ago" | \
grep -iE 'btrfs.*(blocked|hung|error|warning)|I/O error'

满足以下条件后再继续:

  • btrfs filesystem sync 正常返回;
  • 没有 Btrfs 线程处于 D 状态;
  • 内核日志没有新的 blocked、hung、abort 或 Btrfs error;
  • 磁盘活动已经明显回落;
  • Device zone unusable 没有快速增长。

Device zone unusable 不需要降到 0。它可能在未达到下一轮回收条件时停留在几十 GiB,这本身不等于文件系统仍然卡住。

写入也可以分批,并限制优先级和带宽:

1
2
3
4
sudo ionice -c2 -n7 nice -n 15 \
rsync -aHAX --info=progress2 --bwlimit=100M \
  /来源目录/ \
  /mnt/disk1/目标目录/

--bwlimit=100M 只是保守起点。限速不能修复内核 bug,但可以减少 cleaner、事务提交、writeback 和 zone activation 同时处于高负载的概率。不要并发运行多个大型 rsync。

监控 zoned 回收指标

先获得文件系统 UUID:

1
2
3
4
FSID=$(sudo btrfs filesystem show /mnt/disk1 | \
       awk '/uuid:/ {print $NF; exit}')

echo "$FSID"

查看 data、metadata 和 system 的空间及回收指标:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
for type in data metadata system; do
    echo "===== $type ====="
    base="/sys/fs/btrfs/$FSID/allocations/$type"

    grep -H . \
      "$base/bytes_pinned" \
      "$base/bytes_reserved" \
      "$base/bytes_may_use" \
      "$base/bytes_zone_unusable" \
      "$base/reclaim_count" \
      "$base/reclaim_bytes" \
      "$base/reclaim_errors" \
      "$base/periodic_reclaim" \
      "$base/dynamic_reclaim" \
      "$base/bg_reclaim_threshold" 2>/dev/null
done

其中:

  • bytes_pinned:通常要等当前事务提交后才能释放的空间;
  • bytes_zone_unusable:已失效但尚不能直接复用的 zoned 空间;
  • reclaim_count:累计回收尝试次数;
  • reclaim_bytes:累计回收字节数;
  • reclaim_errors:累计回收错误数。

还可以查看事务提交统计:

1
cat "/sys/fs/btrfs/$FSID/commit_stats"

如果 max_commit_ms 持续达到几十秒或数百秒,或者日志再次出现:

1
2
btrfs-cleaner blocked
btrfs-transaction blocked

应停止备份任务并保存日志,不要继续灌入数据等待它自行恢复。

禁止备份任务重叠

所有 rsnapshot 周期使用同一个锁:

1
2
3
4
/usr/bin/flock -n /run/lock/rsnapshot.lock \
  /usr/bin/ionice -c2 -n7 \
  /usr/bin/nice -n 15 \
  /usr/bin/rsnapshot daily

weeklymonthly 也使用:

1
/run/lock/rsnapshot.lock

这样可以避免 daily、weekly、monthly 与大量 rsync 同时进行。Scrub、大量删除和大型复制也应该安排在不同时间段。

第二次故障:事务中止并被强制只读

第一次故障重启后,文件系统可以正常挂载,device stats 也没有错误。但之后再次写入大量数据时,出现了更明确的事务失败:

1
2
3
4
5
6
BTRFS error (device sda): error while writing out transaction: -11
BTRFS warning (device sda): Skipping commit of aborted transaction.
BTRFS: Transaction aborted (error -11)
BTRFS: error (device sda state A) in cleanup_transaction:2060:
errno=-11 unknown
BTRFS info (device sda state EA): forced readonly

运行环境仍是:

1
2
CPU: 0 PID: 1678 Comm: btrfs-transacti
Not tainted 7.0.0-28-generic #28-Ubuntu

这次不是“暂时卡住”,而是事务已经 aborted,内核为保护文件系统主动切换成只读。-11 对应 EAGAIN,但只凭 errno 不能确定触发它的具体代码。

当时的设备统计仍全部为 0:

1
2
3
4
5
write_io_errs    0
read_io_errs     0
flush_io_errs    0
corruption_errs  0
generation_errs  0

空间状态也不是整盘写满:

1
2
3
4
5
Device size:            12.73TiB
Device allocated:        4.95TiB
Device unallocated:      7.78TiB
Device zone unusable:   53.58GiB
Used:                    4.88TiB

这组证据更支持 zoned Btrfs 内部写入、chunk 分配或事务逻辑异常,而不是普通介质 I/O 错误或整盘 ENOSPC。但在没有完整首条错误和对应源码分析前,不应把原因断言为某一个补丁。

forced readonly 后的正确处理

不要尝试:

1
2
3
4
sudo mount -o remount,rw /mnt/disk1
sudo btrfs balance start /mnt/disk1
sudo btrfs filesystem sync /mnt/disk1
sudo btrfs check --repair /dev/sda

先停止写入任务:

1
2
3
4
sudo pkill -TERM -x rsync
sudo pkill -TERM -x rsnapshot

pgrep -af 'rsync|rsnapshot|btrfs balance|btrfs scrub'

保存故障前后完整日志。真正导致事务中止的第一条错误可能出现在 forced readonly 之前几十行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
sudo journalctl -k -b \
  --since "2026-07-24 17:20:00" \
  --until "2026-07-24 17:35:00" \
  > /root/btrfs-abort-2026-07-24.log

sudo dmesg -T \
  > /root/dmesg-btrfs-abort-2026-07-24.log

sudo smartctl -x /dev/sda \
  > /root/smart-sda-2026-07-24.log

确认挂载选项:

1
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /mnt/disk1

其中应出现 ro。随后正常卸载并重启:

1
2
3
cd /
sudo umount /mnt/disk1
sudo reboot

若提示 busy:

1
sudo fuser -vmM /mnt/disk1

停止列出的普通用户进程后再卸载。

检查未完成的复制

事务中止前已经写入的部分数据不能直接当作完整备份。可能出现:

  • 文件尚未创建;
  • 文件长度不完整;
  • 数据写入了,但目录项或时间戳没有提交;
  • 最后一个事务内的改动被回滚。

因此必须保留源盘。切换到稳定环境并重新读写挂载后,用原 rsync 补齐:

1
2
3
4
sudo ionice -c2 -n7 nice -n 15 \
rsync -aHAX --info=progress2 \
  /来源目录/ \
  /mnt/disk1/目标目录/

再用校验模式验证:

1
2
3
rsync -aHAXnc \
  /来源目录/ \
  /mnt/disk1/目标目录/

没有输出才表示 rsync 没有发现需要更新的内容。

内核方案:先区分测试与长期使用

同一台机器、同一个 7.0.0-28-generic、同一块 HC620 已连续出现:

  1. btrfs-cleaner 与 transaction 线程锁死;
  2. error -11、transaction aborted、forced readonly。

因此不再继续用该内核对这块盘做数 TB 读写。讨论过三条路线:

路线 用途 局限
Ubuntu 26.04 + 7.1 Mainline 判断新上游内核是否缓解故障 非 Ubuntu 生产支持内核,具体修复未确认
Ubuntu 24.04 + 6.8 GA 判断 7.0 是否引入回归,长期支持较好 zoned Btrfs 代码较旧
Ubuntu 24.04 + 6.17 短期对比测试 zone reclaim 行为 支持周期接近结束,不适合长期无人值守

这里最重要的不是追求版本号最大,而是:

  • 保留一个可以启动的旧内核;
  • 第一次只做一次性 GRUB 启动;
  • 先只读挂载;
  • 从 100~200GiB 逐步测试;
  • 不再直接复现“删除 3TB 后立即写入 3TB”。

在 Ubuntu 24.04 确认 6.8 GA

不要在现有 Ubuntu 26.04 中混装 Ubuntu 24.04 软件包。更安全的办法是在另一块 SSD 安装 Ubuntu Server 24.04,再挂载 HC620 数据盘测试。

安装并保留 6.8 GA 元包:

1
2
3
sudo apt update
sudo apt install --install-recommends linux-generic-6.8
sudo reboot

确认系统和内核:

1
2
3
4
5
cat /etc/os-release
uname -r

dpkg -l | \
grep -E '^ii +linux-(generic|image-generic|headers-generic)(-6\.8)? '

关键是 uname -r6.8. 开头,不要把某个补丁版本永久写死。

查询当前仓库,而不是照抄本文的历史版本:

1
2
3
4
apt-cache policy \
  linux-generic-6.8 \
  linux-generic \
  linux-generic-hwe-24.04

如果测试目标是 GA 6.8,不要让 linux-generic-hwe-24.04 把系统带回另一条 HWE 内核线。

在 Ubuntu 24.04 短期测试 6.17

先查询当前仓库是否仍提供和维护该包:

1
2
3
4
5
sudo apt update

apt-cache policy \
  linux-generic-6.17 \
  linux-image-generic-6.17

确认包仍可用后,明确安装:

1
2
sudo apt install --install-recommends linux-generic-6.17
sudo update-grub

确认文件:

1
ls -lh /boot/vmlinuz-6.17.0-*-generic

第一次只启动一次。下面的完整版本必须替换成机器实际安装的版本:

1
2
3
4
5
sudo grub-reboot \
  'Advanced options for Ubuntu>Ubuntu, with Linux 6.17.0-xx-generic'

sudo grub-editenv - list
sudo reboot

重启后检查:

1
uname -r

不应在 Ubuntu 26.04 中添加 24.04 Noble 仓库来强装这套内核,否则可能引入 firmware、DKMS、initramfs 和后续 apt 维护问题。

测试 7.1 Mainline 时的边界

Mainline 包适合验证“上游新内核是否改变故障表现”,不等于 Ubuntu 官方生产内核。安装前必须检查:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
. /etc/os-release
echo "$PRETTY_NAME  codename=$VERSION_CODENAME"

dpkg --print-architecture
df -h /boot /

sudo apt update
sudo apt install -y mokutil
mokutil --sb-state
dkms status

如果 Secure Boot 已启用、存在关键 DKMS 模块或服务器没有带外控制台,测试风险会明显增加。具体版本、文件名和校验值应从当前 Ubuntu Mainline Kernel Archive 重新获取,不应复制过期下载地址。

保留当前可启动内核,通过 GRUB 一次性启动新内核。启动后检查:

1
2
3
4
5
6
7
8
uname -r
systemctl --failed
ip addr
ip route
dkms status

sudo dmesg -T | \
grep -iE 'btrfs|hung task|blocked for more|I/O error|ata|medium error|uncorrect'

新内核的首次挂载与测试

无论测试 6.8、6.17 还是更新内核,先保留 /etc/fstab 中的 noauto,启动后只读挂载:

1
2
3
4
5
6
7
sudo mount -o ro /dev/sda /mnt/disk1

sudo journalctl -k -b | \
grep -iE 'btrfs|error|warning|abort|readonly|hung|blocked|I/O error'

sudo btrfs device stats /mnt/disk1
sudo btrfs filesystem usage /mnt/disk1

没有新错误后:

1
2
3
4
sudo umount /mnt/disk1
sudo mount /dev/sda /mnt/disk1

findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /mnt/disk1

逐步测试:

1
2
3
4
5
6
写入 100~200GiB
→ btrfs filesystem sync
→ 检查日志和 D 状态
→ 删除 100~200GiB
→ 等待并再次检查
→ 再写入下一批

不能因为小批量通过,就立即用数 TB 压力证明“已经修复”。至少需要多轮、大量数据和较长运行时间才能提高信心。

是否重新格式化 Btrfs

重新格式化可以得到干净文件系统,但不能单独修复内核 zoned 路径问题。两次异常都发生在运行时的 block-group、zone 和事务路径,且 device stats 没有介质错误。

只有遇到以下情况时,重建文件系统才更有意义:

  • btrfs check --readonly 发现明确结构错误;
  • scrub 持续出现无法修复的校验错误;
  • 无法正常挂载或持续发生 generation 错误;
  • 已决定调整数据/元数据 profile;
  • 准备更换文件系统。

即使要重建,也应先完成另一份可验证的备份,而不是把格式化当作试错命令。

Btrfs 与 F2FS 怎么选

F2FS 可以绕开这次出现的 Btrfs 特有调用路径:

1
2
3
4
5
btrfs_delete_unused_bgs
btrfs_remove_chunk
btrfs_zone_finish_one_bg
Btrfs chunk allocation
Btrfs transaction commit

但它不是“没有问题”的方案。F2FS 同样存在 GC、segment 搬移、zone reset、掉电恢复和文件系统修复方面的风险。大量删除后立即大量写入,仍可能出现较长的前台 GC 延迟。

两者的主要取舍是:

项目 Btrfs zoned F2FS
文件数据校验和 通常没有同等级端到端数据校验
scrub 无 Btrfs 式 scrub
快照 无 Btrfs 式快照
本次故障路径 已实际触发两次 可避开 Btrfs 特有路径
后台空间回收 block group/zone reclaim segment GC/zone reset

如果 HC620 只是多份备份中的一份,F2FS 加外部校验可以接受。如果它是唯一副本,无论使用单盘 Btrfs 还是 F2FS 都不够安全。

使用 F2FS 时可以把 SHA256 清单保存到另一块盘:

1
2
3
4
5
6
cd /mnt/disk1

find backup_data -type f -print0 | \
sort -z | \
xargs -0 sha256sum \
  > /其他磁盘/backup_data.sha256

以后验证:

1
2
cd /mnt/disk1
sha256sum -c /其他磁盘/backup_data.sha256

硬盘自身 ECC、坏扇区重映射和 SATA CRC 仍然会工作,但它们不能覆盖应用、内存、文件系统、控制器和磁盘之间的完整端到端链路。Btrfs 校验和与硬盘 ECC 是互补关系,不是重复功能。

最后的处理原则

这次排障形成了以下实际规则:

  1. 不再用 7.0.0-28-generic 对这块 HC620 做大规模读写;
  2. 保留源数据和至少一份独立副本;
  3. 先复制、校验,再分批删除;
  4. 数 TB 操作拆成 200~500GiB 批次;
  5. rsync、rsnapshot、scrub、大量删除和 balance 不重叠;
  6. 出现 D 状态或 hung-task 日志后立即停止写入;
  7. forced readonly 后不强行 remount,rw;
  8. 不使用 btrfs check --repair 进行盲目尝试;
  9. 测试内核必须保留回退项,并从只读挂载开始;
  10. 长期可靠性首先来自多副本,其次才是文件系统选择。

整个故障链可以概括为:

1
2
3
4
5
6
7
数 TB 删除
→ zoned 无效空间和后台回收增加
→ 数 TB 新写入与 chunk/zone 管理交叠
→ 第一次:btrfs-cleaner 锁等待,事务线程阻塞
→ 重启后暂时恢复,scrub 与设备计数未见错误
→ 再次大量写入
→ 第二次:transaction -11,中止并 forced readonly

这组结果足以说明当前“HC620 + Btrfs zoned + 7.0.0-28 + 大删大写”组合不适合继续承担无人值守的大规模任务,但还不足以证明所有 HC620、所有 Btrfs zoned 或所有 7.0 内核都有同一问题。

参考资料