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 并清除其标记。占用计数保持不变。