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_mapBPF_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;
}

參考資料