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 自測套件中。