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 表示系统中以映射键为索引的 CPU,映射值为配置设置(每个 CPUMAP 条目)。每个 CPUMAP 条目都有一个专用的内核线程绑定到给定的 CPU,以表示远程 CPU 执行单元。
从 Linux 内核版本 5.9 开始,CPUMAP 可以在远程 CPU 上运行第二个 XDP 程序。 这允许 XDP 程序将其处理拆分到多个 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)
将数据包重定向到 map
在索引 key
处引用的端点。对于 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;
}