Linux 核心 buffer_head 大掃除:拆除老舊元資料追蹤,結構縮小 8 位元組
Linux Kernel / torvalds/linux · 2026-06-17
SUSE 核心工程師 Jan Kara 在 2026 年 3 月至 6 月間,向 linux-fsdevel 提交了一系列修補,系統性地移除 Linux 核心中長期存在的「通用元資料 buffer_head 追蹤」基礎設施。這套機制橫跨十餘個老舊檔案系統,實際使用它的極少,卻讓每條掛載路徑都要負擔不必要的額外開銷。LWN.net 於 2026 年 6 月 17 日整理了這項工作的進展,修補已由 Christian Brauner 合入主線,預計隨 Linux 7.2 合併視窗正式推進。
問題根源:全員買單,受益者卻寥寥無幾
struct buffer_head 是 Linux 區塊 I/O 層的基本單位,負責追蹤一個磁碟區塊在頁面快取中的對應關係。歷史上,VFS 層提供了一套「通用元資料追蹤」機制,讓檔案系統可以在 inode 上掛載一份 dirty buffer_head 清單,由核心的 inode_lru_isolate() 在回收 inode 時一併處理。然而 Kara 在提交訊息中直接點明:「只有極少數檔案系統真正使用這套通用元資料 buffer_head 追蹤,但每個人都在付出這份開銷。」 受影響的老式檔案系統包括 AFFS(Amiga Fast File System)、BFS(Boot File System)、ext2、Minix 等。現代主流的 ext4、XFS、Btrfs 早已有各自的元資料管理路徑,從未依賴這套共用機制。
三層改動逐步拆解耦合
此系列修補分三個層次進行。第一層是讓仍在使用通用追蹤的各個檔案系統,在 evict_inode() 中自行負責沖洗(sync)與作廢(invalidate)元資料緩衝區,確保 inode 回收時不遺留髒緩衝,例如提交 23dae9e(AFFS)與 4a7fd18(BFS)均新增對 sync_mapping_buffers(&inode->i_data) 的呼叫。以下是 BFS 的修改示意:
/* bfs_evict_inode(),確保 inode 回收時不遺留髒緩衝 */
static void bfs_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
if (inode->i_nlink)
sync_mapping_buffers(&inode->i_data); /* 新增:自行沖洗 */
invalidate_inode_buffers(inode);
clear_inode(inode);
}第二層是清理 fs/libfs.c 中的 __generic_file_fsync()(提交 aec4fe7):由於呼叫此函式的所有檔案系統都不再使用元資料 buffer_head 追蹤,sync_mapping_buffers() 的呼叫從此處移除,連同對 <linux/buffer_head.h> 的引用一起刪掉。結果是 libfs.c 不再依賴 buffer_head 標頭,減少一個跨子系統的隱性耦合,總計淨刪除 7 行、新增 2 行。
第三層則由 Matthew Wilcox(Oracle)完成:在 Kara 的基礎上,從 struct buffer_head 移除 b_end_io 函式指標欄位(提交 ac75b922)。這個欄位原本供非同步 I/O 完成回呼使用,但隨著 buffer_head 作為 block device I/O 主路徑的角色式微,b_end_io 已無實際使用者,移除它的代價只是同步更新四個相關檔案:include/linux/buffer_head.h、fs/buffer.c、drivers/md/raid5.h 與 Documentation/filesystems/locking.rst。
結構縮減帶來的記憶體紅利
移除 b_end_io(一個 8 位元組的函式指標)使 struct buffer_head 從 104 位元組縮小至 96 位元組。在標準 Debian 核心配置下,每個 4 KiB slab 頁面可容納 42 個 buffer_head 物件,較過去的 39 個增加約 7.7%,等同於在相同記憶體內多放了近一成的緩衝。在大量使用小型 buffer_head 的工作負載下(例如掛載大量磁碟分割、在嵌入式系統上運行 ext2 或 BFS),這個改善可直接轉化為更低的記憶體佔用。
此外,移除 inode_lru_isolate() 內的特殊分支(remove_inode_buffers(),共 29 行刪除自 fs/buffer.c),也簡化了 inode 回收的熱路徑邏輯。Kara 坦承,對於仍保留通用追蹤的老式檔案系統而言,inode 回收路徑略為減緩是可能的取捨,但這些檔案系統不用於性能敏感的環境,整體判斷因此合理。
長遠意義:為 folio 遷移清路
buffer_head 的地位在近年核心開發中持續下降。Matthew Wilcox 主導的 struct folio 計畫意在以更大粒度的記憶體管理單元取代 page/buffer_head 的組合,清除不必要的 buffer_head 機制是讓 folio 遷移更乾淨的前提工作之一。此次 Kara 的系列修補拆掉了 VFS 與老式檔案系統之間一條隱性的依賴鏈,讓未來進一步簡化 block I/O 路徑的空間更大,也是 Linux 7.x 系列持續向前推進核心架構現代化的具體體現。
原始來源:linux/commit/aec4fe7 — Drop sync_mapping_buffers() from __generic_file_fsync()、linux/commit/972b9dd — Ignore inode metadata buffers in inode_lru_isolate()、linux/commit/ac75b92 — buffer: Remove b_end_io
Firefox 151 換上 Rust 版 zlib:效能暴增三十倍背後的 Intel CPU 硬體坑
Trifecta Tech Foundation · 2026-06-16
從 Firefox 151.0.0 起,Mozilla 將瀏覽器的 gzip 解壓縮路徑切換至 zlib-rs——一個以 Rust 重寫、對外暴露相同 C ABI 的 zlib drop-in replacement。切換後,64 KB 至 1 MB 檔案的單次解壓縮速度提升了 25 至 32 倍;然而整合過程中也踢到了一個來自 Intel 第 13、14 代(Raptor Lake)處理器的硬體 bug,並留下了一段教科書級的 unsafe Rust 偵錯記錄。這項工程由 Trifecta Tech Foundation 的 Folkert de Vries 主導,Mozilla 工程師 Mike Hommey 與 Gabriele Svelto 協力完成,從最初接洽到正式上線歷時約兩年。
整合的兩大前置問題
zlib-rs 在設計上以相容性為優先,但並非位元完全相同(bit-identical)的替代品。它在不同壓縮等級下採用與 zlib-ng 對齊、但不同於 stock zlib 的演算法,導致輸出位元組的長度可能略有差異。Firefox 測試套件中原本驗證「輸出與 zlib 完全一致」的測試案例,需要逐一放寬為只驗證解壓縮正確性,而不檢查位元組序列本身。
另一個工程問題是符號命名衝突。Firefox 的建置系統要求所有 zlib 符號加上 MOZ_Z_ 前綴(例如 inflate 改為 MOZ_Z_inflate),以防止與系統 zlib 產生連結衝突。zlib-rs 已在現有的 C 符號匯出機制中支援前綴設定,此部分整合順利,未成為主要障礙。
撞上 Intel Raptor Lake 硬體 bug
整合上線後不久,遙測數據顯示 Intel 13th/14th 代(Raptor Lake)處理器的用戶出現崩潰。日誌顯示的是「邏輯上不可能失敗的 bounds check 失敗」——一個 512 元素的陣列被以超出範圍的索引(如 567)存取並觸發 Rust panic。這正是 Rust 安全機制的價值所在:同樣的情境若是 C 實作,只會靜默地寫入錯誤的記憶體位置,幾乎不可能被即時察覺。
Gabriele Svelto 最終確認根源是已知的 Raptor Lake 硬體不穩定問題。問題指令位於 Huffman 編碼的熱路徑,Rust 程式碼在編譯後由 LLVM 生成如下機器碼:
mov byte ptr [rsi + rdi + 1], ch這條涉及 8-bit 子暫存器(ch)的 mov 指令,在特定情境下會觸發 Raptor Lake 的硬體瑕疵,使寫入的值偶發性地出錯並破壞壓縮緩衝區的狀態。修復方案由 Mike Hommey 提出,以一個最小的 unsafe 區塊強制 LLVM 改用 16-bit 對齊寫入,繞過問題指令:
unsafe { buf.as_mut_ptr().cast::<[u8; 2]>().write_unaligned(bytes) }此 workaround 已上游至 zlib-rs(PR #520),並落地於 Firefox commit 711ef51645a2(Cargo.toml patch 指向修正版本)。當 zlib-rs 最低支援的 LLVM 版本升至 LLVM 23 後,新版編譯器已不再生成該問題指令,此段程式碼屆時可安全移除。
效能數字:x86-64 上的大幅提升
zlib-py 基準測試在 x86-64 平台上量測的結果如下:
| 模式 | 檔案大小 | 速度提升(相對 stock zlib) |
|---|---|---|
| 單次解壓縮 | 1 KB | 5.66× |
| 單次解壓縮 | 64 KB | 32.50× |
| 單次解壓縮 | 1 MB | 26.61× |
| 串流解壓縮 | 10 MB | 8.89× |
ARM64 平台(尤其是 Apple Silicon 的 macOS)的提升幅度相對保守,原因是 Apple 隨 macOS 附帶的 libz 已深度以 inline assembly 優化,zlib-rs 的純 Rust 實作在此平台難以超越。在 Intel/AMD x86-64 硬體上效益最為顯著,而這正是 Firefox 桌面版用戶的主力平台。
安全性作為副產品
此次切換並非以安全為主要訴求,卻在偵錯過程中展示了 Rust 安全保證的實際價值。Raptor Lake bug 在 C 版 zlib 上不會崩潰,只會靜默地寫入錯誤資料,幾乎不可能被及時發現。Rust 的 bounds check 讓問題立即以 panic 形式暴露,顯著縮短了從硬體異常到根本原因確認的時間。Trifecta Tech Foundation 預計繼續把 zlib-rs 推進更多下游使用者,Firefox 151.0.0 是這個過程中規模最大的一次生產環境驗證。
原始來源:Trifecta Tech Foundation — zlib-rs in Firefox、Mozilla Bug 1950764、zlib-rs PR #520、Firefox commit 711ef51645a2
Linux 7.1 正式發布:驅動修正收尾,7.2 合併視窗接棒開啟
LWN.net · 2026-06-14
Linus Torvalds 於 2026 年 6 月 14 日(星期日)在旅途中發布了 Linux 7.1,這是 7.x 系列的第一個穩定點版本。整體而言,此次發布以 GPU 驅動、網路子系統及音效驅動的修正為主,並修補了 USB serial 驅動中的 heap overflow 安全漏洞,符合穩定週期末段的慣例。Torvalds 在公告中表示「沒有什麼特別令人擔憂的地方」,發布次日隨即開啟了 7.2 的合併視窗。
7.1 穩定週期的修正範疇
根據 LWN.net 的報導,7.1 穩定週期內合入約 200+ 筆提交,遠少於合併視窗的數千筆,符合 -rc 後段歷史模式。GPU 驅動(DRM/KMS)接收了最大量的修補,涵蓋 AMD、Intel、Nouveau 等驅動的穩定性問題;網路子系統有多處 TCP/IP 堆疊與乙太網路驅動修正;音效子系統(ALSA)收到 codec 相容性修正。
安全性方面,本版修補了 USB serial 驅動的 heap overflow 漏洞,以及記憶體管理層的若干邊界情境錯誤。這些修正性質屬於點狀修補,不影響核心架構,但對下游 Linux 發行版的穩定性具有直接意義。LWN.net 同期(2026-06-15)發布的開發統計文章(/Articles/1077425/)指出,7.1 週期的貢獻來源仍以大型硬體廠商(AMD、Intel、NVIDIA)的驅動工程師為主,其次是各大發行版維護者與獨立貢獻者。
7.2 合併視窗的技術方向
隨著 7.1 落地,7.2 合併視窗隨即開啟,社群關注的幾項工作已陸續就緒。Jan Kara 的 buffer_head 清理系列(本期另一篇報導已詳細介紹)正是在這個視窗前後合入,象徵核心 I/O 抽象層的持續瘦身。Matthew Wilcox 持續推進的 struct folio 遷移也預計在此視窗取得進展,buffer_head 結構縮小 8 位元組(提交 ac75b922)是這條路線的最新成果。
overlayfs 與 composefs 的整合工作(Amir Goldstein 的提案,見 LWN /Articles/1077052/)也是 7.2 視窗的潛在亮點之一,涉及聯合掛載(union mount)語義的現代化。Torvalds 在 7.1 公告中提及因旅途中網路有限而對視窗開啟時程略有不確定,但最終維持了固定的週日節奏,凸顯核心開發流程高度依賴個人時間承諾的文化慣例。下一個穩定點版本 Linux 7.2 預計於八至十週後發布。
版本號跨越 6.x 的技術意涵
Linux 7.x 系列自 7.0 起在版本號上跨越了 6.x 的慣例,但 Torvalds 一貫強調版本號僅是計數器,不代表任何特定的架構里程碑或 ABI 邊界。7.0 的版本號跳升純粹是因為 6.x 子版本號已累積至一個令 Torvalds 感覺「數字太大」的程度,與技術上的重大轉折無關。從 6.15 跳至 7.0 的決定,延續了過去 2.6 跳 3.0、3.x 跳 4.0 的慣例,是核心開發社群的一種非正式審美選擇。
原始來源:LWN.net — The 7.1 kernel has been released、LWN.net — Development statistics for the 7.1 kernel