xarray-2-实现与实验
基于msm-5.4
一、简介
1. 实现代码位置
//实现代码位置: ./lib/xarray.c ./include/linux/xarray.h //内核自带测试代码: ./lib/test_xarray.c ./tools/testing/radix-tree/xarray.c ./tools/testing/radix-tree/linux/xarray.h //内核自带介绍文档: ./Documentation/core-api/xarray.rst
二、数据结构
1. struct xarray
struct xarray { spinlock_t xa_lock; gfp_t xa_flags; void __rcu * xa_head; };
此结构描述一个 xarray。成员介绍:
(1) xa_lock:
用于保护 XArray 内容的锁。用于写路径互斥,比如 store、erase、cmpxchg、mark 更新、节点分裂/回收。
(2) xa_flags:
用于行为模式与锁变体配置,它决定这个 xarray 的运行模式。
a. 分配相关:
XA_FLAGS_ALLOC: 启用“按空闲索引分配 ID”的语义, 默认 index 从0开始分配。
XA_FLAGS_ALLOC1: 从1开始分配(而不是 0)。
b. 锁上下文相关
XA_FLAGS_LOCK_BH:锁操作要处理软中断语义
XA_FLAGS_LOCK_IRQ:锁操作要处理硬中断语义
c. 标记跟踪相关
某些模式会占用 mark0 做 free tracking(分配型 xarray 常见),这会影响你能否把 mark0 当业务标记用。
(3) xa_head:
用于指向树根,如果数组中的所有元素都为 NULL,则 @xa_head 指向空指针。如果数组中唯一非 NULL 的元素位于索引 0,则 @xa_head 指向该元素。如果数组中还有其他非 NULL 的元素,则 @xa_head 指向一个 @xa_node。
2. struct xa_node
struct xa_node { //xarray.h unsigned char shift; /* Bits remaining in each slot */ unsigned char offset; /* Slot offset in parent */ unsigned char count; /* Total entry count */ unsigned char nr_values; /* Value entry count */ struct xa_node __rcu *parent; /* NULL at top of tree */ struct xarray *array; /* The array we belong to */ union { struct list_head private_list; /* For tree user */ struct rcu_head rcu_head; /* Used when freeing node */ }; void __rcu *slots[XA_CHUNK_SIZE]; //64 union { unsigned long tags[XA_MAX_MARKS][XA_MARK_LONGS]; //[3][1] unsigned long marks[XA_MAX_MARKS][XA_MARK_LONGS]; //[3][1] }; };
此结构描述 XArray 树的一个中间节点对象。如果把 XArray 想成一棵 64 叉树,那么每个 xa_node 就是树上的一个分叉点,负责把一段索引空间再切成 64 份。
整体定位: 每个节点有 64 个槽位(slots)。每个槽位要么指向下一层节点,要么直接存一个条目(普通指针、value entry、内部条目等)。读路径依赖 RCU,无锁读取 slots。写路径在持 xa_lock 条件下修改节点内容和元数据。
成员介绍:
(1) shift
含义是“本层每个槽位下面还覆盖多少索引位范围”。查找时核心公式是:slot = (index >> shift) & 63。shift 越大,说明层级越高、每个槽覆盖的索引范围越大。
(2) offset
表示“当前节点在父节点的第几个槽里”。取值 0 到 63。它让回溯更新父节点时不需要再重新计算位置。根节点没有父节点,这个值对根通常无实际意义。
(3) count
该节点当前有效槽位计数。用于快速判断节点是否空、是否需要回收。典型不变量:0 <= count <= 64。
(4) nr_values
该节点中 value 类型条目的计数。它是一个优化统计量,帮助快速判断这一层是否包含 value entry。典型不变量:nr_values <= count。
(5) parent(RCU指针)
指向父节点,根节点为 NULL。标注为 RCU 指针是为了支持并发读者在 RCU 读侧安全遍历。
(6) array
指向所属的 xarray。很多操作在节点层面仍需要访问数组级配置、锁和标志。
(7) private_list/rcu_head(联合体)
这是“复用内存”的设计。节点活跃时可以把这块内存作为 private_list 给上层使用。节点准备释放时改作 rcu_head,用于 call_rcu 延迟回收。
(8) slots[64](RCU指针数组)
这是节点最核心的数据区。每个槽位对应一段索引子空间。读者通过 RCU 读取它,写者在锁保护下更新它。
(9) tags/marks(联合体)
本质是每个标记一张位图。你给的尺寸是 [3][1],表示有 3 类 mark,每类 64 位,刚好覆盖 64 个槽。某一位为 1,表示对应槽位下存在该 mark,用于快速剪枝扫描(例如找下一个被标记条目)。
三、实现
TODO
四、实验
1. xarray存取实验
#define pr_fmt(fmt) "xarray_demo: " fmt #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/fs.h> #include <linux/xarray.h> int g_val; struct demo_item { int id; char name[16]; }; static DEFINE_XARRAY(demo_xa); static void demo_dump_array(void) { struct demo_item *item; unsigned long index; pr_info("dump begin:\n"); /* entry可以是值夜可以不是值 */ xa_for_each(&demo_xa, index, item) { if (xa_is_value(item)) { pr_info("index=%lu value=%lu\n", index, xa_to_value(item)); continue; } pr_info("index=%lu item{id=%d name=%s}\n", index, item->id, item->name); } pr_info("dump end\n"); } static void demo_free_entries(void) { struct demo_item *item; unsigned long index; /* 若此entry不是值,还要kfree(主要是store时用kzalloc分配的内存) */ xa_for_each(&demo_xa, index, item) { void *entry = xa_erase(&demo_xa, index); if (!entry || xa_is_value(entry)) continue; kfree(entry); } } static int demo_store_item(unsigned long index, int id, const char *name) { struct demo_item *item; void *old; int ret = 0; item = kzalloc(sizeof(*item), GFP_KERNEL); if (!item) return -ENOMEM; item->id = id; strscpy(item->name, name, sizeof(item->name)); old = xa_store(&demo_xa, index, item, GFP_KERNEL); if (xa_is_err(old)) { ret = xa_err(old); kfree(item); return ret; } if (old) kfree(old); return 0; } static int xarray_demo_test_init(void) { struct demo_item *item; void *entry; unsigned long index; int ret; pr_info("test init\n"); ret = demo_store_item(1, 101, "alpha"); if (ret) goto err; ret = demo_store_item(8, 108, "beta"); if (ret) goto err; ret = demo_store_item(42, 142, "gamma"); if (ret) goto err; entry = xa_store(&demo_xa, 100, xa_mk_value(1234), GFP_KERNEL); if (xa_is_err(entry)) { ret = xa_err(entry); goto err; } ret = xa_insert(&demo_xa, 7, xa_mk_value(77), GFP_KERNEL); if (ret) goto err; item = xa_load(&demo_xa, 42); if (item) pr_info("xa_load(42) -> id=%d name=%s\n", item->id, item->name); xa_set_mark(&demo_xa, 42, XA_MARK_0); xa_set_mark(&demo_xa, 100, XA_MARK_0); pr_info("marked entries:\n"); xa_for_each_marked(&demo_xa, index, entry, XA_MARK_0) { if (xa_is_value(entry)) { pr_info("marked index=%lu value=%lu\n", index, xa_to_value(entry)); continue; } item = entry; pr_info("marked index=%lu item=%s\n", index, item->name); } demo_dump_array(); entry = xa_erase(&demo_xa, 7); if (xa_is_value(entry)) pr_info("erased index 7, old value=%lu\n", xa_to_value(entry)); return 0; err: pr_err("init failed: %d\n", ret); demo_free_entries(); xa_destroy(&demo_xa); return ret; } static ssize_t xarray_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char buffer[64] = {0}; int ret, type, val; if (count > sizeof(buffer) - 1) count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; ret = sscanf(buffer, "%d %d", &type, &val); if (ret <= 0) { pr_err("sscanf failed\n"); return -EINVAL;; } pr_info("set param: type=%d, val=%d\n", type, val); if (type == 1) { xarray_demo_test_init(); } g_val = val; return count; } static ssize_t xarray_demo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { char buffer[256]; size_t len; len = snprintf(buffer, sizeof(buffer), "test val=%d\n", g_val); return simple_read_from_buffer(buf, count, ppos, buffer, strlen(buffer)); } static const struct file_operations proc_xarray_demo_fops = { .write = xarray_demo_write, .read = xarray_demo_read, }; static int __init xarray_demo_init(void) { pr_info("init.\n"); proc_create("xarray_test", 0666, NULL, &proc_xarray_demo_fops); return 0; } static void __exit xarray_demo_exit(void) { pr_info("exit\n"); demo_free_entries(); xa_destroy(&demo_xa); } module_init(xarray_demo_init); module_exit(xarray_demo_exit);
实验打印:
# echo 1 0 > /proc/xarray_test # dmesg -c | grep xarray_demo [ 63.623676] (5)[9042:sh]xarray_demo: set param: type=1, val=0 [ 63.623896] (5)[9042:sh]xarray_demo: test init [ 63.624052] (5)[9042:sh]xarray_demo: xa_load(42) -> id=142 name=gamma [ 63.624206] (5)[9042:sh]xarray_demo: marked entries: [ 63.624357] (5)[9042:sh]xarray_demo: marked index=42 item=gamma [ 63.624537] (5)[9042:sh]xarray_demo: marked index=100 value=1234 [ 63.624709] (5)[9042:sh]xarray_demo: dump begin: [ 63.624838] (5)[9042:sh]xarray_demo: index=1 item{id=101 name=alpha} [ 63.625045] (5)[9042:sh]xarray_demo: index=7 value=77 [ 63.625180] (5)[9042:sh]xarray_demo: index=8 item{id=108 name=beta} [ 63.625345] (5)[9042:sh]xarray_demo: index=42 item{id=142 name=gamma} [ 63.625485] (5)[9042:sh]xarray_demo: index=100 value=1234 [ 63.625620] (5)[9042:sh]xarray_demo: dump end [ 63.625686] (5)[9042:sh]xarray_demo: erased index 7, old value=77 #
posted on 2026-04-07 15:29 Hello-World3 阅读(1) 评论(0) 收藏 举报
浙公网安备 33010602011771号