使檔案系統可匯出¶
概述¶
所有檔案系統操作都需要一個 dentry(或兩個)作為起點。本地應用程式透過開啟的檔案描述符或 cwd/root 持有對合適 dentry 的引用計數。然而,透過 NFS 等遠端檔案系統協議訪問檔案系統的遠端應用程式可能無法持有這樣的引用,因此需要另一種方式來引用特定的 dentry。由於備用引用形式需要跨重新命名、截斷和伺服器重啟(以及其他事項,儘管這些往往是最成問題的)保持穩定,因此沒有像“檔名”這樣簡單的答案。
本文討論的機制允許每個檔案系統實現指定如何為任何 dentry 生成一個不透明(在檔案系統外部)的位元組字串,以及如何為任何給定的不透明位元組字串找到合適的 dentry。這個位元組字串將被稱為“檔案控制代碼片段”,因為它對應於 NFS 檔案控制代碼的一部分。
支援檔案控制代碼片段和 dentry 之間對映的檔案系統將被稱為“可匯出的”。
Dcache 問題¶
dcache 通常包含任何給定檔案系統樹的合適字首。這意味著如果任何檔案系統物件位於 dcache 中,那麼該檔案系統物件的所有祖先也位於 dcache 中。由於正常訪問是透過檔名進行的,因此這個字首是自然建立並容易維護的(透過每個物件維護其父物件的引用計數)。
但是,當物件透過解釋檔案控制代碼片段包含到 dcache 中時,不會自動為該物件建立路徑字首。這導致了 dcache 的兩個相關但不同的特徵,這些特徵對於正常檔案系統訪問是不需要的。
dcache 有時必須包含不屬於合適字首的物件。即未連線到根目錄的物件。
dcache 必須為新找到的(透過 ->lookup)目錄做好準備,以便已經有一個(未連線的)dentry,並且必須能夠將該 dentry 移動到位(基於 ->lookup 中的父級和名稱)。這對於目錄尤其重要,因為目錄只有一個 dentry 是 dcache 的不變數。
為了實現這些功能,dcache 具有
一個 dentry 標誌 DCACHE_DISCONNECTED,它設定在任何可能不屬於合適字首的 dentry 上。這在建立匿名 dentry 時設定,並在注意到 dentry 是合適字首中 dentry 的子級時清除。如果設定了此標誌的 dentry 的引用計數變為零,則該 dentry 將立即被丟棄,而不是保留在 dcache 中。如果一個尚未位於 dcache 中的 dentry 透過檔案控制代碼重複訪問(正如 NFSD 可能做的那樣),則每次訪問將分配一個新的 dentry,並在訪問結束時丟棄。
請注意,這樣的 dentry 可以獲取子級、名稱、祖先等,而不會丟失 DCACHE_DISCONNECTED - 該標誌只有在子樹成功重新連線到根目錄時才會清除。在此之前,此類子樹中的 dentry 僅在存在引用時才會被保留;引用計數達到零意味著立即驅逐,與未雜湊的 dentry 相同。這保證了我們不需要在解除安裝時找到它們。
用於建立輔助根目錄的原語 - d_obtain_root(inode)。這些_不_帶有 DCACHE_DISCONNECTED。它們放置在每個超級塊列表 (->s_roots) 上,因此可以在解除安裝時找到它們以進行驅逐。
用於分配匿名 dentry 和幫助在查詢時附加鬆散目錄 dentry 的輔助例程。它們是
- d_obtain_alias(inode) 將返回給定 inode 的 dentry。
如果 inode 已經有一個 dentry,則返回其中一個。
如果沒有,則分配並附加一個新的匿名 (IS_ROOT 和 DCACHE_DISCONNECTED) dentry。
對於目錄,會注意確保只能附加一個 dentry。
- d_splice_alias(inode, dentry) 將一個新 dentry 引入樹中;
如果合適,則傳入的 dentry 或給定 inode 的預先存在的別名(例如 d_obtain_alias 建立的匿名別名)。當使用傳入的 dentry 時,它返回 NULL,遵循 ->lookup 的呼叫約定。
檔案系統問題¶
為了使檔案系統可匯出,它必須
提供下面描述的檔案控制代碼片段例程。
確保在使用 ->lookup 查詢給定父級和名稱的 inode 時使用 d_splice_alias 而不是 d_add。
如果 inode 為 NULL,則 d_splice_alias(inode, dentry) 等效於
d_add(dentry, inode), NULL類似地,d_splice_alias(ERR_PTR(err), dentry) = ERR_PTR(err)
通常 ->lookup 例程將簡單地以
return d_splice_alias(inode, dentry); }
檔案系統實現透過設定 struct super_block 中的 s_export_op 欄位來宣告檔案系統的例項是可匯出的。此欄位必須指向一個“struct export_operations”結構,該結構具有以下成員
- encode_fh (必需)
接受一個 dentry 並建立一個檔案控制代碼片段,該片段稍後可用於查詢或建立同一物件的 dentry。
- fh_to_dentry (必需)
給定一個檔案控制代碼片段,這應該找到隱含的物件併為其建立一個 dentry(可能使用 d_obtain_alias)。
- fh_to_parent (可選但強烈建議)
給定一個檔案控制代碼片段,這應該找到隱含物件的父級併為其建立一個 dentry(可能使用 d_obtain_alias)。如果檔案控制代碼片段太小,可能會失敗。
- get_parent (可選但強烈建議)
當給定一個目錄的 dentry 時,這應該返回父級的 dentry。很有可能父級 dentry 將由 d_alloc_anon 分配。預設的 get_parent 函式只返回一個錯誤,因此任何需要查詢父級的檔案控制代碼查詢都會失敗。 ->lookup(“..”) _不_用作預設值,因為它會在 dcache 中留下“..”條目,這些條目太混亂而無法使用。
- get_name (可選)
當給定父級 dentry 和子級 dentry 時,這應該在由父級 dentry 標識的目錄中找到一個名稱,該名稱指向由子級 dentry 標識的物件。如果沒有提供 get_name 函式,則會提供一個預設實現,該實現使用 vfs_readdir 查詢潛在的名稱,並匹配 inode 編號以找到正確的匹配項。
- flags
某些檔案系統可能需要以不同於其他檔案系統的方式處理。 export_operations 結構還包括一個 flags 欄位,允許檔案系統將此類資訊傳達給 nfsd。有關更多說明,請參見下面的“匯出操作標誌”部分。
檔案控制代碼片段由一個或多個 4 位元組字的陣列和一個位元組的“型別”組成。 decode_fh 例程不應依賴於傳遞給它的大小。此大小可能大於 encode_fh 生成的原始檔案控制代碼,在這種情況下,它將被 nuls 填充。相反,encode_fh 例程應選擇一個“型別”,該型別指示 decode_fh 檔案控制代碼的多少有效,以及應如何解釋它。
匯出操作標誌¶
除了操作向量指標之外,struct export_operations 還包含一個“flags”欄位,允許檔案系統向 nfsd 傳達它可能希望在處理它時以不同的方式執行操作。定義了以下標誌
- EXPORT_OP_NOWCC - 在此檔案系統上停用 NFSv3 WCC 屬性
RFC 1813 建議伺服器始終在每次操作後將弱快取一致性 (WCC) 資料傳送給客戶端。伺服器應原子地收集有關 inode 的屬性,對其執行操作,然後收集之後的屬性。這允許客戶端在某些情況下跳過發出 GETATTR,但意味著伺服器幾乎為所有 RPC 呼叫 vfs_getattr。在某些檔案系統上(特別是那些叢集或聯網的檔案系統),這很昂貴,並且難以保證原子性。此標誌指示 nfsd 在對此檔案系統執行操作時,應跳過在 NFSv3 答覆中向客戶端提供 WCC 屬性。考慮在具有昂貴的 ->getattr inode 操作的檔案系統上啟用此功能,或者在無法保證操作前後屬性收集之間的原子性時啟用此功能。
- EXPORT_OP_NOSUBTREECHK - 不允許在此 fs 上進行子樹檢查
許多 NFS 操作處理檔案控制代碼,伺服器必須對其進行審查以確保它們位於匯出的樹內。當匯出由整個檔案系統組成時,這很簡單。 nfsd 只需確保檔案控制代碼位於檔案系統上。但是,當僅匯出檔案系統的一部分時,nfsd 必須遍歷 inode 的祖先以確保它在匯出的子樹中。這是一項昂貴的操作,並非所有檔案系統都能正確支援它。此標誌使檔案系統免於子樹檢查,並導致 exportfs 在嘗試在其上啟用子樹檢查時返回錯誤。
- EXPORT_OP_CLOSE_BEFORE_UNLINK - 在取消連結之前始終關閉快取檔案
在某些可匯出的檔案系統(如 NFS)上,取消連結仍在開啟的檔案可能會導致大量額外工作。例如,NFS 客戶端將執行“sillyrename”以確保檔案在仍然開啟時保持不變。重新匯出時,該開啟的檔案由 nfsd 持有,因此我們通常最終會執行 sillyrename,然後在連結計數實際變為零之後立即刪除 sillyrenamed 檔案。有時,此刪除操作可能會與其他操作(例如父目錄的 rmdir)競爭。此標誌使 nfsd 在呼叫 vfs 執行取消連結或重新命名操作(該操作將替換現有檔案)之前,關閉此 inode 的任何開啟檔案。
- EXPORT_OP_REMOTE_FS - 此檔案系統的後備儲存是遠端的
PF_LOCAL_THROTTLE 存在於環回 NFSD 中,其中一個執行緒需要寫入一個 bdi(最終 bdi)才能釋放排隊到另一個 bdi(客戶端 bdi)的寫入。此類執行緒會獲得髒頁的私有平衡,以便客戶端 bdi 的髒頁不會影響寫入最終 bdi 的守護程式。對於持久儲存不是本地的檔案系統(例如匯出的 NFS 檔案系統),此約束具有負面影響。 EXPORT_OP_REMOTE_FS 允許匯出停用回寫限制。
- EXPORT_OP_NOATOMIC_ATTR - 檔案系統不原子更新屬性
EXPORT_OP_NOATOMIC_ATTR 指示匯出的檔案系統無法提供 NFSv4 的 change_info4 中“atomic”布林值所需的語義。此布林值向客戶端指示返回的操作前後更改屬性是否與請求的元資料操作(UNLINK、OPEN/CREATE、MKDIR 等)原子地獲取。
- EXPORT_OP_FLUSH_ON_CLOSE - 檔案系統在 close(2) 上重新整理檔案資料
在大多數檔案系統上,inode 在檔案關閉後可能會保持回寫狀態。 NFSD 依賴客戶端活動或本地重新整理執行緒來處理回寫。某些檔案系統(例如 NFS)會在最後一次關閉時重新整理 inode 的所有髒資料。以這種方式匯出的檔案應設定 EXPORT_OP_FLUSH_ON_CLOSE,以便 NFSD 知道在關閉此類檔案時跳過等待回寫。