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 指標可以安全地傳遞給 src1src2

__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 指標可以安全地傳遞給 src1src2

__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 指標可以安全地傳遞給 src1src2

以下是如何使用它們的示例。請注意,本示例中顯示的一些 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 的內容。

__bpf_kfunc u32 bpf_cpumask_first(const struct cpumask *cpumask)

獲取 cpumask 中第一個非零位的索引。

引數

const struct cpumask *cpumask

正在查詢的 cpumask。

說明

查詢 cpumask 的第一個非零位的索引。struct bpf_cpumask 指標可以安全地傳遞給此函式。

返回

  • struct cpumask 中第一個非零位的索引。

__bpf_kfunc u32 bpf_cpumask_first_zero(const struct cpumask *cpumask)

獲取 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 指標可以安全地傳遞給 src1src2

返回

  • 在兩個 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。

__bpf_kfunc u32 bpf_cpumask_weight(const struct cpumask *cpumask)

返回 cpumask 中的位數。

引數

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 - src1src2 具有相同的已設定位。

  • false - src1src2 在至少一位上不同。

說明

struct bpf_cpumask 指標可以安全地傳遞給 src1src2

__bpf_kfunc bool bpf_cpumask_intersects(const struct cpumask *src1, const struct cpumask *src2)

檢查兩個 cpumask 是否重疊。

引數

const struct cpumask *src1

第一個輸入。

const struct cpumask *src2

第二個輸入。

返回

  • true - src1src2 至少有一個相同的已設定位。

  • false - src1src2 沒有相同的已設定位。

說明

struct bpf_cpumask 指標可以安全地傳遞給 src1src2

__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 指標可以安全地傳遞給 src1src2

__bpf_kfunc bool bpf_cpumask_empty(const struct cpumask *cpumask)

檢查 cpumask 是否為空。

引數

const struct cpumask *cpumask

正在檢查的 cpumask。

返回

  • true - cpumask 中沒有設定任何位。

  • false - cpumask 中至少設定了一位。

說明

struct bpf_cpumask 指標可以安全地傳遞給 cpumask

__bpf_kfunc bool bpf_cpumask_full(const struct cpumask *cpumask)

檢查 cpumask 是否已設定所有位。

引數

const struct cpumask *cpumask

正在檢查的 cpumask。

返回

  • true - cpumask 中的所有位均已設定。

  • false - cpumask 中至少有一位被清除。

說明

struct bpf_cpumask 指標可以安全地傳遞給 cpumask

__bpf_kfunc u32 bpf_cpumask_any_distribute(const struct cpumask *cpumask)

從 cpumask 返回一個隨機設定的 CPU。

引數

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 指標可以安全地傳遞給 src1src2


上面已經展示了這些查詢 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 自測套件中。