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)    收藏  举报

导航