這是一篇真實故障覆盤。環境是一塊 Western Digital HC620 Host-Managed SMR 硬碟,使用 Btrfs zoned,掛載在 /mnt/disk1,系統為 Ubuntu 26.04 LTS,核心為:
|
|
故障不是普通的硬碟速度下降,而是先後出現了兩種嚴重異常:
btrfs-cleaner和btrfs-transaction進入不可中斷的D狀態,檔案系統無法卸載;- Btrfs 事務以
-11中止,核心主動把檔案系統切換成只讀。
兩次故障都發生在數 TB 級刪除、複製和 zoned 空間回收附近。最終沒有重灌系統,也沒有執行 btrfs check --repair。本文儘可能保留現場日誌、判斷過程、實際命令和沒有被證實的部分,供遇到相似問題的人參考。
注意:這不是“HC620 上所有 Btrfs 都必然出錯”的證明,也不能僅憑一段呼叫棧確定具體核心補丁。文中核心和 Ubuntu 軟體包版本是 2026 年 7 月 24 日的現場資訊,實際操作前應重新查詢當前倉庫。
環境與觸發過程
這塊盤主要儲存備份和冷資料。第一次故障前的操作大致是:
|
|
HC620 是 Host-Managed SMR。磁碟按 zone 管理,現場顯示每個 zone 為:
|
|
在這類裝置上,已經寫過的 zone 不能像普通 CMR 硬碟一樣任意覆蓋。刪除檔案只是解除資料塊引用,其中的無效空間需要等 block group 回收、有效資料遷移以及 zone reset 後才能重新利用。Btrfs 會把這部分空間顯示為:
|
|
因此,“刪除 3TB 後立即再寫 3TB”不只是一次刪除加一次複製。後臺可能同時發生:
|
|
這成為後續分析呼叫棧的重要背景。
第一次故障:btrfs-cleaner 長時間阻塞
最初日誌包含:
|
|
關鍵呼叫鏈是:
|
|
把它從下往上讀,過程是:
|
|
日誌還提示 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 容器或其他寫盤任務,然後檢查不可中斷程序:
|
|
儲存本次啟動的核心日誌:
|
|
儲存相關執行緒堆疊:
|
|
查詢檔案系統時加超時,避免診斷命令本身也永久阻塞:
|
|
隨後嘗試一次正常卸載:
|
|
結果是:
|
|
target is busy 與核心鎖死不是一回事
target is busy 通常表示仍有以下引用:
- 程序的當前工作目錄位於
/mnt/disk1; - 檔案仍被開啟;
- 備份或複製程式仍在執行;
/mnt/disk1下存在子掛載點;- 使用者程序已經因檔案系統鎖死進入
D狀態。
先保證所有終端退出掛載目錄:
|
|
檢查子掛載和佔用者:
|
|
普通使用者程序可以先正常終止,再考慮強制終止:
|
|
如果是 rsnapshot 或 rsync:
|
|
但不要試圖殺死:
|
|
它們是核心執行緒。現場檢查顯示阻塞者主要就是 Btrfs 核心執行緒,繼續 kill、umount 或全域性 sync 已經無法解決根因。
為什麼沒有使用 lazy unmount 或強制卸載
當時沒有執行:
|
|
umount -l 只是先從目錄樹摘除掛載點,真正的檔案系統引用仍要等不再繁忙後才能清理。Btrfs transaction 已經鎖死時,它可能只隱藏掛載點,並不代表資料已提交或檔案系統已完成卸載。
umount -f 也不是解除本地 Btrfs 核心死鎖的可靠手段。它更常用於部分網路或使用者態檔案系統。
同樣沒有執行:
|
|
這些操作可能等待已經鎖住的事務,或者再次進入出問題的 block-group 回收路徑。
重啟前增加 noauto
為了避免機器重啟後立刻用同一個核心自動讀寫這塊盤,先檢查 /etc/fstab:
|
|
備份配置:
|
|
例如原來是:
|
|
臨時改成:
|
|
noauto 的意思只是開機不自動掛載,不是禁用檔案系統。系統啟動後仍可手動只讀或讀寫掛載。
從正常重啟到最後手段
現場先執行了正常重啟:
|
|
這一次正常完成,重啟後檔案系統也能重新掛載。若正常重啟因 Btrfs 卡住,才按風險逐級增加強制程度:
|
|
最後手段才是:
|
|
兩個 --force 不再正常停止服務和卸載檔案系統,效果接近硬復位,可能丟失尚未提交的資料。它只能用於系統已經無法正常收尾的情況,不能當作普通重啟命令。
重啟後的檢查
先確認實際啟動的核心和磁碟:
|
|
確認沒有自動掛載:
|
|
第一次先只讀掛載:
|
|
實際環境應優先使用 UUID 或 /dev/disk/by-id/,不要預設裝置永遠是 /dev/sda。
檢查核心日誌、Btrfs 裝置計數和空間狀態:
|
|
現場 btrfs device stats 全部為 0:
|
|
這說明 Btrfs 沒有記錄到裝置讀寫、flush、校驗或 generation 錯誤,是好現象。但它不能證明核心鎖死已經修復,也不能排除尚未讀取的資料問題。
scrub 做什麼,不能做什麼
系統穩定後安排了一次 scrub:
|
|
如果希望前臺等待結果:
|
|
取消任務:
|
|
Scrub 會讀取已儲存的資料和後設資料,並核對 Btrfs 儲存的校驗和。它用於發現讀錯誤、資料或後設資料校驗錯誤,不是:
|
|
如果結果為:
|
|
它只能說明本次讀取和校驗沒有發現異常,不能證明 btrfs-cleaner 的 zoned 清理路徑以後不會再次鎖死。
儲存結果:
|
|
Scrub 期間不要同時執行 rsync、rsnapshot、balance 或大量刪除。對這類備份盤,可以按實際使用強度每一至三個月執行一次。
為什麼沒有為了“回收空間”執行 balance
現場曾出現:
|
|
這個數字只表示已經分配的 Data block group 使用率,並不代表整塊盤只剩 1.33%。當時仍有數 TiB Device unallocated,並不缺少可繼續分配的空間。
因此沒有執行:
|
|
Balance 會重新定位 block group,而第一次故障正位於:
|
|
在問題核心上主動 balance,可能再次進入相近的 block-group 刪除和 zone 回收路徑。
如何處理數 TB 刪除與寫入
最有效的工作流調整是:有足夠空間時,先複製並校驗新資料,再刪除舊資料。
|
|
複製:
|
|
比較容量:
|
|
用內容校驗方式演練,不修改目標:
|
|
必須先刪除時,不要一次刪完數 TB 後馬上寫入。可以按日期、子目錄或容量分為 200~500GiB 一批:
|
|
btrfs filesystem sync 會提交檔案系統事務並喚醒相關清理工作,但命令返回不表示所有 block group 和 zone reclaim 已全部完成。
如果刪除的是 Btrfs 子卷或快照,還可以執行:
|
|
它等待已提交刪除請求的子卷完成清理,不適用於普通 rm -rf 刪除。
每批後檢查:
|
|
滿足以下條件後再繼續:
btrfs filesystem sync正常返回;- 沒有 Btrfs 執行緒處於
D狀態; - 核心日誌沒有新的 blocked、hung、abort 或 Btrfs error;
- 磁碟活動已經明顯回落;
Device zone unusable沒有快速增長。
Device zone unusable 不需要降到 0。它可能在未達到下一輪迴收條件時停留在幾十 GiB,這本身不等於檔案系統仍然卡住。
寫入也可以分批,並限制優先順序和頻寬:
|
|
--bwlimit=100M 只是保守起點。限速不能修復核心 bug,但可以減少 cleaner、事務提交、writeback 和 zone activation 同時處於高負載的機率。不要併發執行多個大型 rsync。
監控 zoned 回收指標
先獲得檔案系統 UUID:
|
|
檢視 data、metadata 和 system 的空間及回收指標:
|
|
其中:
bytes_pinned:通常要等當前事務提交後才能釋放的空間;bytes_zone_unusable:已失效但尚不能直接複用的 zoned 空間;reclaim_count:累計回收嘗試次數;reclaim_bytes:累計回收位元組數;reclaim_errors:累計回收錯誤數。
還可以檢視事務提交統計:
|
|
如果 max_commit_ms 持續達到幾十秒或數百秒,或者日誌再次出現:
|
|
應停止備份任務並儲存日誌,不要繼續灌入資料等待它自行恢復。
禁止備份任務重疊
所有 rsnapshot 週期使用同一個鎖:
|
|
weekly 和 monthly 也使用:
|
|
這樣可以避免 daily、weekly、monthly 與大量 rsync 同時進行。Scrub、大量刪除和大型複製也應該安排在不同時間段。
第二次故障:事務中止並被強制只讀
第一次故障重啟後,檔案系統可以正常掛載,device stats 也沒有錯誤。但之後再次寫入大量資料時,出現了更明確的事務失敗:
|
|
執行環境仍是:
|
|
這次不是“暫時卡住”,而是事務已經 aborted,核心為保護檔案系統主動切換成只讀。-11 對應 EAGAIN,但只憑 errno 不能確定觸發它的具體程式碼。
當時的裝置統計仍全部為 0:
|
|
空間狀態也不是整盤寫滿:
|
|
這組證據更支援 zoned Btrfs 內部寫入、chunk 分配或事務邏輯異常,而不是普通介質 I/O 錯誤或整盤 ENOSPC。但在沒有完整首條錯誤和對應原始碼分析前,不應把原因斷言為某一個補丁。
forced readonly 後的正確處理
不要嘗試:
|
|
先停止寫入任務:
|
|
儲存故障前後完整日誌。真正導致事務中止的第一條錯誤可能出現在 forced readonly 之前幾十行:
|
|
確認掛載選項:
|
|
其中應出現 ro。隨後正常卸載並重啟:
|
|
若提示 busy:
|
|
停止列出的普通使用者程序後再卸載。
檢查未完成的複製
事務中止前已經寫入的部分資料不能直接當作完整備份。可能出現:
- 檔案尚未建立;
- 檔案長度不完整;
- 資料寫入了,但目錄項或時間戳沒有提交;
- 最後一個事務內的改動被回滾。
因此必須保留源盤。切換到穩定環境並重新讀寫掛載後,用原 rsync 補齊:
|
|
再用校驗模式驗證:
|
|
沒有輸出才表示 rsync 沒有發現需要更新的內容。
核心方案:先區分測試與長期使用
同一臺機器、同一個 7.0.0-28-generic、同一塊 HC620 已連續出現:
btrfs-cleaner與 transaction 執行緒鎖死;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 元包:
|
|
確認系統和核心:
|
|
關鍵是 uname -r 以 6.8. 開頭,不要把某個補丁版本永久寫死。
查詢當前倉庫,而不是照抄本文的歷史版本:
|
|
如果測試目標是 GA 6.8,不要讓 linux-generic-hwe-24.04 把系統帶回另一條 HWE 核心線。
在 Ubuntu 24.04 短期測試 6.17
先查詢當前倉庫是否仍提供和維護該包:
|
|
確認包仍可用後,明確安裝:
|
|
確認檔案:
|
|
第一次只啟動一次。下面的完整版本必須替換成機器實際安裝的版本:
|
|
重啟後檢查:
|
|
不應在 Ubuntu 26.04 中新增 24.04 Noble 倉庫來強裝這套核心,否則可能引入 firmware、DKMS、initramfs 和後續 apt 維護問題。
測試 7.1 Mainline 時的邊界
Mainline 包適合驗證“上游新核心是否改變故障表現”,不等於 Ubuntu 官方生產核心。安裝前必須檢查:
|
|
如果 Secure Boot 已啟用、存在關鍵 DKMS 模組或伺服器沒有帶外控制檯,測試風險會明顯增加。具體版本、檔名和校驗值應從當前 Ubuntu Mainline Kernel Archive 重新獲取,不應複製過期下載地址。
保留當前可啟動核心,透過 GRUB 一次性啟動新核心。啟動後檢查:
|
|
新核心的首次掛載與測試
無論測試 6.8、6.17 還是更新核心,先保留 /etc/fstab 中的 noauto,啟動後只讀掛載:
|
|
沒有新錯誤後:
|
|
逐步測試:
|
|
不能因為小批次透過,就立即用數 TB 壓力證明“已經修復”。至少需要多輪、大量資料和較長執行時間才能提高信心。
是否重新格式化 Btrfs
重新格式化可以得到乾淨檔案系統,但不能單獨修復核心 zoned 路徑問題。兩次異常都發生在執行時的 block-group、zone 和事務路徑,且 device stats 沒有介質錯誤。
只有遇到以下情況時,重建檔案系統才更有意義:
btrfs check --readonly發現明確結構錯誤;- scrub 持續出現無法修復的校驗錯誤;
- 無法正常掛載或持續發生 generation 錯誤;
- 已決定調整資料/後設資料 profile;
- 準備更換檔案系統。
即使要重建,也應先完成另一份可驗證的備份,而不是把格式化當作試錯命令。
Btrfs 與 F2FS 怎麼選
F2FS 可以繞開這次出現的 Btrfs 特有呼叫路徑:
|
|
但它不是“沒有問題”的方案。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 清單儲存到另一塊盤:
|
|
以後驗證:
|
|
硬碟自身 ECC、壞扇區重對映和 SATA CRC 仍然會工作,但它們不能覆蓋應用、記憶體、檔案系統、控制器和磁碟之間的完整端到端鏈路。Btrfs 校驗和與硬碟 ECC 是互補關係,不是重複功能。
最後的處理原則
這次排障形成了以下實際規則:
- 不再用
7.0.0-28-generic對這塊 HC620 做大規模讀寫; - 保留源資料和至少一份獨立副本;
- 先複製、校驗,再分批刪除;
- 數 TB 操作拆成 200~500GiB 批次;
- rsync、rsnapshot、scrub、大量刪除和 balance 不重疊;
- 出現
D狀態或 hung-task 日誌後立即停止寫入; - forced readonly 後不強行 remount,rw;
- 不使用
btrfs check --repair進行盲目嘗試; - 測試核心必須保留回退項,並從只讀掛載開始;
- 長期可靠性首先來自多副本,其次才是檔案系統選擇。
整個故障鏈可以概括為:
|
|
這組結果足以說明當前“HC620 + Btrfs zoned + 7.0.0-28 + 大刪大寫”組合不適合繼續承擔無人值守的大規模任務,但還不足以證明所有 HC620、所有 Btrfs zoned 或所有 7.0 核心都有同一問題。