searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

DPDK的rte bpf分析

2024-08-02 09:34:14
25
0

1、dpdk的bpf组件

dpdk提供了一个bpf的组件,编译成功后是librte_bpf.so/librte_bpf.a,以一个单独的库呈现。

特点:

1、可以不依赖于dpdk初始化,直接使用

2、用户自定义eBPF helper函数

3、支持arm和x86下的JIT

4、支持eBPF validate

5、支持rte mbuf

6、支持在rx/tx流程的处理

7、eBPF程序入口支持一个参数(void *)

不足之处:

1、不支持尾调用,即ebpf程序不能互相调用

2、API接口分析

2.1 结构体

//bpf主要的结构体信息
/**
 * Possible types for function/BPF program arguments.
 */
enum rte_bpf_arg_type {
	RTE_BPF_ARG_UNDEF,      /**< undefined */
	RTE_BPF_ARG_RAW,        /**< scalar value */
	RTE_BPF_ARG_PTR = 0x10, /**< pointer to data buffer */
	RTE_BPF_ARG_PTR_MBUF,   /**< pointer to rte_mbuf */
	RTE_BPF_ARG_RESERVED    /**< reserved for internal use */
};

/**
 * function argument information
 */
struct rte_bpf_arg {
	enum rte_bpf_arg_type type;
	/**
	 * for ptr type - max size of data buffer it points to
	 * for raw type - the size (in bytes) of the value
	 */
	size_t size;  /* type = RTE_BPF_ARG_PTR时,size = BUF大小 */
	size_t buf_size;
	/**< for mbuf ptr type, max size of rte_mbuf data buffer */
};

/**
 * Possible types for external symbols.
 */
enum rte_bpf_xtype {
	RTE_BPF_XTYPE_FUNC, /**< function */
	RTE_BPF_XTYPE_VAR   /**< variable */
};

/**
 * Definition for external symbols available in the BPF program.
 */
/*
描述eBPF helper的结构体,分为两种:
1、全局变量:当eBPF程序使用全局变量时,需要定义,但是一般不建议使用全局变量
2、函数:elf文件的 rel section中描述的函数。
*/
struct rte_bpf_xsym {
	const char *name;        /**< name */   /* elf文件的 rel section中描述的函数的名称 */
	enum rte_bpf_xtype type; /**< type */
	union {
		struct {
			uint64_t (*val)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);    /* eBPF helper 函数 */
			uint32_t nb_args;
			struct rte_bpf_arg args[EBPF_FUNC_MAX_ARGS];
			/**< Function arguments descriptions. */
			struct rte_bpf_arg ret; /**< function return value. */
		} func;
		struct {
			void *val; /**< actual memory location */
			struct rte_bpf_arg desc; /**< type, size, etc. */
		} var; /**< external variable */
	};
};

/**
 * Input parameters for loading eBPF code.
 */
struct rte_bpf_prm {
	const struct ebpf_insn *ins; /**< array of eBPF instructions */   //eBPF 字节码
	uint32_t nb_ins;            /**< number of instructions in ins */
	const struct rte_bpf_xsym *xsym;           //eBPF helper函数
	/**< array of external symbols that eBPF code is allowed to reference */
	uint32_t nb_xsym; /**< number of elements in xsym */
	struct rte_bpf_arg prog_arg; /**< eBPF program input arg description */ //eBPF程序的入口参数的描述
};

/**
 * Information about compiled into native ISA eBPF code.
 */
struct rte_bpf_jit {
	uint64_t (*func)(void *); /**< JIT-ed native code */   //eBPF程序的入口
	size_t sz;                /**< size of JIT-ed code */
};

/*  
rte bpf的主体
*/
struct rte_bpf {
	struct rte_bpf_prm prm;   //eBPF字节码等信息
	struct rte_bpf_jit jit;      //JIT
	size_t sz;        // sizeof(struct rte_bpf) + n_xsym * sizeof(struct rte_bpf_xsym) + n * sizeof(struct ebpf_insn)
	uint32_t stack_sz;   //
};

2.2 RX/TX收发包流程的接口( rte_bpf_ethdev.h )

前置:需使用dpdk进行收发包,才能使用。

(1)RX/TX加载eBPF的接口

int rte_bpf_eth_rx_elf_load(uint16_t port, uint16_t queue, const struct rte_bpf_prm *prm, const char *fname, const char *sname, uint32_t flags);

int rte_bpf_eth_tx_elf_load(uint16_t port, uint16_t queue, const struct rte_bpf_prm *prm, const char *fname, const char *sname, uint32_t flags);

参数:

port 端口
queue 队列
prm eBPF的helper函数
fname elf文件路径
sname elf文件的可执行区域
flags 预期执行eBPF的方法(JIT和非eBPF编码)

流程:

rte_bpf_eth_rx/tx_elf_load()
    ->bpf_eth_elf_load()
        ->select_rx_callback() /select_tx_callback() //根据 flags 和 eBPF程序入口参数类型,选择rx和tx的回调函数
        ->rte_bpf_elf_load() // 解析elf文件,翻译为eBPF字节码,做validate和JIT,得到eBPF , 见下文
        ->rte_bpf_get_jit() //通过bpf获取JIT
        ->bpf_eth_cbh_add() //根据port和queue 使能callback
        ->rte_eth_add_rx_callback() / rte_eth_add_tx_callback() //根据port id和queue id将callback挂在对应的收发包回调上

(2)RX/TX卸载eBPF的接口

void rte_bpf_eth_rx_unload(uint16_t port, uint16_t queue);

void rte_bpf_eth_tx_unload(uint16_t port, uint16_t queue);

参数:

port 端口号
queue 队列号

流程:

rte_bpf_eth_rx/tx_unload()
    ->bpf_eth_cbh_find() // 根据port和queue找到挂载点
    ->rte_eth_remove_rx_callback() / rte_eth_remove_tx_callback() //从rx和tx上卸载callback
    ->bpf_eth_cbi_unload() //删除挂载点

(3)call流程

rte_eth_rx_burst() / rte_eth_tx_burst()
     -> rte_eth_call_rx_callbacks()
           ->cb->fn.rx() / cb->fn.tx()  // 执行select_tx/rx_callback()的函数

2.3、 generic API (rte_bpf.h)

(1)加载eBPF的接口

struct rte_bpf * rte_bpf_load(const struct rte_bpf_prm *prm);  //通过prm得到bpf

备注:此接口需要填充好prm的所有信息(eBPF字节码,eBPF helper函数、eBPF程序入口参数的描述)

流程见下文:

struct rte_bpf * rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,  const char *sname);

参数:

prm eBPF helper 函数、eBPF程序入口参数的描述
fname elf文件路径
sname elf文件的可执行区域

流程:

rte_bpf_elf_load()
    ->open()  //根据 fname 打开elf文件
    ->bpf_load_elf()
        ->find_elf_code() //根据sname 找到elf文件的可执行区域
        ->elf_reloc_code() //do relocation 即将eBPF程序引用的外部函数(可以理解为库函数)替换为eBPF helper 函数
        ->rte_bpf_load()  //此时已经将elf文件转换为了eBPF字节码

rte_bpf_load()
    ->bpf_check_xsym()  //校验eBPF helper 函数是否合规
    ->bpf_load() //mmap 申请内存,得到struct rte bpf
    ->__rte_bpf_validate()  //eBPF validate
    ->__rte_bpf_jit()  // JIT

//可以修改rte_bpf_elf_load() 函数,只做将elf文件翻译为eBPF字节码。在不要求性能的情况下直接使用eBPF字节码

 

(2)执行eBPF的接口

uint64_t rte_bpf_exec(const struct rte_bpf *bpf, void *ctx);   //执行指定的context

uint32_t rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], uint32_t num);  //同一个eBPF程序,执行一组context

备注:这两个接口是直接执行eBPF字节码的接口,在不要求性能的情况下可以使用,要求性能时使用JIT

参数:

bpf eBPF程序
ctx eBPF程序入口的参数

流程:

rte_bpf_exec

rte_bpf_exec()
     ->rte_bpf_exec_burst() 
     ->bpf_exec()   //直接执行eBPF字节码

 

int rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit);   //执行JIT程序。

参数:

bpf eBPF程序
jit 出参,得到JIT
rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit)
{
	if (bpf == NULL || jit == NULL)
		return -EINVAL;

	jit[0] = bpf->jit;
	return 0;
}
拿到JIT后,运行 jit.func 即可

 

(3)销毁eBPF的接口

void rte_bpf_destroy(struct rte_bpf *bpf);  // 接口会销毁bpf和jit

rte_bpf_destroy(struct rte_bpf *bpf)
{
	if (bpf != NULL) {
		if (bpf->jit.func != NULL)
			munmap(bpf->jit.func, bpf->jit.sz);
		munmap(bpf, bpf->sz);
	}
}
0条评论
0 / 1000
z****n
8文章数
0粉丝数
z****n
8 文章 | 0 粉丝
原创

DPDK的rte bpf分析

2024-08-02 09:34:14
25
0

1、dpdk的bpf组件

dpdk提供了一个bpf的组件,编译成功后是librte_bpf.so/librte_bpf.a,以一个单独的库呈现。

特点:

1、可以不依赖于dpdk初始化,直接使用

2、用户自定义eBPF helper函数

3、支持arm和x86下的JIT

4、支持eBPF validate

5、支持rte mbuf

6、支持在rx/tx流程的处理

7、eBPF程序入口支持一个参数(void *)

不足之处:

1、不支持尾调用,即ebpf程序不能互相调用

2、API接口分析

2.1 结构体

//bpf主要的结构体信息
/**
 * Possible types for function/BPF program arguments.
 */
enum rte_bpf_arg_type {
	RTE_BPF_ARG_UNDEF,      /**< undefined */
	RTE_BPF_ARG_RAW,        /**< scalar value */
	RTE_BPF_ARG_PTR = 0x10, /**< pointer to data buffer */
	RTE_BPF_ARG_PTR_MBUF,   /**< pointer to rte_mbuf */
	RTE_BPF_ARG_RESERVED    /**< reserved for internal use */
};

/**
 * function argument information
 */
struct rte_bpf_arg {
	enum rte_bpf_arg_type type;
	/**
	 * for ptr type - max size of data buffer it points to
	 * for raw type - the size (in bytes) of the value
	 */
	size_t size;  /* type = RTE_BPF_ARG_PTR时,size = BUF大小 */
	size_t buf_size;
	/**< for mbuf ptr type, max size of rte_mbuf data buffer */
};

/**
 * Possible types for external symbols.
 */
enum rte_bpf_xtype {
	RTE_BPF_XTYPE_FUNC, /**< function */
	RTE_BPF_XTYPE_VAR   /**< variable */
};

/**
 * Definition for external symbols available in the BPF program.
 */
/*
描述eBPF helper的结构体,分为两种:
1、全局变量:当eBPF程序使用全局变量时,需要定义,但是一般不建议使用全局变量
2、函数:elf文件的 rel section中描述的函数。
*/
struct rte_bpf_xsym {
	const char *name;        /**< name */   /* elf文件的 rel section中描述的函数的名称 */
	enum rte_bpf_xtype type; /**< type */
	union {
		struct {
			uint64_t (*val)(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);    /* eBPF helper 函数 */
			uint32_t nb_args;
			struct rte_bpf_arg args[EBPF_FUNC_MAX_ARGS];
			/**< Function arguments descriptions. */
			struct rte_bpf_arg ret; /**< function return value. */
		} func;
		struct {
			void *val; /**< actual memory location */
			struct rte_bpf_arg desc; /**< type, size, etc. */
		} var; /**< external variable */
	};
};

/**
 * Input parameters for loading eBPF code.
 */
struct rte_bpf_prm {
	const struct ebpf_insn *ins; /**< array of eBPF instructions */   //eBPF 字节码
	uint32_t nb_ins;            /**< number of instructions in ins */
	const struct rte_bpf_xsym *xsym;           //eBPF helper函数
	/**< array of external symbols that eBPF code is allowed to reference */
	uint32_t nb_xsym; /**< number of elements in xsym */
	struct rte_bpf_arg prog_arg; /**< eBPF program input arg description */ //eBPF程序的入口参数的描述
};

/**
 * Information about compiled into native ISA eBPF code.
 */
struct rte_bpf_jit {
	uint64_t (*func)(void *); /**< JIT-ed native code */   //eBPF程序的入口
	size_t sz;                /**< size of JIT-ed code */
};

/*  
rte bpf的主体
*/
struct rte_bpf {
	struct rte_bpf_prm prm;   //eBPF字节码等信息
	struct rte_bpf_jit jit;      //JIT
	size_t sz;        // sizeof(struct rte_bpf) + n_xsym * sizeof(struct rte_bpf_xsym) + n * sizeof(struct ebpf_insn)
	uint32_t stack_sz;   //
};

2.2 RX/TX收发包流程的接口( rte_bpf_ethdev.h )

前置:需使用dpdk进行收发包,才能使用。

(1)RX/TX加载eBPF的接口

int rte_bpf_eth_rx_elf_load(uint16_t port, uint16_t queue, const struct rte_bpf_prm *prm, const char *fname, const char *sname, uint32_t flags);

int rte_bpf_eth_tx_elf_load(uint16_t port, uint16_t queue, const struct rte_bpf_prm *prm, const char *fname, const char *sname, uint32_t flags);

参数:

port 端口
queue 队列
prm eBPF的helper函数
fname elf文件路径
sname elf文件的可执行区域
flags 预期执行eBPF的方法(JIT和非eBPF编码)

流程:

rte_bpf_eth_rx/tx_elf_load()
    ->bpf_eth_elf_load()
        ->select_rx_callback() /select_tx_callback() //根据 flags 和 eBPF程序入口参数类型,选择rx和tx的回调函数
        ->rte_bpf_elf_load() // 解析elf文件,翻译为eBPF字节码,做validate和JIT,得到eBPF , 见下文
        ->rte_bpf_get_jit() //通过bpf获取JIT
        ->bpf_eth_cbh_add() //根据port和queue 使能callback
        ->rte_eth_add_rx_callback() / rte_eth_add_tx_callback() //根据port id和queue id将callback挂在对应的收发包回调上

(2)RX/TX卸载eBPF的接口

void rte_bpf_eth_rx_unload(uint16_t port, uint16_t queue);

void rte_bpf_eth_tx_unload(uint16_t port, uint16_t queue);

参数:

port 端口号
queue 队列号

流程:

rte_bpf_eth_rx/tx_unload()
    ->bpf_eth_cbh_find() // 根据port和queue找到挂载点
    ->rte_eth_remove_rx_callback() / rte_eth_remove_tx_callback() //从rx和tx上卸载callback
    ->bpf_eth_cbi_unload() //删除挂载点

(3)call流程

rte_eth_rx_burst() / rte_eth_tx_burst()
     -> rte_eth_call_rx_callbacks()
           ->cb->fn.rx() / cb->fn.tx()  // 执行select_tx/rx_callback()的函数

2.3、 generic API (rte_bpf.h)

(1)加载eBPF的接口

struct rte_bpf * rte_bpf_load(const struct rte_bpf_prm *prm);  //通过prm得到bpf

备注:此接口需要填充好prm的所有信息(eBPF字节码,eBPF helper函数、eBPF程序入口参数的描述)

流程见下文:

struct rte_bpf * rte_bpf_elf_load(const struct rte_bpf_prm *prm, const char *fname,  const char *sname);

参数:

prm eBPF helper 函数、eBPF程序入口参数的描述
fname elf文件路径
sname elf文件的可执行区域

流程:

rte_bpf_elf_load()
    ->open()  //根据 fname 打开elf文件
    ->bpf_load_elf()
        ->find_elf_code() //根据sname 找到elf文件的可执行区域
        ->elf_reloc_code() //do relocation 即将eBPF程序引用的外部函数(可以理解为库函数)替换为eBPF helper 函数
        ->rte_bpf_load()  //此时已经将elf文件转换为了eBPF字节码

rte_bpf_load()
    ->bpf_check_xsym()  //校验eBPF helper 函数是否合规
    ->bpf_load() //mmap 申请内存,得到struct rte bpf
    ->__rte_bpf_validate()  //eBPF validate
    ->__rte_bpf_jit()  // JIT

//可以修改rte_bpf_elf_load() 函数,只做将elf文件翻译为eBPF字节码。在不要求性能的情况下直接使用eBPF字节码

 

(2)执行eBPF的接口

uint64_t rte_bpf_exec(const struct rte_bpf *bpf, void *ctx);   //执行指定的context

uint32_t rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[], uint32_t num);  //同一个eBPF程序,执行一组context

备注:这两个接口是直接执行eBPF字节码的接口,在不要求性能的情况下可以使用,要求性能时使用JIT

参数:

bpf eBPF程序
ctx eBPF程序入口的参数

流程:

rte_bpf_exec

rte_bpf_exec()
     ->rte_bpf_exec_burst() 
     ->bpf_exec()   //直接执行eBPF字节码

 

int rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit);   //执行JIT程序。

参数:

bpf eBPF程序
jit 出参,得到JIT
rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit)
{
	if (bpf == NULL || jit == NULL)
		return -EINVAL;

	jit[0] = bpf->jit;
	return 0;
}
拿到JIT后,运行 jit.func 即可

 

(3)销毁eBPF的接口

void rte_bpf_destroy(struct rte_bpf *bpf);  // 接口会销毁bpf和jit

rte_bpf_destroy(struct rte_bpf *bpf)
{
	if (bpf != NULL) {
		if (bpf->jit.func != NULL)
			munmap(bpf->jit.func, bpf->jit.sz);
		munmap(bpf, bpf->sz);
	}
}
文章来自个人专栏
RDMA杂谈
8 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0