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 核心都有同一問題。

參考資料