BPF_MAP_TYPE_ARRAY 和 BPF_MAP_TYPE_PERCPU_ARRAY¶
注意
BPF_MAP_TYPE_ARRAY在核心版本 3.19 中引入BPF_MAP_TYPE_PERCPU_ARRAY在版本 4.6 中引入
BPF_MAP_TYPE_ARRAY 和 BPF_MAP_TYPE_PERCPU_ARRAY 提供通用的陣列儲存。鍵型別是一個無符號 32 位整數(4 位元組),對映是恆定大小的。陣列的大小在建立時透過 max_entries 定義。所有陣列元素在建立時預先分配並零初始化。BPF_MAP_TYPE_PERCPU_ARRAY 為每個 CPU 使用不同的記憶體區域,而 BPF_MAP_TYPE_ARRAY 使用相同的記憶體區域。儲存的值可以是任何大小,但是,所有陣列元素都對齊到 8 位元組。
自核心 5.5 版本起,透過設定標誌 BPF_F_MMAPABLE,可以為 BPF_MAP_TYPE_ARRAY 啟用記憶體對映。對映定義是頁對齊的,並從第一頁開始。分配足夠的頁大小和頁對齊的記憶體塊以儲存所有陣列值,從第二頁開始,這在某些情況下會導致記憶體的過度分配。使用此功能的優點是提高了效能和易用性,因為使用者空間程式不需要使用輔助函式來訪問和修改資料。
用法¶
核心 BPF¶
bpf_map_lookup_elem()¶
void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
可以使用 bpf_map_lookup_elem() 輔助函式檢索陣列元素。此輔助函式返回指向陣列元素的指標,因此為了避免與使用者空間讀取值時發生資料競爭,使用者在原地更新值時必須使用 __sync_fetch_and_add() 等原語。
bpf_map_update_elem()¶
long bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
可以使用 bpf_map_update_elem() 輔助函式更新陣列元素。
bpf_map_update_elem() 成功時返回 0,失敗時返回負錯誤碼。
由於陣列是固定大小的,因此不支援 bpf_map_delete_elem()。要清除陣列元素,可以使用 bpf_map_update_elem() 向該索引插入一個零值。
Per CPU 陣列¶
儲存在 BPF_MAP_TYPE_ARRAY 中的值可以被不同 CPU 上的多個程式訪問。要將儲存限制在單個 CPU,可以使用 BPF_MAP_TYPE_PERCPU_ARRAY。
使用 BPF_MAP_TYPE_PERCPU_ARRAY 時,bpf_map_update_elem() 和 bpf_map_lookup_elem() 輔助函式會自動訪問當前 CPU 的槽位。
bpf_map_lookup_percpu_elem()¶
void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu)
可以使用 bpf_map_lookup_percpu_elem() 輔助函式查詢特定 CPU 的陣列值。成功時返回值,如果未找到條目或 cpu 無效,則返回 NULL。
併發¶
自核心版本 5.1 起,BPF 基礎設施提供了 struct bpf_spin_lock 用於同步訪問。
使用者空間¶
從使用者空間訪問使用與上述同名的 libbpf API,對映透過其 fd 標識。
示例¶
有關功能示例,請參閱 tools/testing/selftests/bpf 目錄。以下程式碼示例演示了 API 的用法。
核心 BPF¶
此程式碼片段展示瞭如何在 BPF 程式中宣告一個數組。
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, long);
__uint(max_entries, 256);
} my_map SEC(".maps");
此示例 BPF 程式展示瞭如何訪問陣列元素。
int bpf_prog(struct __sk_buff *skb)
{
struct iphdr ip;
int index;
long *value;
if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip, sizeof(ip)) < 0)
return 0;
index = ip.protocol;
value = bpf_map_lookup_elem(&my_map, &index);
if (value)
__sync_fetch_and_add(value, skb->len);
return 0;
}
使用者空間¶
BPF_MAP_TYPE_ARRAY¶
此程式碼片段展示瞭如何使用 bpf_map_create_opts 設定標誌來建立陣列。
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
int create_array()
{
int fd;
LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY,
"example_array", /* name */
sizeof(__u32), /* key size */
sizeof(long), /* value size */
256, /* max entries */
&opts); /* create opts */
return fd;
}
此程式碼片段展示瞭如何初始化陣列的元素。
int initialize_array(int fd)
{
__u32 i;
long value;
int ret;
for (i = 0; i < 256; i++) {
value = i;
ret = bpf_map_update_elem(fd, &i, &value, BPF_ANY);
if (ret < 0)
return ret;
}
return ret;
}
此程式碼片段展示瞭如何從陣列中檢索元素值。
int lookup(int fd)
{
__u32 index = 42;
long value;
int ret;
ret = bpf_map_lookup_elem(fd, &index, &value);
if (ret < 0)
return ret;
/* use value here */
assert(value == 42);
return ret;
}
BPF_MAP_TYPE_PERCPU_ARRAY¶
此程式碼片段展示瞭如何初始化每 CPU 陣列的元素。
int initialize_array(int fd)
{
int ncpus = libbpf_num_possible_cpus();
long values[ncpus];
__u32 i, j;
int ret;
for (i = 0; i < 256 ; i++) {
for (j = 0; j < ncpus; j++)
values[j] = i;
ret = bpf_map_update_elem(fd, &i, &values, BPF_ANY);
if (ret < 0)
return ret;
}
return ret;
}
此程式碼片段展示瞭如何訪問陣列值的每 CPU 元素。
int lookup(int fd)
{
int ncpus = libbpf_num_possible_cpus();
__u32 index = 42, j;
long values[ncpus];
int ret;
ret = bpf_map_lookup_elem(fd, &index, &values);
if (ret < 0)
return ret;
for (j = 0; j < ncpus; j++) {
/* Use per CPU value here */
assert(values[j] == 42);
}
return ret;
}
語義¶
如上例所示,在使用者空間中訪問 BPF_MAP_TYPE_PERCPU_ARRAY 時,每個值都是一個包含 ncpus 元素的陣列。
呼叫 bpf_map_update_elem() 時,這些對映不能使用標誌 BPF_NOEXIST。