页队列¶
- 作者:
David Howells <dhowells@redhat.com>
概述¶
folio_queue 结构在分段的页列表中形成一个单独的段,该列表可用于形成 I/O 缓冲区。因此,可以使用 ITER_FOLIOQ iov_iter 类型遍历该列表。
该结构的公开访问成员是
struct folio_queue {
struct folio_queue *next;
struct folio_queue *prev;
...
};
提供了一对指针 next
和 prev
,它们指向被访问段两侧的段。虽然这是一个双向链表,但它有意不是一个循环链表;终端段中的外部兄弟指针应为 NULL。
列表中的每个段还存储
一个有序的页指针序列,
每个页的大小和
每个页三个 1 位标记,
但这些不应直接访问,因为底层数据结构可能会更改,而应使用下面概述的访问函数。
可以通过以下方式使该功能可访问
#include <linux/folio_queue.h>
并使用迭代器
#include <linux/uio.h>
初始化¶
应通过调用以下函数来初始化段
void folioq_init(struct folio_queue *folioq);
并使用指向要初始化的段的指针。请注意,这不一定会初始化所有页指针,因此必须小心检查添加的页数。
添加和删除页¶
可以通过调用以下函数之一在段结构中的下一个未使用槽中设置页
unsigned int folioq_append(struct folio_queue *folioq,
struct folio *folio);
unsigned int folioq_append_mark(struct folio_queue *folioq,
struct folio *folio);
这两个函数都会更新存储的页计数、存储页并记录其大小。第二个函数还会为添加的页设置第一个标记。这两个函数都会返回使用的槽号。[!] 请注意,不会尝试检查是否发生容量溢出,并且列表不会自动扩展。
可以通过调用以下函数来删除页
void folioq_clear(struct folio_queue *folioq, unsigned int slot);
这将清除数组中的槽,并清除该页的所有标记,但不会更改页计数 - 因此将来对该槽的访问必须检查该槽是否被占用。
查询有关页的信息¶
可以使用以下函数查询特定槽中页的信息
struct folio *folioq_folio(const struct folio_queue *folioq,
unsigned int slot);
如果尚未在该槽中设置页,则可能会产生未定义的指针。可以使用以下两个函数之一查询槽中页的大小
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_put()
。每个页有三个标记可以设置。
可以通过以下方式设置标记
void folioq_mark(struct folio_queue *folioq, unsigned int slot);
void folioq_mark2(struct folio_queue *folioq, unsigned int slot);
void folioq_mark3(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);
void folioq_unmark3(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);
bool folioq_is_marked3(const struct folio_queue *folioq, unsigned int slot);
这些标记可以用于任何目的,此 API 不会对其进行解释。
页队列迭代¶
可以使用 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 迭代器函数将在前进时跟随下一个指针,在还原时跟随前一个指针。
无锁同时生产/消费问题¶
如果管理得当,生产者可以在头端扩展列表,消费者可以在尾端同时缩短列表,而无需获取锁。ITER_FOLIOQ 迭代器插入适当的屏障来帮助实现这一点。
在同时生产和消费列表时必须小心。如果到达最后一个段,并且其引用的页完全被 IOV 迭代器消耗,则 iov_iter 结构将保留指向最后一个段的指针,其槽号等于该段的容量。如果再次使用时有另一个段可用,则迭代器将尝试从此处继续,但必须小心,以免该段在迭代器前进之前被消费者删除和释放。
建议队列始终至少包含一个段,即使该段从未被填充或完全耗尽。这可以防止头尾指针崩溃。
API 函数参考¶
-
void folioq_init(struct folio_queue *folioq)¶
初始化页队列段
参数
struct folio_queue *folioq
要初始化的段
描述
初始化页队列段。请注意,页指针保持未初始化状态。
-
unsigned int folioq_nr_slots(const struct folio_queue *folioq)¶
查询页队列段的容量
参数
const struct folio_queue *folioq
要查询的段
描述
查询特定页队列段可以容纳的页数。[!] 注意:不得假设每个段都相同!
-
unsigned int folioq_count(struct folio_queue *folioq)¶
查询页队列段的占用率
参数
struct folio_queue *folioq
要查询的段
描述
查询已添加到页队列段的页数。请注意,当从段中删除页时,该值不会减少。
-
bool folioq_full(struct folio_queue *folioq)¶
查询页队列段是否已满
参数
struct folio_queue *folioq
要查询的段
描述
查询页队列段是否已完全占用。请注意,如果从段中删除页,则该值不会更改。
-
bool folioq_is_marked(const struct folio_queue *folioq, unsigned int slot)¶
检查页队列段中的第一个页标记
参数
const struct folio_queue *folioq
要查询的段
unsigned int slot
要查询的页的槽位号
描述
确定是否为页队列段中指定槽位中的页设置了第一个标记。
-
void folioq_mark(struct folio_queue *folioq, unsigned int slot)¶
设置页队列段中页的第一个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
为页队列段中指定槽位中的页设置第一个标记。
-
void folioq_unmark(struct folio_queue *folioq, unsigned int slot)¶
清除页队列段中页的第一个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
清除页队列段中指定槽位中的页的第一个标记。
-
bool folioq_is_marked2(const struct folio_queue *folioq, unsigned int slot)¶
检查页队列段中的第二个页标记
参数
const struct folio_queue *folioq
要查询的段
unsigned int slot
要查询的页的槽位号
描述
确定是否为页队列段中指定槽位中的页设置了第二个标记。
-
void folioq_mark2(struct folio_queue *folioq, unsigned int slot)¶
设置页队列段中页的第二个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
为页队列段中指定槽位中的页设置第二个标记。
-
void folioq_unmark2(struct folio_queue *folioq, unsigned int slot)¶
清除页队列段中页的第二个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
清除页队列段中指定槽位中的页的第二个标记。
-
bool folioq_is_marked3(const struct folio_queue *folioq, unsigned int slot)¶
检查页队列段中的第三个页标记
参数
const struct folio_queue *folioq
要查询的段
unsigned int slot
要查询的页的槽位号
描述
确定是否为页队列段中指定槽位中的页设置了第三个标记。
-
void folioq_mark3(struct folio_queue *folioq, unsigned int slot)¶
设置页队列段中页的第三个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
为页队列段中指定槽位中的页设置第三个标记。
-
void folioq_unmark3(struct folio_queue *folioq, unsigned int slot)¶
清除页队列段中页的第三个标记
参数
struct folio_queue *folioq
要修改的段
unsigned int slot
要修改的页的槽位号
描述
清除页队列段中指定槽位中的页的第三个标记。
参数
struct folio_queue *folioq
要添加到的段
struct folio *folio
要添加的页
描述
将一个页添加到页队列段中序列的末尾,增加占用计数,并返回刚刚添加的页的槽位号。页大小被提取并存储在队列中,并且标记保持不变。
请注意,由调用者来检查是否会超出段容量并扩展队列。
参数
struct folio_queue *folioq
要添加到的段
struct folio *folio
要添加的页
描述
将一个页添加到页队列段中序列的末尾,增加占用计数,并返回刚刚添加的页的槽位号。页大小被提取并存储在队列中,设置第一个标记,第二个和第三个标记保持不变。
请注意,由调用者来检查是否会超出段容量并扩展队列。
参数
const struct folio_queue *folioq
要访问的段
unsigned int slot
要访问的页槽位
描述
从页队列段中检索指定槽位中的页。请注意,不会进行边界检查,如果该槽位尚未添加,则该指针将是未定义的。如果该槽位已被清除,则将返回 NULL。
-
unsigned int folioq_folio_order(const struct folio_queue *folioq, unsigned int slot)¶
从页队列段获取页的顺序
参数
const struct folio_queue *folioq
要访问的段
unsigned int slot
要访问的页槽位
描述
从 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 的大小。请注意,不执行边界检查,如果该槽位尚未添加,则返回的大小将为 PAGE_SIZE。
-
void folioq_clear(struct folio_queue *folioq, unsigned int slot)¶
从 folio 队列段清除 folio
参数
struct folio_queue *folioq
要清除的段
unsigned int slot
要清除的 folio 槽位
描述
从 folio 队列段中的序列中清除一个 folio 并清除其标记。占用计数保持不变。