RPC 快取¶
本文件簡要介紹了 sunrpc 層中的快取機制,該機制專門用於 NFS 身份驗證。
快取¶
快取取代了舊的匯出表,並允許快取各種各樣的值。
存在許多結構相似的快取,儘管它們的內容和用途可能大相徑庭。 有一組用於管理這些快取的通用程式碼。
可能需要的快取示例包括
從 IP 地址到客戶端名稱的對映
從客戶端名稱和檔案系統到匯出選項的對映
從 UID 到 GID 列表的對映,以解決 NFS 16 個 gid 的限制。
對於沒有統一 uid 分配的站點,在本地 UID/GID 和遠端 UID/GID 之間進行對映
從網路標識到加密身份驗證的公鑰的對映。
通用程式碼處理以下事項,例如
使用正確的鎖定進行常規快取查詢
支援“NEGATIVE”以及肯定條目
允許快取項具有 EXPIRED 時間,並在快取項過期且不再使用時將其刪除。
向用戶空間發出請求以填寫快取條目
允許使用者空間直接設定快取中的條目
延遲依賴於尚未完成的快取條目的 RPC 請求,並在快取條目完成時重放這些請求。
在舊條目過期時清除它們。
建立快取¶
快取需要儲存資料。 這是一個結構定義的形式,該定義必須包含 struct cache_head 作為一個元素,通常是第一個。它還將包含一個鍵和一些內容。每個快取元素都經過引用計數,幷包含用於快取管理的過期和更新時間。
快取需要一個描述快取的“cache_detail”結構。 這儲存雜湊表、一些用於快取管理的引數以及一些詳細說明如何處理特定快取項的操作。
這些操作是
- struct cache_head *alloc(void)
這只是分配適當的記憶體並返回指向嵌入在結構中的 cache_detail 的指標
- void cache_put(struct kref *)
當對某個專案的最後一個引用被刪除時,將呼叫此函式。 傳遞的指標是指向 cache_head 中的“ref”欄位。 cache_put 應該釋放由“cache_init”建立的任何引用,如果設定了 CACHE_VALID,則釋放由 cache_update 建立的任何引用。 然後它應該釋放由“alloc”分配的記憶體。
- int match(struct cache_head *orig, struct cache_head *new)
測試兩個結構中的鍵是否匹配。 如果匹配,則返回 1,如果不匹配,則返回 0。
- void init(struct cache_head *orig, struct cache_head *new)
從“orig”設定“new”中的“key”欄位。 這可能包括引用共享物件。
- void update(struct cache_head *orig, struct cache_head *new)
從“orig”設定“new”中的“content”欄位。
- int cache_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
可選。 用於提供一個 /proc 檔案,其中列出了快取的內容。 這應該只顯示一項,通常只在一行上。
- int cache_request(struct cache_detail *cd, struct cache_head *h, char **bpp, int *blen)
格式化要傳送到使用者空間的請求,以便例項化一個專案。 *bpp 是大小為 *blen 的緩衝區。 bpp 應該在編碼的訊息上向前移動,並且 *blen 應該減少以顯示剩餘多少可用空間。 成功時返回 0,如果空間不足或其他問題,則返回 <0。
- int cache_parse(struct cache_detail *cd, char *buf, int len)
來自使用者空間的訊息已到達以填寫快取條目。 它位於長度為“len”的“buf”中。 cache_parse 應該解析它,使用 sunrpc_cache_lookup_rcu 查詢快取中的專案,並使用 sunrpc_cache_update 更新該專案。
需要使用 cache_register() 註冊快取。 這會將其包含在定期清除以丟棄舊資料的快取列表中。
使用快取¶
要在快取中查詢值,請呼叫 sunrpc_cache_lookup_rcu,傳遞一個指向樣本專案中 cache_head 的指標,其中填充了“key”欄位。 這將被傳遞到 ->match 以識別目標條目。 如果未找到任何條目,將建立一個新條目,新增到快取中,並標記為不包含有效資料。
返回的條目通常傳遞給 cache_check,後者將檢查資料是否有效,並且可能會啟動一個 up-call 以獲取新資料。 如果條目為負,或者需要但無法進行 upcall,cache_check 將返回 -ENOENT,如果 upcall 掛起,則返回 -EAGAIN,如果資料有效,則返回 0;
可以向 cache_check 傳遞一個“struct cache_req*”。 此結構通常嵌入在實際請求中,可用於建立請求的延遲副本 (struct cache_deferred_req)。 當找到的快取條目不是最新的,但有理由相信使用者空間可能會很快提供資訊時,就會這樣做。 當快取條目變為有效時,將重新訪問請求的延遲副本 (->revisit)。 預計此方法將重新安排請求以進行處理。
sunrpc_cache_lookup_rcu 返回的值也可以傳遞給 sunrpc_cache_update 以設定該專案的內容。 將傳遞第二個專案,該專案應儲存內容。 如果 _lookup 找到的專案具有有效資料,則將其丟棄並建立一個新專案。 這樣可以避免任何專案使用者擔心在檢查專案時內容發生更改。 如果 _lookup 找到的專案不包含有效資料,則會將內容複製過來並設定 CACHE_VALID。
填充快取¶
每個快取都有一個名稱,並且在註冊快取時,將在 /proc/net/rpc 中建立一個具有該名稱的目錄
此目錄包含一個名為“channel”的檔案,該檔案是用於在核心和使用者之間進行通訊以填充快取的通道。 此目錄稍後可能包含其他用於與快取互動的檔案。
“channel”的工作方式有點像資料報套接字。 每個“write”都作為一個整體傳遞到快取以進行解析和解釋。 每個快取可以以不同的方式處理寫入請求,但預計寫入的訊息將包含
一個鍵
一個過期時間
一個內容。
目的是在快取中建立一個具有給定鍵的專案或更新為具有給定內容,並且應在該專案上設定過期時間。
從通道讀取更有趣。 當快取查詢失敗,或者當它成功但發現一個可能很快過期的條目時,會提交一個請求,要求使用者空間更新該快取項。 這些請求將顯示在通道檔案中。
連續讀取將返回連續請求。 如果沒有更多請求要返回,read 將返回 EOF,但用於讀取的 select 或 poll 將阻塞,等待新增另一個請求。
因此,使用者空間助手可能會
open the channel.
select for readable
read a request
write a response
loop.
如果它死亡並且需要重新啟動,任何尚未應答的請求仍將顯示在該檔案中,並將被助手的新例項讀取。
每個快取都應該定義一個“cache_parse”方法,該方法接受從使用者空間寫入的訊息並對其進行處理。 它應該返回一個錯誤(該錯誤會傳播回寫入 syscall)或 0。
每個快取還應該定義一個“cache_request”方法,該方法接受一個快取項並將請求編碼到提供的緩衝區中。
注意
如果快取在通道上沒有活動的讀取器,並且在超過 60 秒內沒有活動讀取器,則不會將進一步的請求新增到通道,而是所有未找到有效條目的查詢都會失敗。 這部分是為了向後相容:先前的 nfs 匯出表被認為是權威的,並且失敗的查詢意味著明確的“否”。
請求/響應格式¶
雖然每個快取都可以自由地使用自己的格式來透過通道傳送請求和響應,但建議採用以下格式,並且支援例程可用於提供幫助:每個請求或響應記錄應該是可列印的 ASCII,並且只有精確的一個換行符,該換行符應該位於末尾。 記錄中的欄位應該用空格分隔,通常是一個。 如果欄位中需要空格、換行符或 nul 字元,則必須引用它們。 有兩種機制可用
如果欄位以“x”開頭,則它必須包含偶數個十六進位制數字,並且這些數字對提供欄位中的位元組。
否則,欄位中的 a 必須後跟 3 個八進位制數字,這些數字給出位元組的程式碼。 其他字元被視為它們本身。 至少,空格、換行符、nul 和“'”必須以這種方式引用。