Folio 佇列¶
- 作者:
David Howells <dhowells@redhat.com>
概述¶
folio_queue 結構體在 Folio 的分段列表中形成一個單獨的段,可用於構成 I/O 緩衝區。因此,該列表可以使用 ITER_FOLIOQ iov_iter 型別進行迭代。
該結構體可公開訪問的成員包括:
struct folio_queue {
struct folio_queue *next;
struct folio_queue *prev;
...
};
提供了 next 和 prev 一對指標,它們指向被訪問段兩側的段。雖然這是一個雙向連結串列,但它並非有意設計為迴圈列表;末端段的向外兄弟指標應為 NULL。
列表中的每個段還儲存:
有序的 Folio 指標序列,
每個 Folio 的大小,以及
每個 Folio 三個 1 位標記,
但這些不應直接訪問,因為底層資料結構可能會改變,而應使用下面列出的訪問函式。
此功能可以透過以下方式訪問:
#include <linux/folio_queue.h>
並使用迭代器:
#include <linux/uio.h>
初始化¶
一個段應該透過呼叫以下函式進行初始化:
void folioq_init(struct folio_queue *folioq);
並傳入要初始化段的指標。請注意,這不一定會初始化所有 Folio 指標,因此必須注意檢查已新增 Folio 的數量。
新增和移除 Folio¶
可以透過呼叫以下函式之一,在段結構體的下一個未使用槽位中設定 Folio:
unsigned int folioq_append(struct folio_queue *folioq,
struct folio *folio);
unsigned int folioq_append_mark(struct folio_queue *folioq,
struct folio *folio);
這兩個函式都會更新儲存的 Folio 計數,儲存 Folio 並記錄其大小。第二個函式還會為新增的 Folio 設定第一個標記。這兩個函式都返回所使用的槽位號。[!] 注意,未嘗試檢查容量是否溢位,列表也不會自動擴充套件。
可以透過呼叫以下函式移除 Folio:
void folioq_clear(struct folio_queue *folioq, unsigned int slot);
這會清除陣列中的槽位,並清除該 Folio 的所有標記,但不會改變 Folio 計數——因此將來訪問該槽位時必須檢查該槽位是否被佔用。
查詢 Folio 資訊¶
可以透過以下函式查詢特定槽位中 Folio 的資訊:
struct folio *folioq_folio(const struct folio_queue *folioq,
unsigned int slot);
如果該槽位尚未設定 Folio,這可能會產生未定義的指標。可以透過以下任一函式查詢槽位中 Folio 的大小:
unsigned int folioq_folio_order(const struct folio_queue *folioq,
unsigned int slot);
size_t folioq_folio_size(const struct folio_queue *folioq,
unsigned int slot);
第一個函式以“階”的形式返回大小,第二個函式以位元組數的形式返回大小。
查詢 folio_queue 資訊¶
可以使用以下函式檢索有關特定段的資訊:
unsigned int folioq_nr_slots(const struct folio_queue *folioq);
unsigned int folioq_count(struct folio_queue *folioq);
bool folioq_full(struct folio_queue *folioq);
第一個函式返回段的最大容量。不能假定這在不同段之間不會變化。第二個函式返回已新增到段的 Folio 數量,第三個是表示段是否已達到容量的簡寫。
請注意,計數和滿度不受從段中清除 Folio 的影響。這些更多是用來指示陣列中有多少槽位已初始化,並且假設槽位不會被重複使用,而是隨著佇列的消耗,段將被丟棄。
Folio 標記¶
佇列中的 Folio 也可以被分配標記。這些標記可以用來記錄資訊,例如 Folio 是否需要呼叫 folio_put()。每個 Folio 有三個可設定的標記。
標記可以設定為:
void folioq_mark(struct folio_queue *folioq, unsigned int slot);
void folioq_mark2(struct folio_queue *folioq, unsigned int slot);
清除標記:
void folioq_unmark(struct folio_queue *folioq, unsigned int slot);
void folioq_unmark2(struct folio_queue *folioq, unsigned int slot);
標記可以查詢:
bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot);
bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot);
這些標記可以用於任何目的,並且不會被此 API 解釋。
Folio 佇列迭代¶
可以使用 I/O 迭代器工具,透過型別為 ITER_FOLIOQ 的 iov_iter 迭代器來遍歷段列表。迭代器可以使用以下函式初始化:
void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,
const struct folio_queue *folioq,
unsigned int first_slot, unsigned int offset,
size_t count);
可以告知它從佇列中的特定段、槽位和偏移量開始。iov 迭代器函式在前進時會遵循 next 指標,在回溯時會遵循 prev 指標。
無鎖併發生產/消費問題¶
如果管理得當,生產者可以在頭部擴充套件列表,消費者可以在尾部縮短列表,而無需加鎖。ITER_FOLIOQ 迭代器會插入適當的屏障來協助實現這一點。
在同時生產和消費列表時必須小心。如果到達最後一個段,並且該段引用的 Folio 完全被 IOV 迭代器消耗,則 iov_iter 結構體將指向最後一個段,其槽位號等於該段的容量。如果再次使用迭代器時有另一個段可用,迭代器將嘗試從此繼續,但必須小心,以防在迭代器前進之前段被消費者移除並釋放。
建議佇列始終包含至少一個段,即使該段從未被填充或已完全耗盡。這可以防止頭尾指標塌縮。
API 函式參考¶
-
void folioq_init(struct folio_queue *folioq, unsigned int rreq_id)¶
初始化一個 Folio 佇列段
引數
struct folio_queue *folioq要初始化的段
unsigned int rreq_id在跟蹤行中使用的請求識別符號。
描述
初始化一個 Folio 佇列段並設定用於跟蹤的識別符號。
請注意,Folio 指標保持未初始化狀態。
-
unsigned int folioq_nr_slots(const struct folio_queue *folioq)¶
查詢 Folio 佇列段的容量
引數
const struct folio_queue *folioq要查詢的段
描述
查詢特定 Folio 佇列段可能容納的 Folio 數量。[!] 注意:不能假設這對於每個段都是相同的!
-
unsigned int folioq_count(struct folio_queue *folioq)¶
查詢 Folio 佇列段的佔用率
引數
struct folio_queue *folioq要查詢的段
描述
查詢已新增到 Folio 佇列段的 Folio 數量。請注意,當 Folio 從段中移除時,此計數不會減少。
-
bool folioq_full(struct folio_queue *folioq)¶
查詢 Folio 佇列段是否已滿
引數
struct folio_queue *folioq要查詢的段
描述
查詢 Folio 佇列段是否已完全佔用。請注意,即使 Folio 從段中移除,此狀態也不會改變。
-
bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot)¶
檢查 Folio 佇列段中的第一個 Folio 標記
引數
const struct folio_queue *folioq要查詢的段
unsigned int slot要查詢的 Folio 的槽位號
描述
確定 Folio 佇列段中指定槽位中的 Folio 的第一個標記是否已設定。
-
void folioq_mark(struct folio_queue *folioq, unsigned int slot)¶
在 Folio 佇列段中的 Folio 上設定第一個標記
引數
struct folio_queue *folioq要修改的段
unsigned int slot要修改的 Folio 的槽位號
描述
在 Folio 佇列段中指定槽位中的 Folio 上設定第一個標記。
-
void folioq_unmark(struct folio_queue *folioq, unsigned int slot)¶
清除 Folio 佇列段中的 Folio 上的第一個標記
引數
struct folio_queue *folioq要修改的段
unsigned int slot要修改的 Folio 的槽位號
描述
清除 Folio 佇列段中指定槽位中的 Folio 的第一個標記。
-
bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot)¶
檢查 Folio 佇列段中的第二個 Folio 標記
引數
const struct folio_queue *folioq要查詢的段
unsigned int slot要查詢的 Folio 的槽位號
描述
確定 Folio 佇列段中指定槽位中的 Folio 的第二個標記是否已設定。
-
void folioq_mark2(struct folio_queue *folioq, unsigned int slot)¶
在 Folio 佇列段中的 Folio 上設定第二個標記
引數
struct folio_queue *folioq要修改的段
unsigned int slot要修改的 Folio 的槽位號
描述
在 Folio 佇列段中指定槽位中的 Folio 上設定第二個標記。
-
void folioq_unmark2(struct folio_queue *folioq, unsigned int slot)¶
清除 Folio 佇列段中的 Folio 上的第二個標記
引數
struct folio_queue *folioq要修改的段
unsigned int slot要修改的 Folio 的槽位號
描述
清除 Folio 佇列段中指定槽位中的 Folio 的第二個標記。
引數
struct folio_queue *folioq要新增到的段
struct folio *folio要新增的 Folio
描述
將 Folio 新增到 Folio 佇列段中序列的尾部,增加佔用計數並返回剛新增 Folio 的槽位號。Folio 大小會被提取並存儲在佇列中,標記保持不變。
請注意,由呼叫者負責檢查段容量是否會超出並擴充套件佇列。
-
unsigned int folioq_append_mark(struct folio_queue *folioq, struct folio *folio)¶
將 Folio 新增到 Folio 佇列段
引數
struct folio_queue *folioq要新增到的段
struct folio *folio要新增的 Folio
描述
將 Folio 新增到 Folio 佇列段中序列的尾部,增加佔用計數並返回剛新增 Folio 的槽位號。Folio 大小會被提取並存儲在佇列中,第一個標記被設定,第二個和第三個標記保持不變。
請注意,由呼叫者負責檢查段容量是否會超出並擴充套件佇列。
-
struct folio *folioq_folio(const struct folio_queue *folioq, unsigned int slot)¶
從 Folio 佇列段獲取 Folio
引數
const struct folio_queue *folioq要訪問的段
unsigned int slot要訪問的 Folio 槽位
描述
從 Folio 佇列段中檢索指定槽位中的 Folio。請注意,不進行邊界檢查,如果槽位尚未新增 Folio,則指標將未定義。如果槽位已被清除,則返回 NULL。
-
unsigned int folioq_folio_order(const struct folio_queue *folioq, unsigned int slot)¶
從 Folio 佇列段獲取 Folio 的階
引數
const struct folio_queue *folioq要訪問的段
unsigned int slot要訪問的 Folio 槽位
描述
從 Folio 佇列段中檢索指定槽位中 Folio 的階。請注意,不進行邊界檢查,如果槽位尚未新增 Folio,則返回的階將為 0。
-
size_t folioq_folio_size(const struct folio_queue *folioq, unsigned int slot)¶
從 Folio 佇列段獲取 Folio 的大小
引數
const struct folio_queue *folioq要訪問的段
unsigned int slot要訪問的 Folio 槽位
描述
從 Folio 佇列段中檢索指定槽位中 Folio 的大小。請注意,不進行邊界檢查,如果槽位尚未新增 Folio,則返回的大小將為 PAGE_SIZE。
-
void folioq_clear(struct folio_queue *folioq, unsigned int slot)¶
從 Folio 佇列段中清除 Folio
引數
struct folio_queue *folioq要清除的段
unsigned int slot要清除的 Folio 槽位
描述
從 Folio 佇列段中的序列中清除 Folio 並清除其標記。佔用計數保持不變。