[Linux]学习笔记系列 -- lib/xarray.c eXtensible Array (XArray) 可扩展数组 - 教程

news/2025/10/21 12:09:41/文章来源:https://www.cnblogs.com/tlnshuju/p/19154742

[Linux]学习笔记系列 -- lib/xarray.c eXtensible Array (XArray) 可扩展数组 - 教程

2025-10-21 12:07  tlnshuju  阅读(0)  评论(0)    收藏  举报

文章目录

  • lib/xarray.c eXtensible Array (XArray) 可扩展数组
      • 历史与背景
        • 这项技术是为了解决什么特定问题而诞生的?
        • 它的发展经历了哪些重要的里程碑或版本迭代?
        • 目前该技术的社区活跃度和主流应用情况如何?
      • 核心原理与设计
        • 它的核心工作原理是什么?
        • 它的主要优势体现在哪些方面?
        • 它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
      • 使用场景
        • 在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。
        • 是否有不推荐使用该技术的场景?为什么?
      • 对比分析
        • 请将其 与 其他相似技术 进行详细对比。
  • include/linux/xarray.h
    • XARRAY_INIT
    • xa_init_flags 初始化带有标志的空 XArray
    • xa_init 初始化一个空的 XArray
    • xa_marked 检查 XArray 中是否有任何条目设置了特定标记
    • xas_retry 用于判断是否需要重试 XArray 操作,并在必要时重置操作状态(xa_state)
    • xas_reload 重新获取 XArray 中指定位置的条目
  • lib/xarray.c
    • xa_entry 获取xa_entry条目
    • xas_descend 在 XArray 数据结构中从当前节点向下遍历到子节点或获取目标条目
    • xas_reload 从xarray中重新获取一个条目
    • xas_start 初始化或重新加载 xa_state(XArray 操作状态)以开始遍历 XArray 数据结构
    • xa_load 加载XArray
    • xas_store 将此条目存储在 XArray 中
    • xas_find_marked 在 XArray 数据结构中查找具有特定标记(mark)的下一个条目
    • xas_find_marked
    • __xa_alloc 在 XArray 中分配一个新的条目

lib/xarray.c eXtensible Array (XArray) 可扩展数组

在这里插入图片描述

https://github.com/wdfk-prog/linux-study

历史与背景

这项技术是为了解决什么特定问题而诞生的?

XArray(eXtensible Array,可扩展数组)的诞生是为了替代并改进Linux内核中一个非常重要但使用起来十分复杂的数据结构——Radix Tree。Radix Tree长期以来是内核(尤其是页缓存 page cache)用于管理稀疏长整型索引到指针映射的核心组件,但它存在诸多问题:

  • 复杂的API和使用模式:Radix Tree的API不够直观,开发者需要自行处理很多棘手的边界情况,比如存储NULL指针、处理特殊的“异常条目”(exceptional entries),以及管理并发访问的锁。这使得代码容易出错且难以维护。
  • 锁机制外部化:Radix Tree本身不提供锁机制,使用者必须在外部实现自己的锁来保护数据结构,这增加了驱动和子系统开发者的负担。
  • 内存预加载问题:为了在持有锁时避免睡眠(因分配内存可能导致睡眠),Radix Tree的使用者通常需要“预加载”节点内存,这种机制复杂且可能浪费内存。
  • 功能局限:Radix Tree API缺少一些常见的功能,如“比较并交换”(compare-and-swap)操作,导致多个使用者需要自己实现类似的逻辑。此外,它只能存储指针或特殊的“异常条目”,灵活性不足。

XArray被设计为一个全新的抽象层,它保留了Radix Tree高效的底层实现,但提供了一个更安全、更易用、功能更丰富的API,从根本上解决了上述问题。

它的发展经历了哪些重要的里程碑或版本迭代?
目前该技术的社区活跃度和主流应用情况如何?

XArray现在是Linux内核中用于管理稀疏索引到指针映射的标准和首选数据结构。它已经完全取代了Radix Tree在内核新代码中的地位,并且大量旧代码也已被迁移。

  • 核心应用:页缓存(page cache)是其最重要和最复杂的用户。
  • 广泛使用:它也被用于IDR(ID Allocator)的替代实现、I/O子系统(如io_uring)、内存管理等多个关键领域。
  • API演进:它的API被认为是后续新数据结构(如Maple Tree)设计的范本。

核心原理与设计

它的核心工作原理是什么?

XArray在概念上表现为一个可以自动扩容的、巨大的指针数组,其索引是unsigned long类型。 尽管其外部表现为数组,但其内部实现仍然基于经过优化的Radix Tree结构。

  1. 树状结构:与Radix Tree类似,XArray使用多级树来存储数据。索引(一个长整型)被拆分成多个比特块,每个比特块用于在树的一层中选择一个槽(slot)。通过逐层向下遍历,最终可以定位到与完整索引对应的指针。
  2. 动态分配:XArray初始时不占用任何内存。当第一次向某个索引存储数据时,它会按需分配树节点。这使得它对于存储稀疏索引(索引之间间隔很大)非常高效。
  3. 内置锁:XArray结构体中内嵌了一个自旋锁(spinlock),其API(如 xa_store()xa_erase())会自动处理加锁和解锁,极大地简化了并发编程。
  4. RCU安全查找:查找操作(xa_load())是无锁的,并且利用了RCU(Read-Copy-Update)机制,允许读操作与写操作并行,提高了并发性能。
它的主要优势体现在哪些方面?
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
  • 不适合哈希索引:XArray对连续或聚集的索引表现最优。如果将对象的哈希值作为索引,会导致索引分布稀疏且随机,这会降低其性能和内存效率,因为可能需要创建很深的树来存储单个条目。
  • 索引范围限制:索引必须是unsigned long类型,无法支持更大的索引范围。
  • 非最优的范围操作:虽然支持存储多索引条目,但对于需要高效进行大量范围操作(如查找所有与某个范围重叠的条目)的场景,新的Maple Tree数据结构可能更优。

使用场景

在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。

XArray是内核中将长整型ID映射到指针的通用首选方案,特别适用于ID稀疏分布的场景。

  • 例一:文件页缓存(Page Cache)
    这是XArray最经典的应用。当内核需要读写文件时,它将文件的页偏移量(一个pgoff_t,本质上是unsigned long)作为索引,将指向内存中对应struct page的指针存储在文件的i_pages XArray中。由于用户可能只访问了大文件中的某几部分,这些页偏移量是稀疏的,XArray能高效地管理这种映射。
  • 例二:ID分配器(IDR)
    内核需要为各种对象(如IPC对象、网络设备等)分配唯一的32位整数ID。新的IDR实现就基于XArray,它使用XArray来查找一个未被使用的ID(即XArray中为NULL的索引),并将该ID与指向内核对象的指针关联起来。
  • 例-三:I/O环形缓冲区(io_uring)
    io_uring接口允许用户空间和内核通过一个共享的环形缓冲区高效地提交I/O请求。内核端使用XArray来管理注册的文件描述符,将用户提供的fd作为索引,映射到内部的struct file指针。
是否有不推荐使用该技术的场景?为什么?

对比分析

请将其 与 其他相似技术 进行详细对比。
特性XArrayRadix Tree (已被取代)IDR (旧实现)普通数组哈希表
功能概述用于稀疏长整型索引到指针映射的通用、安全且功能丰富的数据结构。Radix Tree的底层实现,API复杂且不安全。基于Radix Tree的32位整数ID分配和查找机制。用于密集、小范围索引的简单指针数组。用于任意类型键到值的映射,通过哈希函数定位。
实现方式优化的多级Radix Tree。多级Radix Tree。Radix Tree + 位图进行ID分配。连续的内存块。哈希函数 + 桶数组(通常是链表或树)。
API与安全性非常高。API简洁,内置锁,RCU安全查找。。API复杂,需要外部锁,易用错。中等。API专用,比Radix Tree简单,但不如XArray通用。非常高。就是原生数组访问。中等。需要提供好的哈希函数和处理冲突。
稀疏索引支持优秀。专为此设计,内存效率高。优秀优秀。会浪费大量空间存储空指针。优秀
性能开销查找为对数时间复杂度,但常数因子小。插入/删除有锁开销。与XArray底层类似,但API使用不当会引入额外开销。与Radix Tree类似。常数时间 O(1),非常快。平均为常数时间 O(1),最差为O(n)。
资源占用与存储的条目数量成正比,非常节省。与XArray类似。与Radix Tree类似。与索引范围最大值成正比,可能非常浪费。需要预分配桶数组,占用固定内存。
典型用途页缓存新IDRio_uring、文件描述符管理。(历史) 页缓存、旧IDR。(历史) IPC对象ID、设备ID分配。进程的文件描述符表(files_struct中的fd_array)。dentry缓存、网络连接跟踪。

include/linux/xarray.h

XARRAY_INIT

#define XARRAY_INIT(name, flags) {				\
.xa_lock = __SPIN_LOCK_UNLOCKED(name.xa_lock),		\
.xa_flags = flags,					\
.xa_head = NULL,					\
}

xa_init_flags 初始化带有标志的空 XArray

/**
* xa_init_flags() - 初始化带有标志的空 XArray。
* @xa: XArray.
* @flags: XA_FLAG values.
*
* 如果您需要使用特殊标志初始化 XArray(例如,您需要从中断上下文中获取锁),请使用此函数而不是 xa_init()。
*
* Context: Any context.
*/
static inline void xa_init_flags(struct xarray *xa, gfp_t flags)
{
spin_lock_init(&xa->xa_lock);
xa->xa_flags = flags;
xa->xa_head = NULL;
}

xa_init 初始化一个空的 XArray

/**
* xa_init() - 初始化一个空的 XArray.
* @xa: XArray.
*
* An empty XArray is full of NULL entries.
*
* Context: Any context.
*/
static inline void xa_init(struct xarray *xa)
{
xa_init_flags(xa, 0);
}

xa_marked 检查 XArray 中是否有任何条目设置了特定标记

/**
* xa_marked() - 查询此数组中的任何条目是否具有标记集
* @xa:数组
* @mark:标记值
*
* 上下文:任何上下文。
* 如果任何条目设置了此标记,则返回: %true。
*/
static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark)
{
return xa->xa_flags & XA_FLAGS_MARK(mark);
}

xas_retry 用于判断是否需要重试 XArray 操作,并在必要时重置操作状态(xa_state)

/**
* xas_retry() - 如果合适,请重试该作。
* @xas:XArray作状态。
* @entry:来自 xarray 的条目。
*
* 高级函数有时可能会返回内部条目,例如重试条目或零条目。 此函数将 @xas 设置为根据需要从数组的头部重新启动 walk。
*
* 上下文:任何上下文。
* 返回:如果需要重试作,则为 true。
*/
static inline bool xas_retry(struct xa_state *xas, const void *entry)
{
/* entry 是一个零条目(表示空或无效的条目) */
if (xa_is_zero(entry))
return true;
/* entry 不是一个重试条目, */
if (!xa_is_retry(entry))
return false;
/* entry 是一个重试条目,调用 xas_reset 重置操作状态,使 xa_state 从数组的头部重新开始遍历 */
xas_reset(xas);
return true;
}

xas_reload 重新获取 XArray 中指定位置的条目

  • 主要用途是验证之前加载的条目是否仍然保持相同的值,特别是在无锁的页面缓存查找场景中。通过重新加载条目,函数确保操作的正确性和一致性
/**
* xas_reload() - 从 xarray 中重新获取条目。
* @xas:XArray作状态。
*
* 使用此函数可检查先前加载的条目是否仍具有相同的值。
* 这对于无锁页面缓存查找很有用,在无锁中,我们只使用 RCU 锁遍历数组以保护我们,
* 锁定页面,然后检查页面在我们查找后是否没有移动。
*
* 调用方保证 @xas 仍然有效。 如果它可能处于错误或重启状态,请改为调用 xas_load()。
*
* Return:xarray 中此位置的条目。
*/
static inline void *xas_reload(struct xa_state *xas)
{
struct xa_node *node = xas->xa_node;
void *entry;
char offset;
/* 处理空节点的特殊情况 */
if (!node)
return xa_head(xas->xa);
if (IS_ENABLED(CONFIG_XARRAY_MULTI)) {
offset = (xas->xa_index >> node->shift) & XA_CHUNK_MASK;
entry = xa_entry(xas->xa, node, offset);
if (!xa_is_sibling(entry))
return entry;
offset = xa_to_sibling(entry);
} else {
/* 未启用多节点支持,直接使用操作状态中的偏移量(xa_offset) */
offset = xas->xa_offset;
}
/* 返回节点中的条目 */
return xa_entry(xas->xa, node, offset);
}

lib/xarray.c

xa_entry 获取xa_entry条目

/* Private */
static inline void *xa_entry(const struct xarray *xa,
const struct xa_node *node, unsigned int offset)
{
XA_NODE_BUG_ON(node, offset >= XA_CHUNK_SIZE);
return rcu_dereference_check(node->slots[offset],
lockdep_is_held(&xa->xa_lock));
}

xas_descend 在 XArray 数据结构中从当前节点向下遍历到子节点或获取目标条目

static __always_inline void *xas_descend(struct xa_state *xas,
struct xa_node *node)
{
unsigned int offset = get_offset(xas->xa_index, node);
void *entry = xa_entry(xas->xa, node, offset);
xas->xa_node = node;
while (xa_is_sibling(entry)) {/* 兄弟节点 */
offset = xa_to_sibling(entry);	/* 获取兄弟节点的偏移量 */
entry = xa_entry(xas->xa, node, offset);/*  获取兄弟节点对应的条目 */
/* 如果当前节点的层级非叶子节点(node->shift 非零)且条目是另一个节点(通过 xa_is_node(entry) 检查) */
if (node->shift && xa_is_node(entry))
/* 条目设置为 XA_RETRY_ENTRY,表示需要重新尝试 */
entry = XA_RETRY_ENTRY;
}
xas->xa_offset = offset;
return entry;
}

xas_reload 从xarray中重新获取一个条目

/**
* xas_reload() - 从xarray中重新获取一个条目。
* @xas: XArray operation state.
*
* Use this function to check that a previously loaded entry still has
* the same value.  This is useful for the lockless pagecache lookup where
* we walk the array with only the RCU lock to protect us, lock the page,
* then check that the page hasn't moved since we looked it up.
*
* The caller guarantees that @xas is still valid.  If it may be in an
* error or restart state, call xas_load() instead.
*
* Return: The entry at this location in the xarray.
*/
static inline void *xas_reload(struct xa_state *xas)
{
struct xa_node *node = xas->xa_node;
void *entry;
char offset;
if (!node)
return xa_head(xas->xa);
if (IS_ENABLED(CONFIG_XARRAY_MULTI)) {/* 支持多索引条目 */
offset = (xas->xa_index >> node->shift) & XA_CHUNK_MASK;
entry = xa_entry(xas->xa, node, offset);
if (!xa_is_sibling(entry))
return entry;
offset = xa_to_sibling(entry);
} else {
offset = xas->xa_offset;
}
return xa_entry(xas->xa, node, offset);
}

xas_start 初始化或重新加载 xa_state(XArray 操作状态)以开始遍历 XArray 数据结构

/*
* 开始一次遍历。如果 @xas 已经有效,我们假设它在正确的路径上,只需返回我们到达的位置。
* 如果我们处于错误状态,返回 NULL。如果索引超出了当前 xarray 的范围,返回 NULL,而不更改 @xas->xa_node。
* 否则,将 @xas->xa_node 设置为 NULL,并返回数组的当前头部。
*/
static void *xas_start(struct xa_state *xas)
{
void *entry;
if (xas_valid(xas))
return xas_reload(xas);
if (xas_error(xas))
return NULL;
/* 获取 XArray 的头部条目 */
entry = xa_head(xas->xa);
if (!xa_is_node(entry)) {
/* 头部条目不是节点 如果当前索引 xas->xa_index 不为 0 */
if (xas->xa_index)
/* 设置边界 */
return set_bounds(xas);
} else {
/* 查索引是否超出当前节点的范围 */
if ((xas->xa_index >> xa_to_node(entry)->shift) > XA_CHUNK_MASK)
/* 设置边界 */
return set_bounds(xas);
}
xas->xa_node = NULL;
return entry;
}

xa_load 加载XArray

/**
* xas_load() - 从 XArray 加载一个条目(高级)。
* @xas: XArray 操作状态。 *
* 通常会遍历 @xas 到适当状态以加载存储在 xa_index 的条目。然而,如果 @xas 处于错误状态,它将不会执行任何操作并返回 %NULL。xas_load() 不会扩展树。 *
* 如果 xa_state 设置为操作多索引条目,xas_load() 可能返回 %NULL 或内部条目,即使 @xas 指定的范围内存在条目。 *
* 上下文:任何上下文。调用者应持有 xa_lock 或 RCU 锁。
* 返回:通常是 XArray 中的一个条目,但请参阅描述中的例外情况。
*/
void *xas_load(struct xa_state *xas)
{
/* 根据 xas 的状态获取初始条目 */
void *entry = xas_start(xas);
while (xa_is_node(entry)) {
struct xa_node *node = xa_to_node(entry);
/* 检查当前节点的层级(node->shift)是否与 xas->xa_shift 匹配。如果 xas->xa_shift 大于 node->shift,说明已经到达目标层级,退出循环 */
if (xas->xa_shift > node->shift)
break;
/* 调用 xas_descend(xas, node) 进入子节点,继续遍历 */
entry = xas_descend(xas, node);
if (node->shift == 0)
break;
}
return entry;
}
EXPORT_SYMBOL_GPL(xas_load);

xas_store 将此条目存储在 XArray 中

/**
* xas_store() - 将此条目存储在 XArray 中。
* @xas: XArray operation state.
* @entry: New entry.
*
* 如果 @xas 在多重索引条目上操作,则该函数返回的条目基本上是毫无意义的(它可能是内部条目,或者可能是 %NULL,即使在范围覆盖的某些索引中有非 NULL 条目)。这对当前用户没有问题,如果需要可以进行更改。
*
* Return: The old entry at this index.
*/
void *xas_store(struct xa_state *xas, void *entry)
{
struct xa_node *node;
void __rcu **slot = &xas->xa->xa_head;
unsigned int offset, max;
int count = 0;
int values = 0;
void *first, *next;
bool value = xa_is_value(entry);
/* 如果 entry 非空,调用 xas_create 创建必要的节点或结构以存储条目 */
if (entry) {
bool allow_root = !xa_is_node(entry) && !xa_is_zero(entry);
first = xas_create(xas, allow_root);
} else {
first = xas_load(xas);
}-+
if (xas_invalid(xas))
return first;
node = xas->xa_node;
if (node && (xas->xa_shift < node->shift))xas->xa_sibs = 0;if ((first == entry) && !xas->xa_sibs)return first;next = first;offset = xas->xa_offset;max = xas->xa_offset + xas->xa_sibs;if (node) {slot = &node->slots[offset];if (xas->xa_sibs)xas_squash_marks(xas);}if (!entry)xas_init_marks(xas);for (;;) {/** Must clear the marks before setting the entry to NULL,* otherwise xas_for_each_marked may find a NULL entry and* stop early.  rcu_assign_pointer contains a release barrier* so the mark clearing will appear to happen before the* entry is set to NULL.*/rcu_assign_pointer(*slot, entry);if (xa_is_node(next) && (!node || node->shift))xas_free_nodes(xas, xa_to_node(next));if (!node)break;count += !next - !entry;values += !xa_is_value(first) - !value;if (entry) {if (offset == max)break;if (!xa_is_sibling(entry))entry = xa_mk_sibling(xas->xa_offset);} else {if (offset == XA_CHUNK_MASK)break;}next = xa_entry_locked(xas->xa, node, ++offset);if (!xa_is_sibling(next)) {if (!entry && (offset > max))break;first = next;}slot++;}update_node(xas, node, count, values);return first;}EXPORT_SYMBOL_GPL(xas_store);

xas_find_marked 在 XArray 数据结构中查找具有特定标记(mark)的下一个条目

/**
* xas_find_marked() - 在 XArray 中查找下一个标记的条目。
* @xas:XArray作状态。
* @max:要返回的最高索引。
* @mark:标记要搜索的号码。
*
* 如果@xas尚未遍历到条目,则返回索引为 >= xas.xa_index的标记条目。 如果已遍历,则当前指向的 entry 已被处理,因此我们返回索引> xas.xa_index的第一个标记条目。
*
* 如果未找到标记的条目并且数组小于 @max,则@xas设置为 bounds 状态,并将 xas->xa_index 设置为数组中尚未达到的最小索引。 这允许立即传递 @xas toxas_store()。
*
* 如果在到达 @max 之前未找到任何条目,则@xas设置为重新启动状态。
*
* 返回:条目(如果找到),否则为 %NULL。
*/
void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)
{
bool advance = true;
unsigned int offset;
void *entry;
if (xas_error(xas))
return NULL;
if (xas->xa_index > max)
goto max;
/* 如果当前节点为空或是顶层节点,函数初始化索引或处理顶层节点的条目。 */
if (!xas->xa_node) {
xas->xa_index = 1;
goto out;
} else if (xas_top(xas->xa_node)) {
/* 顶层节点包含标记 */
advance = false;
entry = xa_head(xas->xa);
xas->xa_node = NULL;
if (xas->xa_index > max_index(entry))
goto out;
if (!xa_is_node(entry)) {
if (xa_marked(xas->xa, mark))
return entry;
xas->xa_index = 1;
goto out;
}
xas->xa_node = xa_to_node(entry);
xas->xa_offset = xas->xa_index >> xas->xa_node->shift;
}
while (xas->xa_index <= max) {
if (unlikely(xas->xa_offset == XA_CHUNK_SIZE)) {
xas->xa_offset = xas->xa_node->offset + 1;
xas->xa_node = xa_parent(xas->xa, xas->xa_node);
if (!xas->xa_node)
break;
advance = false;
continue;
}
if (!advance) {
entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset);
if (xa_is_sibling(entry)) {
xas->xa_offset = xa_to_sibling(entry);
xas_move_index(xas, xas->xa_offset);
}
}
/* 查找当前节点中的标记条目,并更新偏移量和索引 */
offset = xas_find_chunk(xas, advance, mark);
if (offset > xas->xa_offset) {
advance = false;
xas_move_index(xas, offset);
/* Mind the wrap */
if ((xas->xa_index - 1) >= max)
goto max;
xas->xa_offset = offset;
if (offset == XA_CHUNK_SIZE)
continue;
}
entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset);
if (!entry && !(xa_track_free(xas->xa) && mark == XA_FREE_MARK))
continue;
if (xa_is_sibling(entry))
continue;
if (!xa_is_node(entry))
return entry;
xas->xa_node = xa_to_node(entry);
xas_set_offset(xas);
}
out:
if (xas->xa_index > max)
goto max;
return set_bounds(xas);
max:
xas->xa_node = XAS_RESTART;
return NULL;
}
EXPORT_SYMBOL_GPL(xas_find_marked);

xas_find_marked

/*** xas_find_marked() - Find the next marked entry in the XArray.* @xas: XArray operation state.* @max: Highest index to return.* @mark: Mark number to search for.** If the @xas has not yet been walked to an entry, return the marked entry* which has an index >= xas.xa_index.  If it has been walked, the entry* currently being pointed at has been processed, and so we return the* first marked entry with an index > xas.xa_index.** If no marked entry is found and the array is smaller than @max, @xas is* set to the bounds state and xas->xa_index is set to the smallest index* not yet in the array.  This allows @xas to be immediately passed to* xas_store().** If no entry is found before @max is reached, @xas is set to the restart* state.** Return: The entry, if found, otherwise %NULL.*/
void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)
{bool advance = true;unsigned int offset;void *entry;if (xas_error(xas))return NULL;if (xas->xa_index > max)goto max;if (!xas->xa_node) {xas->xa_index = 1;goto out;} else if (xas_top(xas->xa_node)) {advance = false;entry = xa_head(xas->xa);xas->xa_node = NULL;if (xas->xa_index > max_index(entry))goto out;if (!xa_is_node(entry)) {if (xa_marked(xas->xa, mark))return entry;xas->xa_index = 1;goto out;}xas->xa_node = xa_to_node(entry);xas->xa_offset = xas->xa_index >> xas->xa_node->shift;}while (xas->xa_index <= max) {if (unlikely(xas->xa_offset == XA_CHUNK_SIZE)) {xas->xa_offset = xas->xa_node->offset + 1;xas->xa_node = xa_parent(xas->xa, xas->xa_node);if (!xas->xa_node)break;advance = false;continue;}if (!advance) {entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset);if (xa_is_sibling(entry)) {xas->xa_offset = xa_to_sibling(entry);xas_move_index(xas, xas->xa_offset);}}offset = xas_find_chunk(xas, advance, mark);if (offset > xas->xa_offset) {advance = false;xas_move_index(xas, offset);/* Mind the wrap */if ((xas->xa_index - 1) >= max)goto max;xas->xa_offset = offset;if (offset == XA_CHUNK_SIZE)continue;}entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset);if (!entry && !(xa_track_free(xas->xa) && mark == XA_FREE_MARK))continue;if (xa_is_sibling(entry))continue;if (!xa_is_node(entry))return entry;xas->xa_node = xa_to_node(entry);xas_set_offset(xas);}
out:if (xas->xa_index > max)goto max;return set_bounds(xas);
max:xas->xa_node = XAS_RESTART;return NULL;
}
EXPORT_SYMBOL_GPL(xas_find_marked);

__xa_alloc 在 XArray 中分配一个新的条目

/**
* __xa_alloc() - 在XArray中找到存储此条目的位置。
* @xa: XArray。
* @id: ID指针。
* @limit: 分配ID的范围。
* @entry: 新条目。
* @gfp: 内存分配标志。
*
* 在@limit.min和@limit.max之间的@xa中找到一个空条目,
* 将索引存储到@id指针中,然后将条目存储在该索引处。
* 并发查找不会看到未初始化的@id。
*
* 仅可操作于使用标志XA_FLAGS_ALLOC通过xa_init_flags()初始化的xarray。
*
* 上下文:任何上下文。 进入时需要持有xa_lock。如果@gfp标志允许,
* 可能会释放并重新获取xa_lock。
* 返回值:成功返回0;如果无法分配内存,则返回-ENOMEM;
* 如果@limit中没有空闲条目,则返回-EBUSY。
*/
int __xa_alloc(struct xarray *xa, u32 *id, void *entry,
struct xa_limit limit, gfp_t gfp)
{
XA_STATE(xas, xa, 0);
/* 检查 entry 是否为高级条目 */
if (WARN_ON_ONCE(xa_is_advanced(entry)))
return -EINVAL;
/* 检查 XArray 是否启用了空闲跟踪 */
if (WARN_ON_ONCE(!xa_track_free(xa)))
return -EINVAL;
/* 将其设置为默认条目 XA_ZERO_ENTRY */
if (!entry)
entry = XA_ZERO_ENTRY;
do {
/* 从 limit.min 开始查找标记为空闲(XA_FREE_MARK)的索引 */
xas.xa_index = limit.min;
xas_find_marked(&xas, limit.max, XA_FREE_MARK);
if (xas.xa_node == XAS_RESTART)
xas_set_err(&xas, -EBUSY);
else
*id = xas.xa_index;
xas_store(&xas, entry);
xas_clear_mark(&xas, XA_FREE_MARK);
} while (__xas_nomem(&xas, gfp));/* 如果内存不足,调用 __xas_nomem 处理内存分配。 */
return xas_error(&xas);
}
EXPORT_SYMBOL(__xa_alloc);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/942172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年10月仓储管理系统推荐榜:鸿链云仓领衔对比评测排行

一、引言 对于日均订单过千的电商经营者、SKU数以万计的零售总部、以及需要跨境多仓协同的制造品牌而言,仓储管理系统早已不是“可选项”,而是决定履约成本、库存周转与客户体验的核心基础设施。2025年旺季备货周期缩…

一款优秀笔记软件的自我修养 - 实践

一款优秀笔记软件的自我修养 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco…

NITEX:构建时尚新供应链的数字平台与技术架构

本文深入解析NITEX时尚供应链数字平台的技术实现,涵盖品牌系统设计、前端架构选型、动画技术应用和模块化开发策略,展示了如何通过Nuxt、Sanity和GSAP构建高性能可扩展平台。NITEX:为时尚新供应链构建品牌与数字平台…

电子人速囤!正点原子万用表,电烙铁,电桥镊子等商品!

电子人速囤!正点原子万用表,电烙铁,电桥镊子等商品! 正点原子测量工具!DM40 万用表、T90 电烙铁、LT1 电桥镊子!从实验室到工作台,从新手到专家,这波装备升级福利不容错过!除了这三款爆款新品外,其他产品涵盖…

2025年10月超声波清洗机厂家榜单:十家主流厂商横向对比

进入2025年第四季度,精密制造、半导体、光学镜片、珠宝首饰等行业迎来年度设备升级窗口,超声波清洗机作为关键去微粒、去油膜设备,采购需求集中释放。调研显示,超过62%的产线工程师在选型时首先关注“厂家资质是否…

2025年10月超声波清洗机厂家评价榜:实力对比一览

正在产线赶工的王工发现,镜片清洗良率突然掉到92%,产线停一分钟就是真金白银;实验室里的李博士为硅片表面颗粒数超标头疼,传统喷淋已无法满足ppb级洁净要求;珠宝门店的张店长则想赶在旺季前把旧款翻新,却担心超声…

2025年10月炒股开户券商评测榜:广发证券领衔全维度对比

一、引言 对于计划在2025年四季度布局A股、港股及多元金融资产的投资者而言,选择一家合规稳健、渠道完备、服务成本可控的券商是首要动作。开户环节看似简单,却直接影响后续交易通道稳定性、资金效率、产品可得性以及…

2025年10月超声波清洗机厂家评测榜:十强对比与权威数据解读

进入2025年第四季度,精密制造、半导体、光学镜片、珠宝首饰等行业对高洁净度需求持续放大,带动超声波清洗设备询盘量环比上升约18%。面对“交期紧、工艺差异大、环保审查严”三重压力,采购经理、工艺工程师与实验室…

2025 年桥梁护栏厂家最新推荐排行榜:聚焦安全防护与耐用性能的实力企业甄选指南

引言 当前交通建设持续推进,桥梁数量不断增多,桥梁护栏作为保障桥梁安全的核心设施,市场需求日益旺盛。但市场上桥梁护栏品牌繁杂,部分产品安全防护性能不足,难以抵御意外冲击,增加事故风险;部分产品耐用性差,…

在Java中,如何实现封装

在 Java 中实现封装主要通过 访问修饰符 和 getter/setter 方法 来实现,核心步骤是"隐藏属性、暴露接口"。具体实现方式如下: 一、使用访问修饰符隐藏类的成员 Java 提供了 4 种访问修饰符,用于控制类成员…

2025年10月超声波清洗机厂家排行:十家主流企业深度评测

“生产线节拍越来越快,人工擦拭已经跟不上良率要求;环评收紧,委外清洗费用又节节攀升。”——这是最近三个月里,我在东莞、苏州、成都三地调研时听到最多的两句话。2025年第三季度,工信部《精密制造清洗环节调研简…

2025年10月美白精华产品排行:从成分到肤感全维度评测

入秋之后,紫外线强度虽有所下降,但“晒后反黑”“糖化暗黄”成为社交平台高频提问。国家药监局2024年化妆品注册备案年报显示,美白淡斑类精华备案量同比增23%,其中“温和不刺激”“28天见效”成为检索热词。小红书…

Koodo Reader快捷键大全:提升阅读效率的键盘执行技巧

Koodo Reader快捷键大全:提升阅读效率的键盘执行技巧pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

2025年10月美白精华产品推荐榜:十款热门单品深度对比

入秋以后,紫外线强度虽降,但夏季累积的暗沉、晒斑开始浮现,不少用户把“提亮肤色、淡化黄气”写进十月护肤清单。小红书后台数据显示,九月底“美白精华”搜索量环比上升42%,其中“敏感肌可用”“抗糖减黄”“不反…

详细介绍:rabbitMQ续谈

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年10月超声波清洗机厂家推荐榜:十强对比评测

把“超声波清洗机厂家”六个字敲进搜索框的那一刻,你大概率正被以下场景包围:产线升级急需替代人工的自动化清洗方案,实验室要满足微米级洁净度,或是珠宝车间想在不伤表面的前提下提升出货速度。无论哪种场景,核心…

2025年10月不锈钢水箱厂家推荐榜:十强对比评测

正在打开自来水龙头的小区物业经理、筹备酒店热水系统的工程负责人、或是为新建厂房规划消防蓄水的业主,都会面临同一个问题:不锈钢水箱厂家这么多,到底选谁才放心?2025年,住建部《二次供水工程技术规程》更新后,…

2025年10月长白山旅游度假酒店推荐:口碑榜与实景对比排行

国庆长假刚过,长白山已进入“金秋+初雪”交替的摄影黄金期,也是错峰度假性价比最高的窗口。很多用户把“住得舒服”列为长白山行程的第一优先级:白天徒步、滑雪、泡温泉,晚上想回到房间就能看见林海或星空,最好下…

2025 年最新推荐北京 / 陕西百度官网认证代理商榜单:全方位评估服务实力助企业选靠谱机构

引言 在互联网深度融入企业发展的当下,官网作为品牌展示与业务拓展的核心窗口,其真实性与可信度直接影响消费者决策,而百度官网认证正是提升官网公信力的关键举措。然而当前市场上百度官网认证服务商数量繁杂,部分…

2025年10月不锈钢水箱厂家评价榜:实力参数横向对比

把“不锈钢水箱”这五个字敲进搜索框的人,往往正处在三种场景:新建小区泵房要验收、老酒店水箱锈穿急换、工厂扩建要增容。他们共同的关键词是“耐用”“合规”“别漏水”,共同的焦虑是“厂家太多,谁真靠谱”。202…