BPF cpumask kfuncs¶
1. 简介¶
struct cpumask
是内核中的位图数据结构,其索引反映系统上的 CPU。通常,cpumask 用于跟踪任务被关联到的 CPU,但它们也可以用于例如跟踪与调度域关联的内核,机器上的空闲内核等。
BPF 为程序提供了一组 BPF 内核函数 (kfuncs),这些函数可用于分配、修改、查询和释放 cpumask。
2. BPF cpumask 对象¶
BPF 程序可以使用两种不同类型的 cpumask。
2.1 struct bpf_cpumask *
¶
struct bpf_cpumask *
是由 BPF 代表 BPF 程序分配的 cpumask,其生命周期完全由 BPF 控制。这些 cpumask 受 RCU 保护,可以被修改,可以用作 kptr,并且可以安全地强制转换为 struct cpumask *
。
2.1.1 struct bpf_cpumask *
生命周期¶
可以使用以下函数分配、获取和释放 struct bpf_cpumask *
-
__bpf_kfunc struct bpf_cpumask *bpf_cpumask_create(void)¶
创建一个可变的 BPF cpumask。
参数
void
无参数
说明
分配一个可以被 BPF 程序查询、修改、获取和释放的 cpumask。此函数返回的 cpumask 必须作为 kptr 嵌入到 map 中,或者使用 bpf_cpumask_release()
释放。
bpf_cpumask_create()
使用 BPF 内存分配器分配内存,不会阻塞。如果没有可用内存,它可能会返回 NULL。
返回
成功时,指向新的 struct bpf_cpumask 实例的指针。
如果 BPF 内存分配器内存不足,则返回 NULL。
-
__bpf_kfunc struct bpf_cpumask *bpf_cpumask_acquire(struct bpf_cpumask *cpumask)¶
获取对 BPF cpumask 的引用。
参数
struct bpf_cpumask *cpumask
正在获取的 BPF cpumask。cpumask 必须是受信任的指针。
说明
获取对 BPF cpumask 的引用。此函数返回的 cpumask 必须作为 kptr 嵌入到 map 中,或者使用 bpf_cpumask_release()
释放。
返回
传递给函数的 struct bpf_cpumask 指针。
-
__bpf_kfunc void bpf_cpumask_release(struct bpf_cpumask *cpumask)¶
释放先前获取的 BPF cpumask。
参数
struct bpf_cpumask *cpumask
正在释放的 cpumask。
说明
释放先前获取的对 BPF cpumask 的引用。当 BPF cpumask 的最后一个引用被释放时,它随后会在 BPF 内存分配器中的 RCU 回调中被释放。
例如
struct cpumask_map_value {
struct bpf_cpumask __kptr * cpumask;
};
struct array_map {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, struct cpumask_map_value);
__uint(max_entries, 65536);
} cpumask_map SEC(".maps");
static int cpumask_map_insert(struct bpf_cpumask *mask, u32 pid)
{
struct cpumask_map_value local, *v;
long status;
struct bpf_cpumask *old;
u32 key = pid;
local.cpumask = NULL;
status = bpf_map_update_elem(&cpumask_map, &key, &local, 0);
if (status) {
bpf_cpumask_release(mask);
return status;
}
v = bpf_map_lookup_elem(&cpumask_map, &key);
if (!v) {
bpf_cpumask_release(mask);
return -ENOENT;
}
old = bpf_kptr_xchg(&v->cpumask, mask);
if (old)
bpf_cpumask_release(old);
return 0;
}
/**
* A sample tracepoint showing how a task's cpumask can be queried and
* recorded as a kptr.
*/
SEC("tp_btf/task_newtask")
int BPF_PROG(record_task_cpumask, struct task_struct *task, u64 clone_flags)
{
struct bpf_cpumask *cpumask;
int ret;
cpumask = bpf_cpumask_create();
if (!cpumask)
return -ENOMEM;
if (!bpf_cpumask_full(task->cpus_ptr))
bpf_printk("task %s has CPU affinity", task->comm);
bpf_cpumask_copy(cpumask, task->cpus_ptr);
return cpumask_map_insert(cpumask, task->pid);
}
2.1.1 struct bpf_cpumask *
作为 kptr¶
如上所述和说明,这些 struct bpf_cpumask *
对象也可以存储在 map 中并用作 kptr。如果 struct bpf_cpumask *
在 map 中,则可以使用 bpf_kptr_xchg() 从 map 中删除引用,或者使用 RCU 机会性地获取。
/* struct containing the struct bpf_cpumask kptr which is stored in the map. */
struct cpumasks_kfunc_map_value {
struct bpf_cpumask __kptr * bpf_cpumask;
};
/* The map containing struct cpumasks_kfunc_map_value entries. */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, int);
__type(value, struct cpumasks_kfunc_map_value);
__uint(max_entries, 1);
} cpumasks_kfunc_map SEC(".maps");
/* ... */
/**
* A simple example tracepoint program showing how a
* struct bpf_cpumask * kptr that is stored in a map can
* be passed to kfuncs using RCU protection.
*/
SEC("tp_btf/cgroup_mkdir")
int BPF_PROG(cgrp_ancestor_example, struct cgroup *cgrp, const char *path)
{
struct bpf_cpumask *kptr;
struct cpumasks_kfunc_map_value *v;
u32 key = 0;
/* Assume a bpf_cpumask * kptr was previously stored in the map. */
v = bpf_map_lookup_elem(&cpumasks_kfunc_map, &key);
if (!v)
return -ENOENT;
bpf_rcu_read_lock();
/* Acquire a reference to the bpf_cpumask * kptr that's already stored in the map. */
kptr = v->cpumask;
if (!kptr) {
/* If no bpf_cpumask was present in the map, it's because
* we're racing with another CPU that removed it with
* bpf_kptr_xchg() between the bpf_map_lookup_elem()
* above, and our load of the pointer from the map.
*/
bpf_rcu_read_unlock();
return -EBUSY;
}
bpf_cpumask_setall(kptr);
bpf_rcu_read_unlock();
return 0;
}
2.2 struct cpumask
¶
struct cpumask
是实际包含正在查询、修改等的 cpumask 位图的对象。struct bpf_cpumask
包装了 struct cpumask
,这就是为什么将其强制转换为这种类型是安全的(但请注意,将 struct cpumask *
强制转换为 struct bpf_cpumask *
是 不 安全的,并且验证器会拒绝任何试图这样做的程序)。
正如我们将在下面看到的,任何修改其 cpumask 参数的 kfunc 都将使用 struct bpf_cpumask *
作为该参数。任何只查询 cpumask 的参数将改为使用 struct cpumask *
。
3. cpumask kfuncs¶
上面,我们描述了可用于分配、获取、释放等 struct bpf_cpumask *
的 kfunc。本文档的本节将描述用于修改和查询 cpumask 的 kfunc。
3.1 修改 cpumask¶
一些 cpumask kfunc 是“只读的”,因为它们不修改任何参数,而另一些 kfunc 则修改至少一个参数(这意味着该参数必须是 struct bpf_cpumask *
,如上所述)。
本节将描述所有修改至少一个参数的 cpumask kfunc。下面的 3.2 查询 cpumask 描述了只读 kfunc。
3.1.1 设置和清除 CPU¶
bpf_cpumask_set_cpu()
和 bpf_cpumask_clear_cpu()
可用于分别在 struct bpf_cpumask
中设置和清除 CPU
-
__bpf_kfunc void bpf_cpumask_set_cpu(u32 cpu, struct bpf_cpumask *cpumask)¶
在 BPF cpumask 中设置 CPU 的位。
参数
u32 cpu
要在 cpumask 中设置的 CPU。
struct bpf_cpumask *cpumask
正在设置位的 BPF cpumask。
-
__bpf_kfunc void bpf_cpumask_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask)¶
在 BPF cpumask 中清除 CPU 的位。
参数
u32 cpu
要从 cpumask 中清除的 CPU。
struct bpf_cpumask *cpumask
正在清除位的 BPF cpumask。
这些 kfunc 非常简单,可以例如按如下方式使用
/**
* A sample tracepoint showing how a cpumask can be queried.
*/
SEC("tp_btf/task_newtask")
int BPF_PROG(test_set_clear_cpu, struct task_struct *task, u64 clone_flags)
{
struct bpf_cpumask *cpumask;
cpumask = bpf_cpumask_create();
if (!cpumask)
return -ENOMEM;
bpf_cpumask_set_cpu(0, cpumask);
if (!bpf_cpumask_test_cpu(0, cast(cpumask)))
/* Should never happen. */
goto release_exit;
bpf_cpumask_clear_cpu(0, cpumask);
if (bpf_cpumask_test_cpu(0, cast(cpumask)))
/* Should never happen. */
goto release_exit;
/* struct cpumask * pointers such as task->cpus_ptr can also be queried. */
if (bpf_cpumask_test_cpu(0, task->cpus_ptr))
bpf_printk("task %s can use CPU %d", task->comm, 0);
release_exit:
bpf_cpumask_release(cpumask);
return 0;
}
bpf_cpumask_test_and_set_cpu()
和 bpf_cpumask_test_and_clear_cpu()
是互补的 kfunc,允许调用者原子地测试和设置(或清除)CPU
-
__bpf_kfunc bool bpf_cpumask_test_and_set_cpu(u32 cpu, struct bpf_cpumask *cpumask)¶
原子地测试和设置 BPF cpumask 中的 CPU。
参数
u32 cpu
正在设置和查询的 CPU。
struct bpf_cpumask *cpumask
正在设置和查询的包含 CPU 的 BPF cpumask。
返回
true - cpu 在 cpumask 中设置
false - cpu 未在 cpumask 中设置,或者 cpu 无效。
-
__bpf_kfunc bool bpf_cpumask_test_and_clear_cpu(u32 cpu, struct bpf_cpumask *cpumask)¶
原子地测试和清除 BPF cpumask 中的 CPU。
参数
u32 cpu
正在清除和查询的 CPU。
struct bpf_cpumask *cpumask
正在清除和查询的包含 CPU 的 BPF cpumask。
返回
true - cpu 在 cpumask 中设置
false - cpu 未在 cpumask 中设置,或者 cpu 无效。
我们还可以使用 bpf_cpumask_setall()
和 bpf_cpumask_clear()
在一个操作中设置和清除整个 struct bpf_cpumask *
对象
-
__bpf_kfunc void bpf_cpumask_setall(struct bpf_cpumask *cpumask)¶
设置 BPF cpumask 中的所有位。
参数
struct bpf_cpumask *cpumask
所有位都已设置的 BPF cpumask。
-
__bpf_kfunc void bpf_cpumask_clear(struct bpf_cpumask *cpumask)¶
清除 BPF cpumask 中的所有位。
参数
struct bpf_cpumask *cpumask
正在清除的 BPF cpumask。
3.1.2 cpumask 之间的操作¶
除了设置和清除单个 cpumask 中的各个 CPU 外,调用者还可以使用 bpf_cpumask_and()
、bpf_cpumask_or()
和 bpf_cpumask_xor()
在多个 cpumask 之间执行按位运算
-
__bpf_kfunc bool bpf_cpumask_and(struct bpf_cpumask *dst, const struct cpumask *src1, const struct cpumask *src2)¶
AND 两个 cpumask 并存储结果。
参数
struct bpf_cpumask *dst
正在存储结果的 BPF cpumask。
const struct cpumask *src1
第一个输入。
const struct cpumask *src2
第二个输入。
返回
true - dst 在操作后至少设置了一位
false - dst 在操作后为空
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
-
__bpf_kfunc void bpf_cpumask_or(struct bpf_cpumask *dst, const struct cpumask *src1, const struct cpumask *src2)¶
OR 两个 cpumask 并存储结果。
参数
struct bpf_cpumask *dst
正在存储结果的 BPF cpumask。
const struct cpumask *src1
第一个输入。
const struct cpumask *src2
第二个输入。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
-
__bpf_kfunc void bpf_cpumask_xor(struct bpf_cpumask *dst, const struct cpumask *src1, const struct cpumask *src2)¶
XOR 两个 cpumask 并存储结果。
参数
struct bpf_cpumask *dst
正在存储结果的 BPF cpumask。
const struct cpumask *src1
第一个输入。
const struct cpumask *src2
第二个输入。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
以下是如何使用它们的示例。请注意,本示例中显示的一些 kfunc 将在下面更详细地介绍。
/**
* A sample tracepoint showing how a cpumask can be mutated using
bitwise operators (and queried).
*/
SEC("tp_btf/task_newtask")
int BPF_PROG(test_and_or_xor, struct task_struct *task, u64 clone_flags)
{
struct bpf_cpumask *mask1, *mask2, *dst1, *dst2;
mask1 = bpf_cpumask_create();
if (!mask1)
return -ENOMEM;
mask2 = bpf_cpumask_create();
if (!mask2) {
bpf_cpumask_release(mask1);
return -ENOMEM;
}
// ...Safely create the other two masks... */
bpf_cpumask_set_cpu(0, mask1);
bpf_cpumask_set_cpu(1, mask2);
bpf_cpumask_and(dst1, (const struct cpumask *)mask1, (const struct cpumask *)mask2);
if (!bpf_cpumask_empty((const struct cpumask *)dst1))
/* Should never happen. */
goto release_exit;
bpf_cpumask_or(dst1, (const struct cpumask *)mask1, (const struct cpumask *)mask2);
if (!bpf_cpumask_test_cpu(0, (const struct cpumask *)dst1))
/* Should never happen. */
goto release_exit;
if (!bpf_cpumask_test_cpu(1, (const struct cpumask *)dst1))
/* Should never happen. */
goto release_exit;
bpf_cpumask_xor(dst2, (const struct cpumask *)mask1, (const struct cpumask *)mask2);
if (!bpf_cpumask_equal((const struct cpumask *)dst1,
(const struct cpumask *)dst2))
/* Should never happen. */
goto release_exit;
release_exit:
bpf_cpumask_release(mask1);
bpf_cpumask_release(mask2);
bpf_cpumask_release(dst1);
bpf_cpumask_release(dst2);
return 0;
}
可以使用 bpf_cpumask_copy()
将整个 cpumask 的内容复制到另一个 cpumask
-
__bpf_kfunc void bpf_cpumask_copy(struct bpf_cpumask *dst, const struct cpumask *src)¶
将 cpumask 的内容复制到 BPF cpumask 中。
参数
struct bpf_cpumask *dst
正在复制到的 BPF cpumask。
const struct cpumask *src
正在复制的 cpumask。
说明
struct bpf_cpumask 指针可以安全地传递给 src。
3.2 查询 cpumask¶
除了上述 kfunc 之外,还有一组只读 kfunc 可用于查询 cpumask 的内容。
参数
const struct cpumask *cpumask
正在查询的 cpumask。
说明
查找 cpumask 的第一个非零位的索引。struct bpf_cpumask 指针可以安全地传递给此函数。
返回
struct cpumask 中第一个非零位的索引。
参数
const struct cpumask *cpumask
正在查询的 cpumask。
说明
查找 cpumask 的第一个未设置位的索引。struct bpf_cpumask 指针可以安全地传递给此函数。
返回
struct cpumask 中第一个零位的索引。
-
__bpf_kfunc u32 bpf_cpumask_first_and(const struct cpumask *src1, const struct cpumask *src2)¶
从两个 cpumask 的 AND 运算中返回第一个非零位的索引。
参数
const struct cpumask *src1
第一个 cpumask。
const struct cpumask *src2
第二个 cpumask。
说明
查找两个 cpumask 的 AND 运算的第一个非零位的索引。struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
返回
在两个 cpumask 实例中均为非零的第一个位的索引。
-
__bpf_kfunc bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask)¶
测试 CPU 是否在 cpumask 中设置。
参数
u32 cpu
正在查询的 CPU。
const struct cpumask *cpumask
正在查询的包含 CPU 的 cpumask。
返回
true - cpu 在 cpumask 中设置
false - cpu 未在 cpumask 中设置,或者 cpu 是无效 CPU。
参数
const struct cpumask *cpumask
正在查询的 cpumask。
说明
计算给定 cpumask 中设置的位数。
返回
在掩码中设置的位数。
-
__bpf_kfunc bool bpf_cpumask_equal(const struct cpumask *src1, const struct cpumask *src2)¶
检查两个 cpumask 是否相等。
参数
const struct cpumask *src1
第一个输入。
const struct cpumask *src2
第二个输入。
返回
true - src1 和 src2 具有相同的已设置位。
false - src1 和 src2 在至少一位上不同。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
-
__bpf_kfunc bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2)¶
检查两个 cpumask 是否重叠。
参数
const struct cpumask *src1
第一个输入。
const struct cpumask *src2
第二个输入。
返回
true - src1 和 src2 至少有一个相同的已设置位。
false - src1 和 src2 没有相同的已设置位。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
-
__bpf_kfunc bool bpf_cpumask_subset(const struct cpumask *src1, const struct cpumask *src2)¶
检查 cpumask 是否是另一个 cpumask 的子集。
参数
const struct cpumask *src1
正在作为子集检查的第一个 cpumask。
const struct cpumask *src2
正在作为超集检查的第二个 cpumask。
返回
true - src1 的所有位都在 src2 中设置。
false - src1 中至少有一位未在 src2 中设置。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
参数
const struct cpumask *cpumask
正在检查的 cpumask。
返回
true - cpumask 中没有设置任何位。
false - cpumask 中至少设置了一位。
说明
struct bpf_cpumask 指针可以安全地传递给 cpumask。
参数
const struct cpumask *cpumask
正在检查的 cpumask。
返回
true - cpumask 中的所有位均已设置。
false - cpumask 中至少有一位被清除。
说明
struct bpf_cpumask 指针可以安全地传递给 cpumask。
参数
const struct cpumask *cpumask
正在查询的 cpumask。
返回
如果至少设置了一位,则返回 [0, num_cpus) 中的随机设置位。
如果没有设置位,则返回 >= num_cpus。
说明
struct bpf_cpumask 指针可以安全地传递给 src。
-
__bpf_kfunc u32 bpf_cpumask_any_and_distribute(const struct cpumask *src1, const struct cpumask *src2)¶
从两个 cpumask 的 AND 运算中返回一个随机设置的 CPU。
参数
const struct cpumask *src1
第一个 cpumask。
const struct cpumask *src2
第二个 cpumask。
返回
如果至少设置了一位,则从两个 cpumask 的 AND 运算中返回 [0, num_cpus) 中的随机设置位。
如果没有设置位,则返回 >= num_cpus。
说明
struct bpf_cpumask 指针可以安全地传递给 src1 和 src2。
上面已经展示了这些查询 kfuncs 的一些示例用法。 我们将不再在这里重复这些示例。 但是请注意,上述所有 kfuncs 都在tools/testing/selftests/bpf/progs/cpumask_success.c中进行了测试,因此如果您正在寻找有关如何使用它们的更多示例,请查看那里。
4. 添加 BPF cpumask kfuncs¶
支持的 BPF cpumask kfuncs 集合与 include/linux/cpumask.h 中的 cpumask 操作并非(尚未)完全匹配。 如果需要,这些 cpumask 操作中的任何一个都可以很容易地封装在一个新的 kfunc 中。 如果您想支持新的 cpumask 操作,请随时提交补丁。 如果您确实添加了新的 cpumask kfunc,请在此处记录它,并将任何相关的自测测试用例添加到 cpumask 自测套件中。