LWN: Btrfs inode號問題的解決方案!
關(guān)注了就能看到更多這么棒的文章哦~
The Btrfs inode-number epic (part 2: solutions)
By Jonathan Corbet
August 23, 2021
DeepL assisted translation
https://lwn.net/Articles/866709/
本系列文章的上一篇探討了當包含 subvolume 的 Btrfs 文件系統(tǒng)通過 NFS export 使用時出現(xiàn)的困境。Btrfs 有幾個很特別的行為,使得這種情況下的處理變得更復(fù)雜:為 subvolume 使用了單獨的設(shè)備號,以及整個文件系統(tǒng)并沒有進行唯一 inode 編號。最近,Neil Brown 開始致力于解決這些問題,結(jié)果發(fā)現(xiàn)情況比預(yù)料中的還要困難,需要進行很多嘗試。
Take 1: internal mounts
Brown 的第一組 patch set 打算創(chuàng)造一個 "internal mounts" 的概念來解決這些問題。Btrfs 會為用戶可見的每個 subvolume 自動創(chuàng)建所謂的 internal mount。這個自動 mount 機制就可以讓這些 mount 節(jié)點正常出現(xiàn)。進行一些調(diào)整之后,內(nèi)核的 NFS 服務(wù)器守護程序(nfsd)就可以識別出這些特殊的 mount,不需要 NFS 遠端顯式進行 mount 操作,就可以正常使用了。完成這個操作之后,系統(tǒng)顯示的設(shè)備號就和預(yù)期的保持了一致,并且 inode 號在 mount 中就是唯一確定的了。
乍一看,這套 patch set 似乎是解決問題的好辦法。早在 7 月份,當有人向他介紹這種方法時,文件系統(tǒng)開發(fā)者 Christoph Hellwig 回應(yīng)說。"這是我多年來一直想要的方案"。進行了這些改動之后,Btrfs 看起來就不那么奇怪了,一些長期存在的問題也終于得到了解決。
不過,這套 patch set 很快就遇到了麻煩。Al Viro 指出,用來查詢設(shè)備號的這個機制中,可能會在持有一個不允許進行 I/O 的鎖時觸發(fā) I/O 操作,從而使得系統(tǒng)死鎖。而如果沒有這個查詢機制,那么我們就無法從文件系統(tǒng)中獲得設(shè)備號。這里有個潛在的替代方案,那就是為了每個 internal mount 都單獨提供一個包含了所需信息 superblock,不過這個方案更糟糕。內(nèi)核的虛擬文件系統(tǒng)層中的許多操作都需要去遍歷所有 mount 上來的 superblock 的列表。為 Btrfs subvolume 增加數(shù)以千計的 superblock 會導(dǎo)致許多性能問題出現(xiàn),需要大量的改動才能解決。
此外,Amir Goldstein 指出,這種新增的 mount structure 可能會給 overlayfs 帶來麻煩。同時這也會導(dǎo)致他開發(fā)的一些用戶空間工具無法正常工作了。還有一個小問題,那就是所有這些 internal mount 將如何在/proc/mounts 中展示。在有大量 subvolume 的系統(tǒng)中,這會導(dǎo)致 /proc/mounts 變成了一個非常混亂的東西,同時也可能暴露出其他那些 private subvolume 的名稱。
Take 2: file handles
Brown 總結(jié)說:"在目前的框架下這個問題是無法解決的"。具體來說,問題在于為 inode 號預(yù)留的 64 位格式,對現(xiàn)在的 Btrfs 來說也是完全不夠的。這個問題在 overlayfs 中就變得更糟了,由于需要整合多個文件系統(tǒng)的 inode 號,那么最終產(chǎn)物的編號數(shù)量必然會大于任何一個文件系統(tǒng)中的數(shù)量。Brown 將目前 overlayfs 的解決方案簡述了一下:"它重用了這些高位 bit,寄希望于文件系統(tǒng)不會使用到",這個想法不太現(xiàn)實。但是,只要 inode 編號被限制在某個固定大小之內(nèi)的話,就沒有辦法解決這個問題,這是他的看法。
他繼續(xù)說,最好是使用許多文件系統(tǒng)中都提供的 file handle 文件句柄,這主要用于 NFS。某個文件的句柄可以通過 name_to_handle_at() 來獲得。句柄的 length 可以是任意數(shù)量,它包括了一個 generation number,這就很好地解決了文件被刪除時的 inode-number 被重復(fù)使用的問題。如果用戶空間使用 handle 句柄而不是 inode 號來判斷兩個文件是否相同,那么很多問題都會直接消失了。
當然,又會出現(xiàn)一些新的問題,那就是需要對用戶空間接口和程序要進行許多修改了。目前那些內(nèi)核 export 出來的文件(例如/proc 下的那些文件)都不使用 handle 句柄,所以必須先創(chuàng)建一組新文件,其中包含 handle 句柄。任何查看 inode 號的程序都必須要進行更改。這就會導(dǎo)致大量的用戶空間工具都被破壞。Brown 多次重申,他認為這樣做可能是可以接受的(而且是必要的):
如果你拒絕冒著破壞現(xiàn)有東西的風(fēng)險,那么你就無法取得進展。只要人們可以選擇好這個破壞會在何時發(fā)生,并且進行提前警告提醒,那么他們往往可以很好地應(yīng)付過來。
不過,導(dǎo)致兼容性問題的改動仍然是很難讓人們接受的。除此之外,要想讓這個改動達到全部目的,那么就還需要修改 Btrfs,停止使用人工生成的設(shè)備號用在 subvolume 上,這也是一個不小的改動。而且,正如 Viro 所指出的,兩個不同的文件句柄 handle 有可能會是指向同一文件的。
綜上所述,這種方法也沒有贏得勝利。
Take 3: mount options
Brown 的第三次嘗試則是從一個不同的方向來解決這個問題,那就是讓所有的改變都可以是明確選擇打開或者關(guān)閉的。具體來說,他為 Btrfs 文件系統(tǒng)增加了兩個新的 mount option,用來改變文件系統(tǒng)在 inode 和設(shè)備號這兩方面的行為。
第一個選項是 inumbits= ,它改變了 inode 號的顯示方式。默認值零,會導(dǎo)致使用 internal object ID(正如目前 Btrfs 的情況)。而非零值就是告訴 Btrfs 要去生成 "大部分情況下是唯一的" 并且能符合指定的 bit 數(shù)限制的 inode 號。具體來說,為了在 subvolume 中為某個特定對象生成 inode 號的時候,Btrfs 會這樣做:
根據(jù) subvolume 號來生成一個 "overlay" 值,這是通過對 inode 編號進行 byte-swapping 來得到的,這樣一來這些 low-order bit(在各個 subvolume 之間差異最大的那些 bit)就在最高位了(most-significant bit)。
overlay 值會被右移,從而適應(yīng) inumbits= 所指定的位數(shù)。如果這個參數(shù)是 64 的話就不需要進行移位了。
然后,用 overlay 值與 object number 進行 XOR,從而生成用戶空間最終看到的 inode 編號。
這樣生成的 inode 編號在 subvolume 內(nèi)部仍然是唯一的,而在一個規(guī)模更大的 Btrfs 文件系統(tǒng)中,雖然仍然可能會出現(xiàn)編號碰撞(也就是出現(xiàn)相同的 inode 號),但出現(xiàn)概率會更加小。設(shè)置 inumbits=64 的話就可以最大限度地減少 inode 號重復(fù)的可能,但是在一些場景下(比如 overlayfs 場景)最高位的這幾個 bit 會被其他子系統(tǒng)用到,因此更應(yīng)該使用一個比較小的數(shù)量(比如 56)。
第二個 mount option 是 numdevs= ,它用來控制文件系統(tǒng)中會使用多少個設(shè)備號來代表 subvolume。默認值是 many,也就是仍然保留原來的行為,為每個 subvolume 都分配一個單獨的設(shè)備號。而設(shè)置 numdevs=1 的話則會使所有 subvolume 都共用同一個設(shè)備號。如果使用這個選項 mount 了一個文件系統(tǒng)的時候,像 find 和 du 這樣的工具就會無法檢測出跨越了 subvolume 邊界的情況,所以那些工具中原有的一些選項本來可以將范圍限制在單一文件系統(tǒng)之內(nèi)的,現(xiàn)在就無法生效了。同樣也可以指定 numdevs=2,這樣在從一個 subvolume 移動到下一個 subvolume 時,會替換成另一個設(shè)備號。總共有兩個設(shè)備號會接替使用。這就使得 find 這樣的工具能如預(yù)期正常工作了。
最后,這組 patch set 還增加了一個 "tree ID" 的概念,可以通過 statx() 系統(tǒng)調(diào)用來獲取到。Btrfs 會用 subvolume ID 來填入這個查詢的反饋之中,然后應(yīng)用程序就可以用它來可靠地確定兩個文件是否包含在同一個 subvolume 之中了。
Btrfs 開發(fā)者 Josef Bacik 將這項工作描述為 "朝著正確方向邁出的一步",但他說更希望看到一個不需要特殊掛載選項的解決方案。"mount options 總是很混亂的,而且經(jīng)常會導(dǎo)致發(fā)行版在還不了解它的含義的情況下就打開這些選項,然后我們就不得不永遠保持支持了"。他認為一個適當?shù)慕鉀Q方案就不應(yīng)該給用戶提供可能會做出錯誤決定的機會。他建議在 nfsd 中使用新增的 tree ID 來來解決 NFS 的具體問題,如果需要的話,也可以自己生成新的 inode 號。
Brown 反駁說,如果不增加 mount 選項的話,還不如直接創(chuàng)建一個新的文件系統(tǒng)類型("btrfs2 或者也許是 betrfs"),來直接使用新的行為。不過 Bacik 也同樣不喜歡這個主意。Brown 補充說,他不希望在 nfsd 中對 Btrfs 的 inode 號進行一些 "神奇的轉(zhuǎn)換" 操作。如果一個文件系統(tǒng)需要這樣的操作,那么就應(yīng)該在文件系統(tǒng)本身內(nèi)來完成,這是他的觀點。然后他要求 Btrfs 開發(fā)者們決定一下他們解決這個問題的首選方式方式是什么,但沒有得到答案。
Take 4: the uniquifier
8 月 13 日,Brown 又回來了,這次提供了一個非常小的 patch,旨在解決引發(fā)整個討論的 NFS 問題。這個 patch 會使文件系統(tǒng)能提供一個與文件相關(guān)的 "uniquifier "值。這個名字怎么聽起來更加像是個職業(yè)摔跤手的名字。。。此值只可以在內(nèi)核中使用。然后 NFS 服務(wù)器可以將該值與文件的 inode 號進行 XOR 操作,得到一個更可能是唯一的數(shù)字。Btrfs 會相應(yīng)地提供上述的 overlay 值來作為這個 uniquifier。nfsd 使用這個值的話,問題(基本上)就消失了。
Bacik 說,這種方法是 "合理的",并代表 Btrfs 文件系統(tǒng)提供了 ack。這樣看來,它終于可以成為當前問題的解決方案了?;蛘咧辽倏梢哉f它更接近最終方案了。Brown 后來意識到,在過渡階段,這個改動之后的 inode 號會導(dǎo)致當前客戶端上出現(xiàn)人們十分畏懼的 "stale file handle "錯誤。后來他更新了一下 patch set,在 NFS 文件句柄的一個未使用的字節(jié)中增加了一個新的 flag,表示這種 "new-style" inode 號,從而防止出現(xiàn)上述錯誤。
第四次方案的第二個版本可能確實可以讓 Btrfs 用戶的一些 NFS 相關(guān)問題消失了。但這種做法還是像是一種 hack 方式,因為它在內(nèi)部進行了一些 magic number 處理,并且仍然無法保證能創(chuàng)造出唯一的 inode 號。事實上,在談到這項工作時,Brown 甚至自己也稱之為 "這是一種為了繞過文件系統(tǒng)中的錯誤功能而提供的 hack 做法"。不過,在應(yīng)對生產(chǎn)系統(tǒng)和大量用戶群體時,這類 hack 的解決方式可能就是生活的一部分了。如果不破壞用戶系統(tǒng),就不可能有更干凈、更正確的解決方案了。因此,在出現(xiàn)其他一些嚴重到足以迫使人們接受一個更具破壞性的解決方案之前,這個 uniquifier 方案可能是大家可以得到的最好結(jié)果了。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。
長按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開源社區(qū)的各種新近言論~
