eBPF/XDP中bpf_spin_lock加载报错解析及用法咨询
问题描述
我想要在eBPF内核程序中测试bpf_spin_lock函数,编写的代码如下:
#include <stddef.h> #include <linux/bpf.h> #include <linux/in.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <linux/ipv6.h> #include <linux/icmpv6.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_endian.h> /* Defines xdp_stats_map from packet04 */ #include "../common/xdp_stats_kern_user.h" #include "../common/xdp_stats_kern.h" #include <../common/parsing_helpers.h> #include <bpf/bpf_helpers.h> #define ETH_ALEN 6 #define MAX_ENTRIES 1000 /*struct { __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); __type(key, __u32); __type(value, __u64); __uint(max_entries, MAX_ENTRIES); } hash_map SEC(".maps"); */ struct hash_elem { int cnt; struct bpf_spin_lock lock; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u32); __type(value, struct hash_elem); __uint(max_entries, 100); } hash_map SEC(".maps"); struct a{struct bpf_spin_lock lock;}; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u32); __type(value, long); __uint(max_entries, 2); } hash_map1 SEC(".maps"); //static __u32 i=0; SEC("xdp") int xdp_prog_simple(struct xdp_md *ctx) { struct hash_elem val; bpf_spin_lock(&val.lock); val.cnt++; bpf_spin_unlock(&val.lock); bpf_map_update_elem(&hash_map, &val.cnt, &val, BPF_ANY); return XDP_PASS; }
加载程序时出现错误,错误日志如下:
libbpf: load bpf program failed: Permission denied libbpf: -- BEGIN DUMP LOG --- libbpf: ; bpf_spin_lock(&val.lock); 0: (bf) r6 = r10 1: (07) r6 += -4 ; bpf_spin_lock(&val.lock); 2: (bf) r1 = r6 3: (85) call bpf_spin_lock#93 R1 type=fp expected=map_value processed 4 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 libbpf: -- END LOG -- libbpf: failed to load program 'xdp' libbpf: failed to load object './k.o' ERR: loading BPF-OBJ file(./k.o) (-22): Invalid argument ERR: loading file: ./k.o
错误指令dump的最后部分为:
; bpf_spin_lock(&val.lock); 2: (bf) r1 = r6 3: (85) call bpf_spin_lock#93 R1 type=fp expected=map_value
错误含义解析
错误提示R1 type=fp expected=map_value明确指出:调用bpf_spin_lock时传入的参数类型不符合BPF验证器要求。
BPF自旋锁的设计规则是必须绑定在BPF映射的元素中,验证器期望传入的是映射存储的值(map_value类型),但你代码中传入的是栈上局部变量的地址(fp指栈帧指针,代表栈内存区域)。栈内存是每个程序实例私有的,用栈上的锁做同步没有意义,且存在安全风险,因此验证器直接拦截了这种用法。
汇编指令dump的含义
这段汇编对应代码中bpf_spin_lock(&val.lock);的执行逻辑:
0: (bf) r6 = r10:将栈帧指针r10的值复制到寄存器r61: (07) r6 += -4:调整r6的指向,定位到栈上val.lock成员的内存地址2: (bf) r1 = r6:将val.lock的地址放入寄存器r1(BPF调用约定中,r1是第一个函数参数)3: (85) call bpf_spin_lock#93:调用编号为93的bpf_spin_lock辅助函数
验证器在检查到r1指向栈内存而非映射值时,抛出了类型不匹配的错误。
XDP程序中能否使用自旋锁API?
可以,但有严格的使用限制:
- 锁必须存储在BPF映射中:只能对从哈希表(
BPF_MAP_TYPE_HASH)、数组(BPF_MAP_TYPE_ARRAY)等支持并发访问的映射中获取到的元素内的自旋锁进行操作,不能用在栈、普通全局变量上。 - 内核版本要求:一般需要Linux 4.18及以上内核版本支持BPF自旋锁特性。
- 上下文限制:XDP运行在网络驱动的中断上下文,自旋锁的持有时间必须极短,绝对不能执行任何可能阻塞的操作(如复杂的内存分配、耗时的辅助函数调用),否则会严重影响系统性能甚至导致死锁。
修正后的代码示例
要修复你的代码,需先从映射中获取元素,再对元素内的锁进行操作:
SEC("xdp") int xdp_prog_simple(struct xdp_md *ctx) { __u32 key = 0; // 示例用固定key,实际可根据数据包内容生成唯一key struct hash_elem *val = bpf_map_lookup_elem(&hash_map, &key); if (!val) { // 元素不存在时初始化 struct hash_elem init_val = {.cnt = 0}; bpf_map_update_elem(&hash_map, &key, &init_val, BPF_NOEXIST); val = bpf_map_lookup_elem(&hash_map, &key); if (!val) return XDP_PASS; } bpf_spin_lock(&val->lock); val->cnt++; bpf_spin_unlock(&val->lock); return XDP_PASS; }
内容的提问来源于stack exchange,提问作者user786
相关产品推荐
相关产品推荐

