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。
-
__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()
释放。
-
__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 作为 kptr 的 struct bpf_cpumask *
¶
如上所述并示出,这些 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 cpumask *
转换为 struct bpf_cpumask *
是**不**安全的,并且验证器将拒绝任何尝试这样做的程序)。
正如我们将在下面看到的,任何修改其 cpumask 参数的 kfunc 都将使用 struct bpf_cpumask *
作为该参数。任何只查询 cpumask 的参数都将使用 struct cpumask *
。
3. cpumask kfuncs¶
上面,我们描述了可用于分配、获取、释放等的 kfuncs struct bpf_cpumask *
。本文档的这一部分将描述用于修改和查询 cpumask 的 kfuncs。
3.1 修改 cpumask¶
一些 cpumask kfuncs 是“只读”的,因为它们不修改其任何参数,而另一些则修改至少一个参数(这意味着该参数必须是 struct bpf_cpumask *
,如上所述)。
本节将描述修改至少一个参数的所有 cpumask kfuncs。下面的 3.2 查询 cpumask 描述了只读 kfuncs。
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。
这些 kfuncs 非常简单,可以按如下方式使用,例如
/**
* 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()
是互补的 kfuncs,允许调用者以原子方式测试和设置(或清除)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
正在设置和查询的 BPF cpumask,其中包含一个 CPU。
返回值
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
正在清除和查询的 BPF cpumask,其中包含一个 CPU。
返回值
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)¶
对两个 cpumask 执行 AND 操作并存储结果。
参数
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)¶
对两个 cpumask 执行 OR 操作并存储结果。
参数
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)¶
对两个 cpumask 执行 XOR 操作并存储结果。
参数
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 的内容复制到另一个。
-
__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 指针可以安全地传递给此函数。
参数
const struct cpumask *cpumask
正在查询的 cpumask。
描述
查找 cpumask 的第一个未设置位的索引。 struct bpf_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。
-
__bpf_kfunc bool bpf_cpumask_test_cpu(u32 cpu, const struct cpumask *cpumask)¶
测试是否在 cpumask 中设置了 CPU。
参数
u32 cpu
正在查询的 CPU。
const struct cpumask *cpumask
正在查询的 cpumask,以确定是否包含 CPU。
返回值
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 是否是另一个的子集。
参数
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。
上面展示了这些查询 kfunc 的一些示例用法。我们不会在这里重复这些示例。但请注意,所有上述 kfunc 都在 tools/testing/selftests/bpf/progs/cpumask_success.c 中进行了测试,因此如果您想了解更多关于如何使用它们的示例,请查看那里。
4. 添加 BPF cpumask kfunc¶
目前支持的 BPF cpumask kfunc 集合与 include/linux/cpumask.h 中的 cpumask 操作不是(还不是)一一对应的。如果需要,可以将任何这些 cpumask 操作轻松地封装在一个新的 kfunc 中。如果您想支持新的 cpumask 操作,请随时提交补丁。如果您添加了新的 cpumask kfunc,请在此处记录它,并将任何相关的自测试用例添加到 cpumask 自测试套件中。