You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的值复制到寄存器r6
  • 1: (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?

可以,但有严格的使用限制:

  1. 锁必须存储在BPF映射中:只能对从哈希表(BPF_MAP_TYPE_HASH)、数组(BPF_MAP_TYPE_ARRAY)等支持并发访问的映射中获取到的元素内的自旋锁进行操作,不能用在栈、普通全局变量上。
  2. 内核版本要求:一般需要Linux 4.18及以上内核版本支持BPF自旋锁特性。
  3. 上下文限制: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:06:28