BPF_MAP_TYPE_CPUMAP¶
注意
BPF_MAP_TYPE_CPUMAP在核心版本 4.15 中引入
‘cpumap’ 主要用作後端對映,用於 XDP BPF 輔助呼叫 bpf_redirect_map() 以及 XDP_REDIRECT 動作,類似於 ‘devmap’。
與將 XDP 幀重定向到另一個 NIC 裝置的 devmap 不同,此對映型別將原始 XDP 幀重定向到另一個 CPU。遠端 CPU 將執行 SKB 分配並呼叫正常的網路堆疊。
此對映型別的一個示例用例是基於軟體的接收側擴充套件 (RSS)。
CPUMAP 表示系統中以對映鍵(map-key)索引的 CPU,對映值(map-value)是配置設定(每個 CPUMAP 條目)。每個 CPUMAP 條目都有一個繫結到給定 CPU 的專用核心執行緒,以代表遠端 CPU 執行單元。
從 Linux 核心版本 5.9 開始,CPUMAP 可以在遠端 CPU 上執行第二個 XDP 程式。這允許 XDP 程式將其處理分散到多個 CPU 上。例如,在初始 CPU(看到/接收資料包的 CPU)需要進行最少資料包處理,而遠端 CPU(資料包被定向到的 CPU)可以花費更多週期處理幀的場景中。初始 CPU 是 XDP 重定向程式執行的地方。遠端 CPU 接收原始的 xdp_frame 物件。
用法¶
核心 BPF¶
bpf_redirect_map()¶
long bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags)
將資料包重定向到由索引 key 處的 map 引用的端點。對於 BPF_MAP_TYPE_CPUMAP,此對映包含對 CPU 的引用。
如果對映查詢失敗,flags 的低兩位用作返回碼。這樣,返回值為呼叫者選擇的 XDP 程式返回碼之一,最高可達 XDP_TX。
使用者空間¶
注意
CPUMAP 條目只能從使用者空間更新/查詢/刪除,不能從 eBPF 程式進行。嘗試從核心 eBPF 程式呼叫這些函式將導致程式載入失敗併發出驗證器警告。
bpf_map_update_elem()¶
int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags);
可以使用 bpf_map_update_elem() 輔助函式新增或更新 CPU 條目。此輔助函式原子地替換現有元素。value 引數可以是 struct bpf_cpumap_val。
struct bpf_cpumap_val { __u32 qsize; /* queue size to remote target CPU */ union { int fd; /* prog fd on map write */ __u32 id; /* prog id on map read */ } bpf_prog; };
- flags 引數可以是以下之一
BPF_ANY: 建立新元素或更新現有元素。
BPF_NOEXIST: 僅當元素不存在時才建立新元素。
BPF_EXIST: 更新現有元素。
bpf_map_lookup_elem()¶
int bpf_map_lookup_elem(int fd, const void *key, void *value);
可以使用 bpf_map_lookup_elem() 輔助函式檢索 CPU 條目。
bpf_map_delete_elem()¶
int bpf_map_delete_elem(int fd, const void *key);
可以使用 bpf_map_delete_elem() 輔助函式刪除 CPU 條目。此輔助函式在成功時返回 0,失敗時返回負錯誤碼。
示例¶
核心¶
以下程式碼片段展示瞭如何宣告一個名為 cpu_map 的 BPF_MAP_TYPE_CPUMAP 以及如何使用輪詢排程方案將資料包重定向到遠端 CPU。
struct {
__uint(type, BPF_MAP_TYPE_CPUMAP);
__type(key, __u32);
__type(value, struct bpf_cpumap_val);
__uint(max_entries, 12);
} cpu_map SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, 12);
} cpus_available SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__type(key, __u32);
__type(value, __u32);
__uint(max_entries, 1);
} cpus_iterator SEC(".maps");
SEC("xdp")
int xdp_redir_cpu_round_robin(struct xdp_md *ctx)
{
__u32 key = 0;
__u32 cpu_dest = 0;
__u32 *cpu_selected, *cpu_iterator;
__u32 cpu_idx;
cpu_iterator = bpf_map_lookup_elem(&cpus_iterator, &key);
if (!cpu_iterator)
return XDP_ABORTED;
cpu_idx = *cpu_iterator;
*cpu_iterator += 1;
if (*cpu_iterator == bpf_num_possible_cpus())
*cpu_iterator = 0;
cpu_selected = bpf_map_lookup_elem(&cpus_available, &cpu_idx);
if (!cpu_selected)
return XDP_ABORTED;
cpu_dest = *cpu_selected;
if (cpu_dest >= bpf_num_possible_cpus())
return XDP_ABORTED;
return bpf_redirect_map(&cpu_map, cpu_dest, 0);
}
使用者空間¶
以下程式碼片段展示瞭如何動態地將 CPUMAP 的 max_entries 設定為系統上可用的最大 CPU 數量。
int set_max_cpu_entries(struct bpf_map *cpu_map)
{
if (bpf_map__set_max_entries(cpu_map, libbpf_num_possible_cpus()) < 0) {
fprintf(stderr, "Failed to set max entries for cpu_map map: %s",
strerror(errno));
return -1;
}
return 0;
}