XArray¶
- 作者:
Matthew Wilcox
概述¶
XArray 是一种抽象数据类型,其行为类似于一个巨大的指针数组。它满足了散列表或常规可变大小数组的许多相同需求。与散列表不同,它允许您以缓存高效的方式合理地访问下一个或上一个条目。与可变大小数组相比,它无需复制数据或更改 MMU 映射即可扩展数组。它比双向链表更节省内存、更易于并行化且缓存友好。它利用 RCU 实现无锁查找。
当使用的索引密集聚类时,XArray 的实现是高效的;对对象进行哈希并将哈希值用作索引将无法很好地执行。XArray 针对小索引进行了优化,但对于大索引仍然具有良好的性能。如果您的索引可能大于 ULONG_MAX
,那么 XArray 不适合您。XArray 最重要的用户是页缓存。
普通指针可以直接存储在 XArray 中。它们必须是 4 字节对齐的,这对于 kmalloc()
和 alloc_page() 返回的任何指针都是如此。对于任意用户空间指针或函数指针则不然。您可以存储指向静态分配对象的指针,只要这些对象至少有 4 字节对齐。
您还可以在 XArray 中存储 0 到 LONG_MAX
之间的整数。您必须首先使用 xa_mk_value()
将其转换为一个条目。当您从 XArray 检索一个条目时,您可以通过调用 xa_is_value()
来检查它是否是值条目,并通过调用 xa_to_value()
将其转换回整数。
一些用户希望标记他们存储在 XArray 中的指针。您可以使用 xa_tag_pointer()
创建一个带有标记的条目,使用 xa_untag_pointer()
将标记的条目转换回未标记的指针,并使用 xa_pointer_tag()
检索条目的标记。标记的指针使用与区分值条目和普通指针相同的位,因此您必须决定在任何特定的 XArray 中是存储值条目还是标记指针。
XArray 不支持存储 IS_ERR()
指针,因为某些指针会与值条目或内部条目冲突。
XArray 的一个不寻常的特性是能够创建占据一系列索引的条目。一旦存储,查找范围内的任何索引都将返回与查找范围内任何其他索引相同的条目。存储到任何一个索引都会存储到所有这些索引。多索引条目可以明确地分割成更小的条目。取消设置(使用 xa_erase()
或使用 NULL
调用 xa_store()
)任何条目都将导致 XArray 忘记该范围。
普通 API¶
首先初始化一个 XArray,可以使用 DEFINE_XARRAY()
用于静态分配的 XArray,或者使用 xa_init()
用于动态分配的 XArray。一个刚初始化的 XArray 在每个索引处都包含一个 NULL
指针。
然后您可以使用 xa_store()
设置条目,并使用 xa_load()
获取条目。xa_store()
会用新条目覆盖任何现有条目,并返回该索引处存储的旧条目。您可以使用 xa_erase()
或使用 xa_store()
将条目设置为 NULL
来取消设置条目。从未存储过的条目与已被 xa_erase()
擦除的条目之间没有区别;最近存储为 NULL
的条目也等效,除非 XArray 是用 XA_FLAGS_ALLOC
初始化的。
您可以通过使用 xa_cmpxchg()
有条件地替换索引处的条目。与 cmpxchg() 类似,它仅当该索引处的条目具有“旧”值时才会成功。它还会返回该索引处原有的条目;如果它返回与作为“旧”值传递的条目相同的条目,则 xa_cmpxchg()
成功。
如果您只想在当前索引处的条目为 NULL
时才存储新条目,可以使用 xa_insert()
,如果条目不为空,它将返回 -EBUSY
。
您可以通过调用 xa_extract()
将条目从 XArray 复制到普通数组中。或者,您可以通过调用 xa_for_each()
、 xa_for_each_start()
或 xa_for_each_range()
来遍历 XArray 中的现有条目。您可能更倾向于使用 xa_find()
或 xa_find_after()
移动到 XArray 中的下一个现有条目。
调用 xa_store_range()
会在一定范围的索引中存储相同的条目。如果您这样做,其他一些操作的行为会略显奇怪。例如,标记一个索引处的条目可能会导致其他一些(但不是所有)索引处的条目被标记。存储到一个索引可能会导致其他一些(但不是所有)索引检索到的条目发生变化。
有时您需要确保后续调用 xa_store()
不需要分配内存。xa_reserve()
函数会在指定索引处存储一个保留条目。普通 API 的用户会将此条目视为包含 NULL
。如果您不需要使用保留条目,可以调用 xa_release()
来移除未使用的条目。如果同时有其他用户存储到该条目,xa_release()
将不执行任何操作;如果您希望该条目变为 NULL
,则应使用 xa_erase()
。对保留条目使用 xa_insert()
将失败。
如果数组中的所有条目都是 NULL
,则 xa_empty()
函数将返回 true
。
最后,您可以通过调用 xa_destroy()
从 XArray 中移除所有条目。如果 XArray 条目是指针,您可能希望首先释放这些条目。您可以通过使用 xa_for_each()
迭代器遍历 XArray 中所有现有条目来完成此操作。
搜索标记¶
数组中的每个条目都关联着三个位,称为标记。每个标记都可以独立地设置或清除。您可以使用 xa_for_each_marked()
迭代器遍历已标记的条目。
您可以使用 xa_get_mark()
查询条目上是否设置了标记。如果条目不是 NULL
,您可以使用 xa_set_mark()
设置标记,并通过调用 xa_clear_mark()
移除条目上的标记。您可以通过调用 xa_marked()
查询 XArray 中是否有任何条目设置了特定标记。从 XArray 中擦除一个条目会导致与该条目关联的所有标记都被清除。
设置或清除多索引条目的任何索引上的标记将影响该条目覆盖的所有索引。查询任何索引上的标记将返回相同的结果。
没有办法遍历未标记的条目;数据结构不允许高效地实现这一点。目前没有迭代器来搜索位的逻辑组合(例如,遍历所有同时设置了 XA_MARK_1
和 XA_MARK_2
的条目,或遍历所有设置了 XA_MARK_0
或 XA_MARK_2
的条目)。如果出现用户需求,未来可能会添加这些功能。
分配 XArray¶
如果您使用 DEFINE_XARRAY_ALLOC()
来定义 XArray,或者通过向 xa_init_flags()
传递 XA_FLAGS_ALLOC
来初始化它,则 XArray 会跟踪条目是否在使用中。
您可以调用 xa_alloc()
将条目存储在 XArray 的未使用索引处。如果您需要从中断上下文修改数组,可以使用 xa_alloc_bh()
或 xa_alloc_irq()
以便在分配 ID 时禁用中断。
使用 xa_store()
、xa_cmpxchg()
或 xa_insert()
也会将条目标记为已分配。与普通 XArray 不同,存储 NULL
会将条目标记为正在使用中,就像 xa_reserve()
一样。要释放条目,请使用 xa_erase()
(或者如果您只想在条目为 NULL
时才释放,则使用 xa_release()
)。
默认情况下,最低的空闲条目从 0 开始分配。如果您想从 1 开始分配条目,使用 DEFINE_XARRAY_ALLOC1()
或 XA_FLAGS_ALLOC1
会更高效。如果您想将 ID 分配到最大值,然后循环回到最低的空闲 ID,可以使用 xa_alloc_cyclic()
。
您不能将 XA_MARK_0
与分配 XArray 一起使用,因为此标记用于跟踪条目是否空闲。其他标记可供您使用。
内存分配¶
xa_store()
、xa_cmpxchg()
、xa_alloc()
、xa_reserve()
和 xa_insert()
函数接受一个 gfp_t 参数,以防 XArray 需要分配内存来存储此条目。如果条目正在被删除,则无需执行内存分配,并且指定的 GFP 标志将被忽略。
可能无法分配内存,特别是如果您传递了一组限制性的 GFP 标志。在这种情况下,函数会返回一个特殊值,该值可以使用 xa_err()
转换为错误号。如果您不需要确切知道发生了哪个错误,使用 xa_is_err()
效率略高。
锁机制¶
使用普通 API 时,您无需担心锁机制。XArray 使用 RCU 和内部自旋锁来同步访问。
- 无需加锁
- 获取 RCU 读锁
- 内部获取 xa_lock
- 假设进入时已持有 xa_lock
如果您想利用锁来保护存储在 XArray 中的数据结构,您可以在调用 xa_load()
之前调用 xa_lock(),然后在调用 xa_unlock() 之前对找到的对象进行引用计数。这将防止在查找对象和增加引用计数之间,存储操作将对象从数组中移除。您还可以使用 RCU 来避免解引用已释放的内存,但这超出了本文档的范围。
XArray 在修改数组时不会禁用中断或软中断。从中断或软中断上下文读取 XArray 是安全的,因为 RCU 锁提供了足够的保护。
例如,如果您想在进程上下文中将条目存储到 XArray 中,然后在软中断上下文中擦除它们,您可以这样做:
void foo_init(struct foo *foo)
{
xa_init_flags(&foo->array, XA_FLAGS_LOCK_BH);
}
int foo_store(struct foo *foo, unsigned long index, void *entry)
{
int err;
xa_lock_bh(&foo->array);
err = xa_err(__xa_store(&foo->array, index, entry, GFP_KERNEL));
if (!err)
foo->count++;
xa_unlock_bh(&foo->array);
return err;
}
/* foo_erase() is only called from softirq context */
void foo_erase(struct foo *foo, unsigned long index)
{
xa_lock(&foo->array);
__xa_erase(&foo->array, index);
foo->count--;
xa_unlock(&foo->array);
}
如果您要从中断或软中断上下文修改 XArray,您需要使用 xa_init_flags()
初始化数组,并传入 XA_FLAGS_LOCK_IRQ
或 XA_FLAGS_LOCK_BH
。
上面的示例还展示了一种常见模式,即希望扩展存储侧 xa_lock 的覆盖范围,以保护与数组关联的某些统计信息。
与中断上下文共享 XArray 也是可能的,可以使用 xa_lock_irqsave() 在中断处理程序和进程上下文中都进行加锁,或者在进程上下文中使用 xa_lock_irq() 并在中断处理程序中使用 xa_lock()。一些更常见的模式有辅助函数,例如 xa_store_bh()
、xa_store_irq()
、xa_erase_bh()
、xa_erase_irq()
、xa_cmpxchg_bh()
和 xa_cmpxchg_irq()
。
有时您需要使用互斥锁保护对 XArray 的访问,因为该锁在锁定层次结构中位于另一个互斥锁之上。但这并不能授权您在不持有 xa_lock 的情况下使用 __xa_erase()
等函数;xa_lock 用于 lockdep 验证,并且将来会用于其他目的。
__xa_set_mark()
和 __xa_clear_mark()
函数也可用于您查找条目并希望原子地设置或清除标记的情况。在这种情况下,使用高级 API 可能更高效,因为它可以避免您两次遍历树。
高级 API¶
高级 API 以牺牲更难使用和更少安全保障的接口为代价,提供了更高的灵活性和更好的性能。高级 API 不会为您执行任何锁定,并且在修改数组时,您需要使用 xa_lock。在对数组进行只读操作时,您可以选择使用 xa_lock 或 RCU 锁。您可以在同一个数组上混合使用高级和普通操作;实际上,普通 API 是通过高级 API 实现的。高级 API 仅适用于具有 GPL 兼容许可证的模块。
高级 API 基于 xa_state。这是一个不透明的数据结构,您可以使用 XA_STATE()
宏在栈上声明它。此宏初始化 xa_state,准备开始遍历 XArray。它用作游标,以维护在 XArray 中的位置,并允许您组合各种操作而无需每次都从头开始。xa_state 的内容受 rcu_read_lock()
或 xas_lock() 保护。如果您需要释放保护您状态和树的锁,您必须调用 xas_pause()
,这样将来的调用就不会依赖于未受保护的状态部分。
xa_state 也用于存储错误。您可以调用 xas_error()
来检索错误。所有操作在继续之前都会检查 xa_state 是否处于错误状态,因此您无需在每次调用后都检查错误;您可以连续进行多次调用,只在方便时检查。XArray 代码本身当前生成的错误只有 ENOMEM
和 EINVAL
,但它支持任意错误,以防您想自己调用 xas_set_err()
。
如果 xa_state 包含 ENOMEM
错误,调用 xas_nomem()
将尝试使用指定的 gfp 标志分配更多内存,并将其缓存在 xa_state 中以供下次尝试。其思想是,您获取 xa_lock,尝试操作,然后释放锁。操作在持有锁时尝试分配内存,但它更有可能失败。一旦您释放了锁,xas_nomem()
可以更努力地尝试分配更多内存。如果值得重试操作(即存在内存错误**且**分配了更多内存),它将返回 true
。如果它之前分配了内存,并且该内存没有被使用,并且没有错误(或不是 ENOMEM
的其他错误),那么它将释放之前分配的内存。
内部条目¶
XArray 为其自身目的保留了一些条目。这些条目从不通过普通 API 公开,但在使用高级 API 时,可以看到它们。通常处理它们的最佳方式是将它们传递给 xas_retry()
,如果返回 true
,则重试操作。
名称 |
测试 |
用法 |
节点 |
xa_is_node() |
一个 XArray 节点。在使用多索引 xa_state 时可能会可见。 |
兄弟节点 |
多索引条目的非规范条目。该值指示此节点中的哪个槽位具有规范条目。 |
|
重试 |
此条目当前正在被持有 xa_lock 的线程修改。包含此条目的节点可能会在此 RCU 周期结束时释放。您应该从数组的头部重新开始查找。 |
|
零值 |
零值条目通过普通 API 显示为 |
将来可能会添加其他内部条目。在可能的情况下,它们将由 xas_retry()
处理。
附加功能¶
xas_create_range()
函数分配存储范围内所有条目所需的所有内存。如果无法分配内存,它将在 xa_state 中设置 ENOMEM。
您可以使用 xas_init_marks()
将条目上的标记重置为默认状态。这通常是所有标记清除,除非 XArray 被标记为 XA_FLAGS_TRACK_FREE
,在这种情况下标记 0 被设置,所有其他标记被清除。使用 xas_store()
将一个条目替换为另一个条目不会重置该条目上的标记;如果需要重置标记,应明确进行。
xas_load()
将尽可能地将 xa_state 游标移动到条目附近。如果您知道 xa_state 游标已经移动到条目,并且需要检查条目是否未更改,您可以使用 xas_reload()
来节省一次函数调用。
如果您需要在 XArray 中移动到不同的索引,请调用 xas_set()
。这会将游标重置到树的顶部,通常会使下一个操作将游标移动到树中所需的位置。如果您想移动到下一个或上一个索引,请调用 xas_next()
或 xas_prev()
。设置索引不会遍历数组,因此不需要持有锁,而移动到下一个或上一个索引则需要。
您可以使用 xas_find()
搜索下一个现有条目。这等同于 xa_find()
和 xa_find_after()
;如果游标已经移动到某个条目,那么它将找到当前引用条目之后的下一个条目。否则,它将返回 xa_state 索引处的条目。使用 xas_next_entry()
移动到下一个现有条目而不是 xas_find()
,在大多数情况下可以节省一次函数调用,但代价是会生成更多的内联代码。
xas_find_marked()
函数类似。如果 xa_state 尚未遍历,它将返回 xa_state 索引处的条目(如果已标记)。否则,它将返回 xa_state 引用条目之后的第一个标记条目。xas_next_marked()
函数等同于 xas_next_entry()
。
当使用 xas_for_each()
或 xas_for_each_marked()
遍历 XArray 的一个范围时,可能需要暂时停止迭代。xas_pause()
函数为此目的而存在。完成必要工作并希望恢复后,xa_state 将处于适当状态,以便在您上次处理的条目之后继续迭代。如果您在迭代时禁用了中断,那么每隔 XA_CHECK_SCHED
个条目暂停迭代并重新启用中断是良好的习惯。
xas_get_mark()
、xas_set_mark()
和 xas_clear_mark()
函数要求 xa_state 游标已移动到 XArray 中的适当位置;如果您之前立即调用了 xas_pause()
或 xas_set()
,它们将不执行任何操作。
您可以调用 xas_set_update()
以便在 XArray 每次更新节点时调用回调函数。页缓存工作集代码使用此功能来维护其仅包含影子条目的节点列表。
多索引条目¶
XArray 能够将多个索引绑定在一起,从而对一个索引的操作会影响所有索引。例如,存储到任何索引都会改变从任何索引检索到的条目的值。设置或清除任何索引上的标记将设置或清除所有绑定在一起的索引上的标记。当前实现只允许绑定对齐的 2 的幂范围;例如,索引 64-127 可以绑定在一起,但 2-6 不可以。这可以节省大量的内存;例如,绑定 512 个条目将节省超过 4KB。
您可以通过使用 XA_STATE_ORDER()
或 xas_set_order()
,然后调用 xas_store()
来创建多索引条目。使用多索引 xa_state 调用 xas_load()
会将 xa_state 游标移动到树中的正确位置,但返回值没有意义,即使范围内存储有条目,也可能是内部条目或 NULL
。调用 xas_find_conflict()
将返回范围内的第一个条目,如果范围内没有条目则返回 NULL
。xas_for_each_conflict()
迭代器将遍历与指定范围重叠的每个条目。
如果 xas_load()
遇到多索引条目,xa_state 中的 xa_index 将不会改变。当遍历 XArray 或调用 xas_find()
时,如果初始索引位于多索引条目的中间,它将不会被更改。后续调用或迭代会将索引移动到范围内的第一个索引。每个条目只会返回一次,无论它占据多少个索引。
不支持将 xas_next()
或 xas_prev()
与多索引 xa_state 一起使用。对多索引条目使用这些函数中的任何一个都将揭示兄弟条目;调用者应跳过这些条目。
将 NULL
存储到多索引条目的任何索引中,都会将每个索引处的条目设置为 NULL
并解除绑定。多索引条目可以通过在不持有 xa_lock 的情况下调用 xas_split_alloc()
,然后获取锁并调用 xas_split()
,或者在持有 xa_lock 的情况下调用 xas_try_split()
来分割成占用更小范围的条目。xas_split_alloc()
+ xas_split()
与 xas_try_alloc() 的区别在于,xas_split_alloc()
+ xas_split()
一次性均匀地将条目从原始顺序分割到新顺序,而 xas_try_split()
则根据给定索引迭代地非均匀分割包含该索引的条目。例如,要分割一个 order-9 条目,它占用 2^(9-6)=8 个槽位(假设 XA_CHUNK_SHIFT
为 6),xas_split_alloc()
+ xas_split()
需要 8 个 xa_node。xas_try_split()
将 order-9 条目分割成 2 个 order-8 条目,然后根据给定索引将一个 order-8 条目分割成 2 个 order-7 条目,依此类推,并将一个 order-1 条目分割成 2 个 order-0 条目。当分割 order-6 条目并需要一个新的 xa_node 时,xas_try_split()
如果可能将尝试分配一个。因此,xas_try_split()
只需要 1 个 xa_node 而不是 8 个。
函数和结构体¶
-
void *xa_mk_value(unsigned long v)¶
从整数创建 XArray 条目。
参数
unsigned long v
要存储在 XArray 中的值。
上下文
任何上下文。
返回
适合存储在 XArray 中的条目。
-
unsigned long xa_to_value(const void *entry)¶
获取存储在 XArray 条目中的值。
参数
const void *entry
XArray 条目。
上下文
任何上下文。
返回
存储在 XArray 条目中的值。
-
bool xa_is_value(const void *entry)¶
判断条目是否为值。
参数
const void *entry
XArray 条目。
上下文
任何上下文。
返回
如果条目是值则为 true,如果是指针则为 false。
-
void *xa_tag_pointer(void *p, unsigned long tag)¶
为带标记的指针创建 XArray 条目。
参数
void *p
普通指针。
unsigned long tag
标记值(0、1 或 3)。
描述
如果 XArray 的用户愿意,他们可以标记其指针而不是存储值条目。提供了三个标记(0、1 和 3)。这些标记与 xa_mark_t 不同,因为它们不会通过数组向上复制,也无法进行搜索。
上下文
任何上下文。
返回
一个 XArray 条目。
-
void *xa_untag_pointer(void *entry)¶
将 XArray 条目转换为普通指针。
参数
void *entry
XArray 条目。
描述
如果您在 XArray 中存储了带标记的指针,请调用此函数获取该指针的未标记版本。
上下文
任何上下文。
返回
一个指针。
-
unsigned int xa_pointer_tag(void *entry)¶
获取存储在 XArray 条目中的标记。
参数
void *entry
XArray 条目。
描述
如果您在 XArray 中存储了带标记的指针,请调用此函数获取该指针的标记。
上下文
任何上下文。
返回
一个标记。
-
bool xa_is_zero(const void *entry)¶
该条目是否为零值条目?
参数
const void *entry
从 XArray 检索到的条目
描述
普通 API 会将包含零值条目的槽位内容返回为 NULL。您只能通过高级 API 看到零值条目。
返回
如果条目是零值条目,则为 true
。
-
bool xa_is_err(const void *entry)¶
报告 XArray 操作是否返回了错误
参数
const void *entry
调用 XArray 函数的结果
描述
如果 XArray 操作无法完成,它将返回一个指示错误的特殊值。此函数告诉您是否发生了错误;xa_err()
告诉您发生了哪个错误。
上下文
任何上下文。
返回
如果条目指示错误,则为 true
。
-
int xa_err(void *entry)¶
将 XArray 结果转换为错误号。
参数
void *entry
调用 XArray 函数的结果。
描述
如果 XArray 操作无法完成,它将返回一个编码了错误号的特殊指针值。此函数从指针值中提取错误号,如果指针不表示错误号,则返回 0。
上下文
任何上下文。
返回
一个负的错误号或 0。
-
struct xa_limit¶
表示 ID 范围。
定义:
struct xa_limit {
u32 max;
u32 min;
};
成员
max
要分配的最大 ID(包含)。
min
要分配的最低 ID(包含)。
描述
此结构体直接或通过 XA_LIMIT() 宏用于传达有效分配的 ID 范围。为您预定义了三个常用范围:* xa_limit_32b - [0 - UINT_MAX] * xa_limit_31b - [0 - INT_MAX] * xa_limit_16b - [0 - USHRT_MAX]
-
struct xarray¶
XArray 的锚点。
定义:
struct xarray {
spinlock_t xa_lock;
};
成员
xa_lock
保护 XArray 内容的锁。
描述
要使用 xarray,请将其静态定义或嵌入到您的数据结构中。它是一个非常小的数据结构,因此通常没有必要单独分配它并在您的数据结构中保留一个指向它的指针。
您也可以使用 xa_lock 来保护您自己的数据结构。
-
DEFINE_XARRAY_FLAGS¶
DEFINE_XARRAY_FLAGS (name, flags)
定义具有自定义标志的 XArray。
参数
name
您的 XArray 的名称字符串。
flags
XA_FLAG 值。
描述
这旨在用于文件范围的 XArray 定义。它声明并初始化一个带有指定名称和标志的空 XArray。它等同于对数组调用 xa_init_flags()
,但在编译时而不是运行时进行初始化。
-
DEFINE_XARRAY¶
DEFINE_XARRAY (name)
定义一个 XArray。
参数
name
您的 XArray 的名称字符串。
描述
这旨在用于文件范围的 XArray 定义。它声明并初始化一个带有指定名称的空 XArray。它等同于对数组调用 xa_init()
,但在编译时而不是运行时进行初始化。
-
DEFINE_XARRAY_ALLOC¶
DEFINE_XARRAY_ALLOC (name)
定义一个从 0 开始分配 ID 的 XArray。
-
DEFINE_XARRAY_ALLOC1¶
DEFINE_XARRAY_ALLOC1 (name)
定义一个从 1 开始分配 ID 的 XArray。
参数
struct xarray *xa
XArray。
gfp_t flags
XA_FLAG 值。
描述
如果您需要使用特殊标志(例如,您需要在中断上下文中获取锁)初始化 XArray,请使用此函数而不是 xa_init()
。
上下文
任何上下文。
参数
struct xarray *xa
XArray。
描述
一个空的 XArray 充满了 NULL 条目。
上下文
任何上下文。
参数
const struct xarray *xa
XArray。
上下文
任何上下文。
返回
如果数组仅包含 NULL 指针,则为 true
。
参数
const struct xarray *xa
数组
xa_mark_t mark
标记值
上下文
任何上下文。
返回
如果任何条目设置了此标记,则为 true
。
-
xa_for_each_range¶
xa_for_each_range (xa, index, entry, start, last)
遍历 XArray 的一部分。
参数
xa
XArray。
index
条目的索引。
entry
从数组中检索到的条目。
start
从数组中检索的第一个索引。
last
从数组中检索的最后一个索引。
描述
在迭代期间,entry
将是存储在 xa
中 index
处的条目的值。您可以在迭代期间修改 index
,如果想跳过或重新处理索引。在迭代期间修改数组是安全的。迭代结束时,entry
将被设置为 NULL,并且 index
的值将小于或等于 max。
xa_for_each_range()
是 O(n.log(n)) 而 xas_for_each()
是 O(n)。您必须自行处理 xas_for_each()
的锁定,如果您在每次迭代后都必须解锁,它最终也会是 O(n.log(n))。xa_for_each_range()
在遇到重试条目时会自旋;如果您打算看到重试条目,则应改用 xas_for_each()
迭代器。xas_for_each()
迭代器将比 xa_for_each_range()
展开为更多的内联代码。
上下文
任何上下文。获取并释放 RCU 锁。
-
xa_for_each_start¶
xa_for_each_start (xa, index, entry, start)
遍历 XArray 的一部分。
参数
xa
XArray。
index
条目的索引。
entry
从数组中检索到的条目。
start
从数组中检索的第一个索引。
描述
在迭代期间,entry
将是存储在 xa
中 index
处的条目的值。您可以在迭代期间修改 index
,如果想跳过或重新处理索引。在迭代期间修改数组是安全的。迭代结束时,entry
将被设置为 NULL,并且 index
的值将小于或等于 max。
xa_for_each_start()
的时间复杂度为 O(n.log(n)),而 xas_for_each()
的时间复杂度为 O(n)。你必须自行处理 xas_for_each()
的锁定,如果你需要在每次迭代后解锁,那么其时间复杂度也将变为 O(n.log(n))。xa_for_each_start()
在遇到重试条目时会自旋;如果你想查看重试条目,则应改用 xas_for_each()
迭代器。与 xa_for_each_start()
相比,xas_for_each()
迭代器会展开成更多的内联代码。
上下文
任何上下文。获取并释放 RCU 锁。
-
xa_for_each¶
xa_for_each (xa, index, entry)
迭代 XArray 中存在的条目。
参数
xa
XArray。
index
条目的索引。
entry
从数组中检索到的条目。
描述
在迭代期间,entry
将是存储在 xa
中 index
处的条目的值。您可以在迭代期间修改 index
,如果想跳过或重新处理索引。在迭代期间修改数组是安全的。迭代结束时,entry
将被设置为 NULL,并且 index
的值将小于或等于 max。
xa_for_each()
的时间复杂度为 O(n.log(n)),而 xas_for_each()
的时间复杂度为 O(n)。你必须自行处理 xas_for_each()
的锁定,如果你需要在每次迭代后解锁,那么其时间复杂度也将变为 O(n.log(n))。xa_for_each()
在遇到重试条目时会自旋;如果你想查看重试条目,则应改用 xas_for_each()
迭代器。与 xa_for_each()
相比,xas_for_each()
迭代器会展开成更多的内联代码。
上下文
任何上下文。获取并释放 RCU 锁。
-
xa_for_each_marked¶
xa_for_each_marked (xa, index, entry, filter)
迭代 XArray 中已标记的条目。
参数
xa
XArray。
index
条目的索引。
entry
从数组中检索到的条目。
filter
选择标准。
描述
在迭代期间,entry 将是存储在 xa 的 index 处的条目的值。迭代将跳过数组中所有与 filter 不匹配的条目。如果你想跳过或重新处理索引,可以在迭代期间修改 index。在迭代期间修改数组是安全的。迭代结束时,entry 将被设置为 NULL,并且 index 的值将小于或等于 max。
xa_for_each_marked()
的时间复杂度为 O(n.log(n)),而 xas_for_each_marked()
的时间复杂度为 O(n)。你必须自行处理 xas_for_each()
的锁定,如果你需要在每次迭代后解锁,那么其时间复杂度也将变为 O(n.log(n))。xa_for_each_marked()
在遇到重试条目时会自旋;如果你想查看重试条目,则应改用 xas_for_each_marked()
迭代器。与 xa_for_each_marked()
相比,xas_for_each_marked()
迭代器会展开成更多的内联代码。
上下文
任何上下文。获取并释放 RCU 锁。
-
void *xa_store_bh(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
将此条目存储到 XArray 中。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
此函数类似于调用 xa_store()
,但它在持有数组锁的同时禁用软中断(softirqs)。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。
返回
此索引处的旧条目,如果发生错误则为 xa_err()
。
-
void *xa_store_irq(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
将此条目存储到 XArray 中。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
此函数类似于调用 xa_store()
,但它在持有数组锁的同时禁用中断。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。
返回
此索引处的旧条目,如果发生错误则为 xa_err()
。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
描述
此函数返回后,从 index 加载将返回 NULL
。如果此索引是多索引条目的一部分,则所有索引都将被擦除,并且没有任何条目会是多索引条目的一部分。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。
返回
曾经在此索引处的条目。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
描述
此函数返回后,从 index 加载将返回 NULL
。如果此索引是多索引条目的一部分,则所有索引都将被擦除,并且没有任何条目会是多索引条目的一部分。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。
返回
曾经在此索引处的条目。
-
void *xa_cmpxchg(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp)¶
有条件地替换 XArray 中的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *old
用于比较的旧值。
void *entry
要放入数组的新值。
gfp_t gfp
内存分配标志。
描述
如果 index 处的条目与 old 相同,则将其替换为 entry。如果返回值等于 old,则交换成功。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
此索引处的旧值,如果发生错误则为 xa_err()
。
-
void *xa_cmpxchg_bh(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp)¶
有条件地替换 XArray 中的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *old
用于比较的旧值。
void *entry
要放入数组的新值。
gfp_t gfp
内存分配标志。
描述
此函数类似于调用 xa_cmpxchg()
,但它在持有数组锁的同时禁用软中断(softirqs)。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
此索引处的旧值,如果发生错误则为 xa_err()
。
-
void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp)¶
有条件地替换 XArray 中的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *old
用于比较的旧值。
void *entry
要放入数组的新值。
gfp_t gfp
内存分配标志。
描述
此函数类似于调用 xa_cmpxchg()
,但它在持有数组锁的同时禁用中断。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
此索引处的旧值,如果发生错误则为 xa_err()
。
-
int xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
将此条目存储到 XArray 中,除非已有其他条目存在。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
如果不存在任何条目,插入 NULL 条目将存储一个保留条目(例如 xa_reserve()
)。如果存在保留条目,插入将失败,即使从该索引加载会返回 NULL。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
存储成功则返回 0。如果存在其他条目则返回 -EBUSY。如果无法分配内存则返回 -ENOMEM。
-
int xa_insert_bh(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
将此条目存储到 XArray 中,除非已有其他条目存在。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
如果不存在任何条目,插入 NULL 条目将存储一个保留条目(例如 xa_reserve()
)。如果存在保留条目,插入将失败,即使从该索引加载会返回 NULL。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
存储成功则返回 0。如果存在其他条目则返回 -EBUSY。如果无法分配内存则返回 -ENOMEM。
-
int xa_insert_irq(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
将此条目存储到 XArray 中,除非已有其他条目存在。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
如果不存在任何条目,插入 NULL 条目将存储一个保留条目(例如 xa_reserve()
)。如果存在保留条目,插入将失败,即使从该索引加载会返回 NULL。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
存储成功则返回 0。如果存在其他条目则返回 -EBUSY。如果无法分配内存则返回 -ENOMEM。
-
int xa_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
要分配的 ID 范围。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int xa_alloc_bh(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
要分配的 ID 范围。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int xa_alloc_irq(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
要分配的 ID 范围。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
已分配 ID 的范围。
u32 *next
指向下一个要分配的 ID 的指针。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。空条目的搜索将从 next 开始,如果需要,将循环查找。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
请注意,关心是否发生循环的调用者应改用 __xa_alloc_cyclic()
。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
分配成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int xa_alloc_cyclic_bh(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
已分配 ID 的范围。
u32 *next
指向下一个要分配的 ID 的指针。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。空条目的搜索将从 next 开始,如果需要,将循环查找。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
请注意,关心是否发生循环的调用者应改用 __xa_alloc_cyclic()
。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
分配成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int xa_alloc_cyclic_irq(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
已分配 ID 的范围。
u32 *next
指向下一个要分配的 ID 的指针。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。空条目的搜索将从 next 开始,如果需要,将循环查找。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
请注意,关心是否发生循环的调用者应改用 __xa_alloc_cyclic()
。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
分配成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
gfp_t gfp
内存分配标志。
描述
确保数组中 index 处有位置可以存储条目。如果 index 处已存储有内容,此函数不执行任何操作。如果该处没有内容,则此条目被标记为保留。从保留条目加载会返回一个 NULL
指针。
如果你没有使用已保留的条目,请调用 xa_release()
或 xa_erase()
来释放所有不必要的内存。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
如果预留成功则返回 0,失败则返回 -ENOMEM。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
gfp_t gfp
内存分配标志。
描述
禁用软中断(softirq)版本的 xa_reserve()
。
上下文
任何上下文。在禁用软中断(softirqs)的同时获取并释放 xa_lock。
返回
如果预留成功则返回 0,失败则返回 -ENOMEM。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
gfp_t gfp
内存分配标志。
描述
禁用中断版本的 xa_reserve()
。
上下文
进程上下文。在禁用中断的同时获取并释放 xa_lock。
返回
如果预留成功则返回 0,失败则返回 -ENOMEM。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
描述
调用 xa_reserve()
后,你可以调用此函数来释放预留。如果 index 处的条目已存储数据,则此函数不执行任何操作。
-
bool xa_is_sibling(const void *entry)¶
该条目是兄弟条目吗?
参数
const void *entry
从 XArray 检索到的条目
返回
如果该条目是兄弟条目,则为 true
。
-
bool xa_is_retry(const void *entry)¶
该条目是重试条目吗?
参数
const void *entry
从 XArray 检索到的条目
返回
如果该条目是重试条目,则为 true
。
-
bool xa_is_advanced(const void *entry)¶
该条目是否仅限于高级 API 使用?
参数
const void *entry
要存储到 XArray 中的条目。
返回
如果该条目无法通过普通 API 存储,则为 true
。
-
xa_update_node_t¶
类型定义(Typedef):XArray 的回调函数。
语法
void xa_update_node_t (struct xa_node *node)
参数
struct xa_node *node
正在处理的节点
描述
每当 XArray 更新节点中存在和值条目的计数时,都会调用此函数。它允许高级用户维护节点中的 private_list。
上下文
xa_lock 被持有,并且中断可能被禁用。实现不应释放 xa_lock,也不应重新启用中断。
-
XA_STATE¶
XA_STATE (name, array, index)
声明一个 XArray 操作状态。
参数
name
此操作状态的名称(通常为 xas)。
array
要操作的数组。
index
感兴趣的初始索引。
描述
在栈上声明并初始化一个 xa_state。
-
XA_STATE_ORDER¶
XA_STATE_ORDER (name, array, index, order)
声明一个 XArray 操作状态。
参数
name
此操作状态的名称(通常为 xas)。
array
要操作的数组。
index
感兴趣的初始索引。
order
条目的阶(order)。
描述
在栈上声明并初始化一个 xa_state。此 XA_STATE()
变体允许你指定要操作的元素的“阶(order)”。
-
int xas_error(const struct xa_state *xas)¶
返回存储在 xa_state 中的 errno。
参数
const struct xa_state *xas
XArray 操作状态。
返回
如果没有记录错误则返回 0。如果记录了错误则返回一个负的 errno。
-
void *xas_set_err(struct xa_state *xas, long err)¶
在 xa_state 中记录一个错误。
参数
struct xa_state *xas
XArray 操作状态。
long err
负错误码。
描述
仅使用负 err 调用此函数;零或正错误可能不会按你预期的方式运行。如果你想清除 xa_state 中的错误,请使用 xas_reset()
。
-
bool xas_invalid(const struct xa_state *xas)¶
xas 是否处于重试或错误状态?
参数
const struct xa_state *xas
XArray 操作状态。
返回
如果 xas 无法用于操作,则为 true
。
-
bool xas_valid(const struct xa_state *xas)¶
xas 是否是数组中的有效游标?
参数
const struct xa_state *xas
XArray 操作状态。
返回
如果 xas 可用于操作,则为 true
。
-
bool xas_is_node(const struct xa_state *xas)¶
xas 是否指向一个节点?
参数
const struct xa_state *xas
XArray 操作状态。
返回
如果 xas 当前引用一个节点,则为 true
。
-
void *xas_reset(struct xa_state *xas)¶
重置 XArray 操作状态。
参数
struct xa_state *xas
XArray 操作状态。
描述
重置 xas 的错误或遍历状态,以便将来对数组的遍历将从根节点开始。如果你已释放 xarray 锁并想重用 xa_state,请使用此功能。
上下文
任何上下文。
-
bool xas_retry(struct xa_state *xas, const void *entry)¶
如果合适,重试操作。
参数
struct xa_state *xas
XArray 操作状态。
const void *entry
来自 xarray 的条目。
描述
高级函数有时可能会返回一个内部条目,例如重试条目或零条目。此函数将 xas 设置为在需要时从数组头部重新开始遍历。
上下文
任何上下文。
返回
如果操作需要重试,则为 true。
-
void *xas_reload(struct xa_state *xas)¶
从 xarray 重新获取一个条目。
参数
struct xa_state *xas
XArray 操作状态。
描述
使用此函数检查先前加载的条目是否仍具有相同的值。这对于无锁页缓存查找非常有用,在该查找中,我们仅使用 RCU 锁来保护我们遍历数组,锁定页面,然后检查页面自我们查找以来是否未移动。
调用者保证 xas 仍然有效。如果它可能处于错误或重新启动状态,请改用 xas_load()
。
返回
xarray 中此位置的条目。
-
void *xas_set(struct xa_state *xas, unsigned long index)¶
为不同索引设置 XArray 操作状态。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long index
XArray 中的新索引。
描述
将操作状态移动到引用不同的索引。这将导致从顶部开始遍历;请参阅 xas_next()
以移动到相邻索引。
-
void *xas_advance(struct xa_state *xas, unsigned long index)¶
跳过兄弟条目。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long index
最后一个兄弟条目的索引。
描述
将操作状态移动到引用最后一个兄弟条目。这对于通常希望看到兄弟条目但有时希望跳过它们的循环很有用。如果你想移动到一个不属于此条目的索引,请使用 xas_set()
。
-
void *xas_set_order(struct xa_state *xas, unsigned long index, unsigned int order)¶
为多槽条目设置 XArray 操作状态。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long index
操作的目标。
unsigned int order
条目占用 2^**order** 个索引。
-
void *xas_set_update(struct xa_state *xas, xa_update_node_t update)¶
为回调设置 XArray 操作状态。
参数
struct xa_state *xas
XArray 操作状态。
xa_update_node_t update
更新节点时要调用的函数。
描述
XArray 可以在更新 xa_node 后通知调用者。这是高级功能,仅页缓存和交换缓存需要。
-
void *xas_next_entry(struct xa_state *xas, unsigned long max)¶
将迭代器前进到下一个存在的条目。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long max
要返回的最高索引。
描述
xas_next_entry()
是一个内联函数,用于优化 xarray 遍历以提高速度。它等同于调用 xas_find()
,并将为所有复杂情况调用 xas_find()
。
返回
xas 当前引用的条目之后的下一个存在的条目。
-
void *xas_next_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)¶
将迭代器前进到下一个已标记的条目。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long max
要返回的最高索引。
xa_mark_t mark
要搜索的标记。
描述
xas_next_marked()
是一个内联函数,用于优化 xarray 遍历以提高速度。它等同于调用 xas_find_marked()
,并将为所有复杂情况调用 xas_find_marked()
。
返回
xas 当前引用的条目之后的下一个已标记的条目。
-
xas_for_each¶
xas_for_each (xas, entry, max)
迭代 XArray 的一个范围。
参数
xas
XArray 操作状态。
entry
从数组中检索到的条目。
max
从数组中检索的最高索引。
描述
循环体将为 xarray 中当前 xas 位置和 max 之间的每个存在的条目执行。entry 将被设置为从 xarray 中检索到的条目。在循环体中从数组中删除条目是安全的。在迭代时,你应持有 RCU 锁或 xa_lock。如果你需要释放锁,请首先调用 xas_pause()
。
-
xas_for_each_marked¶
xas_for_each_marked (xas, entry, max, mark)
迭代 XArray 的一个范围。
参数
xas
XArray 操作状态。
entry
从数组中检索到的条目。
max
从数组中检索的最高索引。
mark
要搜索的标记。
描述
循环体将为 xarray 中当前 xas 位置和 max 之间的每个已标记条目执行。entry 将被设置为从 xarray 中检索到的条目。在循环体中从数组中删除条目是安全的。在迭代时,你应持有 RCU 锁或 xa_lock。如果你需要释放锁,请首先调用 xas_pause()
。
-
xas_for_each_conflict¶
xas_for_each_conflict (xas, entry)
迭代 XArray 的一个范围。
参数
xas
XArray 操作状态。
entry
从数组中检索到的条目。
描述
循环体将为 xas 指定范围内的 XArray 中的每个条目执行。如果循环正常终止,entry 将为 NULL
。用户可以跳出循环,这将使 entry 设置为冲突条目。调用者也可以调用 xa_set_err() 以在设置错误记录原因的同时退出循环。
-
void *xas_prev(struct xa_state *xas)¶
将迭代器移动到前一个索引。
参数
struct xa_state *xas
XArray 操作状态。
描述
如果 xas 处于错误状态,它将保持错误状态,并且此函数将返回 NULL
。如果 xas 从未被遍历,它将产生调用 xas_load()
的效果。否则,索引将减一,并且状态将被遍历到数组中进行下一次操作的正确位置。
如果迭代器引用索引 0,此函数将循环到 ULONG_MAX
。
返回
新索引处的条目。这可能是 NULL
或一个内部条目。
-
void *xas_next(struct xa_state *xas)¶
将状态移动到下一个索引。
参数
struct xa_state *xas
XArray 操作状态。
描述
如果 xas 处于错误状态,它将保持错误状态,并且此函数将返回 NULL
。如果 xas 从未被遍历,它将产生调用 xas_load()
的效果。否则,索引将加一,并且状态将被遍历到数组中进行下一次操作的正确位置。
如果迭代器引用索引 ULONG_MAX
,此函数将循环到 0。
返回
新索引处的条目。这可能是 NULL
或一个内部条目。
-
void *xas_load(struct xa_state *xas)¶
从 XArray 加载一个条目(高级)。
参数
struct xa_state *xas
XArray 操作状态。
描述
通常将 xas 遍历到适当状态以加载存储在 xa_index 的条目。但是,如果 xas 处于错误状态,它将不执行任何操作并返回 NULL
。xas_load()
永远不会展开树。
如果 xa_state 设置为操作多索引条目,xas_load()
可能会返回 NULL
或内部条目,即使 xas 指定的范围内存在条目。
上下文
任何上下文。调用者应持有 xa_lock 或 RCU 锁。
返回
通常是 XArray 中的一个条目,但有关例外情况请参阅描述。
-
bool xas_nomem(struct xa_state *xas, gfp_t gfp)¶
如果需要,分配内存。
参数
struct xa_state *xas
XArray 操作状态。
gfp_t gfp
内存分配标志。
描述
如果我们需要向 XArray 添加新节点,我们会在持有锁的同时尝试使用 GFP_NOWAIT 分配内存,这通常会成功。如果失败,则 xas 会被标记为需要内存才能继续。调用者应释放锁并调用 xas_nomem()
。如果 xas_nomem()
成功,调用者应重试操作。
前进进度得到保证,因为一个节点在此处分配并存储在 xa_state 中,xas_alloc() 将在那里找到它。更多节点可能会在 slab 分配器中找到,但我们不会在这里占用它们。
返回
如果需要内存且已成功分配,则为 true。
-
void *xas_free_nodes(struct xa_state *xas, struct xa_node *top)¶
释放此节点及其引用的所有节点
参数
struct xa_state *xas
数组操作状态。
struct xa_node *top
要释放的节点
描述
此节点已从树中删除。我们现在必须释放它及其所有子节点。可能存在带有对树引用数据的 RCU 遍历器,因此我们必须用重试标记替换所有条目。
-
void *xas_create_range(struct xa_state *xas)¶
确保对此范围的存储操作将成功
参数
struct xa_state *xas
XArray 操作状态。
描述
创建 xas 覆盖范围内所有槽位。设置 xas 创建单索引条目并将其定位在范围的开头。这有利于尚未转换为使用多索引条目的用户。
-
void *xas_store(struct xa_state *xas, void *entry)¶
将此条目存储到 XArray 中。
参数
struct xa_state *xas
XArray 操作状态。
void *entry
新条目。
描述
如果 xas 正在操作多索引条目,此函数返回的条目本质上是无意义的(它可能是内部条目,也可能是 NULL
,即使该范围覆盖的某些索引处存在非 NULL 条目)。这对任何现有用户都不是问题,并且如果需要可以更改。
返回
此索引处的旧条目。
-
bool xas_get_mark(const struct xa_state *xas, xa_mark_t mark)¶
返回此标记的状态。
参数
const struct xa_state *xas
XArray 操作状态。
xa_mark_t mark
标记号。
返回
如果标记已设置则返回 true,如果标记已清除或 xas 处于错误状态则返回 false。
-
void *xas_set_mark(const struct xa_state *xas, xa_mark_t mark)¶
在此条目及其父节点上设置标记。
参数
const struct xa_state *xas
XArray 操作状态。
xa_mark_t mark
标记号。
描述
在此条目上设置指定的标记,并沿树向上,在其所有祖先条目上设置标记。如果 xas 尚未遍历到某个条目,或者处于错误状态,则不执行任何操作。
-
void xas_clear_mark(const struct xa_state *xas, xa_mark_t mark)¶
清除此条目及其父级上的标记。
参数
const struct xa_state *xas
XArray 操作状态。
xa_mark_t mark
标记号。
描述
清除此条目上指定的标记,并回溯到头部,尝试清除所有祖先条目上的标记。如果 xas 尚未遍历到某个条目,或者处于错误状态,则不执行任何操作。
-
void xas_init_marks(const struct xa_state *xas)¶
初始化此条目的所有标记
参数
const struct xa_state *xas
数组操作状态。
描述
初始化由 xas 指定的条目的所有标记。如果正在使用标记跟踪空闲条目,则需要在所有条目上设置此标记。所有其他标记都将被清除。
此实现效率不高;我们可能会多次遍历树。
-
void xas_split_alloc(struct xa_state *xas, void *entry, unsigned int order, gfp_t gfp)¶
为拆分条目分配内存。
参数
struct xa_state *xas
XArray 操作状态。
void *entry
将存储在数组中的新条目。
unsigned int order
当前条目阶。
gfp_t gfp
内存分配标志。
描述
此函数应在调用 xas_split()
之前调用。如有必要,它将分配新节点(并用 entry 填充它们),以准备将 order 大小的条目拆分为存储在 xas 中的阶的条目。
上下文
如果 gfp 标志允许,可能会睡眠。
-
void xas_split(struct xa_state *xas, void *entry, unsigned int order)¶
将多索引条目拆分为更小的条目。
参数
struct xa_state *xas
XArray 操作状态。
void *entry
要存储在数组中的新条目。
unsigned int order
当前条目阶。
描述
新条目的大小在 xas 中设置。**entry** 中的值将被复制到所有替换条目。
上下文
任何上下文。调用者应持有 xa_lock。
-
unsigned int xas_try_split_min_order(unsigned int order)¶
xas_try_split()
可接受的最小拆分阶
参数
unsigned int order
当前条目阶。
描述
如果不需要新的 xa_node,xas_try_split()
可以将多索引条目拆分到小于 order - 1 的大小。此函数提供了 xas_try_split()
支持的最小阶。
返回
xas_try_split()
支持的最小阶
上下文
任何上下文。
-
void *xas_try_split(struct xa_state *xas, void *entry, unsigned int order)¶
尝试拆分多索引条目。
参数
struct xa_state *xas
XArray 操作状态。
void *entry
要存储在数组中的新条目。
unsigned int order
当前条目阶。
描述
新条目的大小在 xas 中设置。**entry** 中的值将复制到所有替换条目。当且仅当需要一个新的 xa_node 时,如果 xas->xa_alloc 为 NULL,函数将使用 GFP_NOWAIT 获取一个。如果需要更多新的 xa_node,函数将返回 EINVAL 错误。
注意
如果想最小化 xas_try_split()
调用,请使用 xas_try_split_min_order()
获取下一个拆分阶,而不是 order - 1。
上下文
任何上下文。调用者应持有 xa_lock。
-
void xas_pause(struct xa_state *xas)¶
暂停遍历以释放锁。
参数
struct xa_state *xas
XArray 操作状态。
描述
某些用户需要暂停遍历并释放其持有的锁,以便让步给更高优先级的线程或对条目执行操作。这些用户在释放锁之前应调用此函数。它会重置 xas,使其在用户重新获得锁后适合下一次循环迭代。如果在遍历过程中发现的大多数条目都需要调用 xas_pause()
,则 xa_for_each()
迭代器可能更适合。
请注意,xas_pause()
仅适用于正向迭代。如果用户需要暂停反向迭代,我们将需要一个 xas_pause_rev()。
-
void *xas_find(struct xa_state *xas, unsigned long max)¶
在 XArray 中查找下一个存在的条目。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long max
要返回的最高索引。
描述
如果 xas 尚未遍历到某个条目,则返回索引 >= xas.xa_index 的条目。如果已遍历,则当前指向的条目已处理,因此我们移动到下一个条目。
如果未找到条目且数组小于 max,则迭代器将设置为数组中尚未存在的最小索引。这使得 xas 可以立即传递给 xas_store()
。
返回
如果找到条目则返回该条目,否则返回 NULL
。
-
void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)¶
在 XArray 中查找下一个被标记的条目。
参数
struct xa_state *xas
XArray 操作状态。
unsigned long max
要返回的最高索引。
xa_mark_t mark
要搜索的标记号。
描述
如果 xas 尚未遍历到某个条目,则返回索引 >= xas.xa_index 的标记条目。如果已遍历,则当前指向的条目已处理,因此我们返回索引 > xas.xa_index 的第一个标记条目。
如果未找到标记条目且数组小于 max,则 xas 被设置为边界状态,并且 xas->xa_index 被设置为数组中尚未存在的最小索引。这使得 xas 可以立即传递给 xas_store()
。
如果在达到 max 之前未找到条目,则 xas 被设置为重启状态。
返回
如果找到条目则返回该条目,否则返回 NULL
。
-
void *xas_find_conflict(struct xa_state *xas)¶
在指定范围内查找下一个存在的条目。
参数
struct xa_state *xas
XArray 操作状态。
描述
xas 描述了一个范围以及该范围内的位置。
上下文
任何上下文。要求持有 xa_lock。
返回
xas 覆盖范围内的下一个条目,或 NULL
。
参数
struct xarray *xa
XArray。
unsigned long index
数组中的索引。
上下文
任何上下文。获取并释放 RCU 锁。
返回
xa 中 index 处的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
描述
此函数返回后,从 index 加载将返回 NULL
。如果此索引是多索引条目的一部分,则所有索引都将被擦除,并且没有任何条目会是多索引条目的一部分。
上下文
任何上下文。进入时要求持有 xa_lock。
返回
曾经在此索引处的条目。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
描述
此函数返回后,从 index 加载将返回 NULL
。如果此索引是多索引条目的一部分,则所有索引都将被擦除,并且没有任何条目会是多索引条目的一部分。
上下文
任何上下文。获取并释放 xa_lock。
返回
曾经在此索引处的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
调用此函数时,您必须已持有 xa_lock。如果需要分配内存,它会释放锁,然后重新获取。
上下文
任何上下文。进入时要求持有 xa_lock。如果 gfp 标志允许,可能会释放并重新获取 xa_lock。
返回
此索引处的旧条目,如果发生错误则为 xa_err()
。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
此函数返回后,从该索引加载将返回 entry。存储到现有的多索引条目会更新每个索引的条目。与 index 关联的标记不受影响,除非 entry 为 NULL
。
上下文
任何上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会休眠。
返回
成功时返回此索引处的旧条目;如果 entry 无法存储在 XArray 中,则返回 xa_err(-EINVAL);如果内存分配失败,则返回 xa_err(-ENOMEM)。
-
void *__xa_cmpxchg(struct xarray *xa, unsigned long index, void *old, void *entry, gfp_t gfp)¶
有条件地替换 XArray 中的条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *old
用于比较的旧值。
void *entry
要放入数组的新值。
gfp_t gfp
内存分配标志。
描述
调用此函数时,您必须已持有 xa_lock。如果需要分配内存,它会释放锁,然后重新获取。
如果 index 处的条目与 old 相同,则将其替换为 entry。如果返回值等于 old,则交换成功。
上下文
任何上下文。进入时要求持有 xa_lock。如果 gfp 标志允许,可能会释放并重新获取 xa_lock。
返回
此索引处的旧值,如果发生错误则为 xa_err()
。
-
int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)¶
如果 XArray 中不存在条目,则存储此条目。
参数
struct xarray *xa
XArray。
unsigned long index
数组索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
如果不存在任何条目,插入 NULL 条目将存储一个保留条目(例如 xa_reserve()
)。如果存在保留条目,插入将失败,即使从该索引加载会返回 NULL。
上下文
任何上下文。进入时要求持有 xa_lock。如果 gfp 标志允许,可能会释放并重新获取 xa_lock。
返回
存储成功则返回 0。如果存在其他条目则返回 -EBUSY。如果无法分配内存则返回 -ENOMEM。
-
void *xa_store_range(struct xarray *xa, unsigned long first, unsigned long last, void *entry, gfp_t gfp)¶
将此条目存储在 XArray 的一系列索引中。
参数
struct xarray *xa
XArray。
unsigned long first
要影响的起始索引。
unsigned long last
要影响的结束索引。
void *entry
新条目。
gfp_t gfp
内存分配标志。
描述
此函数返回后,从 first 和 last(包括两者)之间的任何索引加载都将返回 entry。存储到现有的多索引条目会更新每个索引的条目。与 index 关联的标记不受影响,除非 entry 为 NULL
。
上下文
进程上下文。获取并释放 xa_lock。如果 gfp 标志允许,可能会睡眠。
返回
成功时返回 NULL
;如果 entry 无法存储在 XArray 中,则返回 xa_err(-EINVAL);如果内存分配失败,则返回 xa_err(-ENOMEM)。
-
int xas_get_order(struct xa_state *xas)¶
获取条目的阶。
参数
struct xa_state *xas
XArray 操作状态。
描述
在 xas_load 之后调用,xas 不应处于错误状态。
返回
一个介于 0 到 63 之间的数字,表示条目的阶。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
返回
一个介于 0 到 63 之间的数字,表示条目的阶。
-
int __xa_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
分配 ID 的范围。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
上下文
任何上下文。进入时要求持有 xa_lock。如果 gfp 标志允许,可能会释放并重新获取 xa_lock。
返回
成功则返回 0,如果无法分配内存则返回 -ENOMEM,或者如果 limit 中没有空闲条目则返回 -EBUSY。
-
int __xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, u32 *next, gfp_t gfp)¶
在 XArray 中找到存储此条目的位置。
参数
struct xarray *xa
XArray。
u32 *id
ID 的指针。
void *entry
新条目。
struct xa_limit limit
已分配 ID 的范围。
u32 *next
指向下一个要分配的 ID 的指针。
gfp_t gfp
内存分配标志。
描述
在 limit.min 和 limit.max 之间找到 xa 中的空条目,将索引存储到 id 指针中,然后将条目存储在该索引处。并发查找不会看到未初始化的 id。空条目的搜索将从 next 开始,如果需要,将循环查找。
仅可在使用 xa_init_flags()
设置了 XA_FLAGS_ALLOC 标志的 xarray 上操作。
上下文
任何上下文。进入时要求持有 xa_lock。如果 gfp 标志允许,可能会释放并重新获取 xa_lock。
返回
如果分配成功且未循环,则返回 0。如果分配成功但在循环后,则返回 1;如果无法分配内存,则返回 -ENOMEM;如果 limit 中没有空闲条目,则返回 -EBUSY。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
xa_mark_t mark
标记号。
描述
尝试在 NULL
条目上设置标记不会成功。
上下文
任何上下文。进入时要求持有 xa_lock。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
xa_mark_t mark
标记号。
上下文
任何上下文。进入时要求持有 xa_lock。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
xa_mark_t mark
标记号。
描述
此函数使用 RCU 读锁,因此返回时结果可能已过时。如果需要结果稳定,请使用锁。
上下文
任何上下文。获取并释放 RCU 锁。
返回
如果 index 处的条目设置了此标记,则返回 True;否则返回 False。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
xa_mark_t mark
标记号。
描述
尝试在 NULL
条目上设置标记不会成功。
上下文
进程上下文。获取并释放 xa_lock。
参数
struct xarray *xa
XArray。
unsigned long index
条目的索引。
xa_mark_t mark
标记号。
描述
清除标记总是成功的。
上下文
进程上下文。获取并释放 xa_lock。
-
void *xa_find(struct xarray *xa, unsigned long *indexp, unsigned long max, xa_mark_t filter)¶
在 XArray 中搜索一个条目。
参数
struct xarray *xa
XArray。
unsigned long *indexp
指向一个索引的指针。
unsigned long max
要搜索的最大索引。
xa_mark_t filter
选择标准。
描述
查找 xa 中与 filter 匹配且索引不小于 indexp 且不大于 max 的最小索引处的条目。如果找到条目,indexp 将更新为该条目的索引。此函数受 RCU 读锁保护,因此可能无法找到同时添加的条目。它不会返回 XA_RETRY_ENTRY
;如果需要查看重试条目,请使用 xas_find()
。
上下文
任何上下文。获取并释放 RCU 锁。
返回
如果找到条目则返回该条目,否则返回 NULL
。
-
void *xa_find_after(struct xarray *xa, unsigned long *indexp, unsigned long max, xa_mark_t filter)¶
在 XArray 中搜索一个存在的条目。
参数
struct xarray *xa
XArray。
unsigned long *indexp
指向一个索引的指针。
unsigned long max
要搜索的最大索引。
xa_mark_t filter
选择标准。
描述
查找 xa 中与 filter 匹配且索引大于 indexp 且不大于 max 的最小索引处的条目。如果找到条目,indexp 将更新为该条目的索引。此函数受 RCU 读锁保护,因此可能会遗漏同时添加的条目。它不会返回 XA_RETRY_ENTRY
;如果需要查看重试条目,请使用 xas_find()
。
上下文
任何上下文。获取并释放 RCU 锁。
返回
如果找到,则返回指针;否则返回 NULL
。
-
unsigned int xa_extract(struct xarray *xa, void **dst, unsigned long start, unsigned long max, unsigned int n, xa_mark_t filter)¶
将选定的条目从 XArray 复制到普通数组。
参数
struct xarray *xa
要从中复制的源 XArray。
void **dst
要将条目复制到的缓冲区。
unsigned long start
XArray 中符合选择条件的首个索引。
unsigned long max
XArray 中符合选择条件的最后一个索引。
unsigned int n
要复制的最大条目数。
xa_mark_t filter
选择标准。
描述
从 XArray 复制最多 n 个与 filter 匹配的条目。复制的条目的索引将在 start 和 max 之间(包括两者)。
filter 可以是一个 XArray 标记值,在这种情况下,带有该标记的条目将被复制。它也可以是 XA_PRESENT
,在这种情况下,所有非 NULL
的条目都将被复制。
返回的条目可能不代表 XArray 在某个时间点的快照。例如,如果另一个线程先存储到索引 5,然后存储到索引 10,则调用 xa_extract()
可能会返回索引 5 的旧内容和索引 10 的新内容。此函数运行期间未修改的索引不会被跳过。
如果需要更强的保证,在调用此函数时持有 xa_lock 将防止并发修改。
上下文
任何上下文。获取并释放 RCU 锁。
返回
复制的条目数。
-
void xa_delete_node(struct xa_node *node, xa_update_node_t update)¶
工作集代码的私有接口。
参数
struct xa_node *node
要从树中移除的节点。
xa_update_node_t update
用于更新祖先节点的函数。
上下文
进入时必须持有 xa_lock 且不会释放。
参数
struct xarray *xa
XArray。
描述
调用此函数后,XArray 为空并已释放为其内部数据结构分配的所有内存。您负责释放 XArray 引用的对象。
上下文
任何上下文。获取并释放 xa_lock,中断安全。