1、dpdk的bpf組件
dpdk提供了一個bpf的(de)組件,編譯成功后是librte_bpf.so/librte_bpf.a,以一個單獨的(de)庫呈現。
特點:
1、可以不依(yi)賴于(yu)dpdk初(chu)始化,直接使用
2、用戶自定義eBPF helper函(han)數(shu)
3、支持arm和(he)x86下的JIT
4、支持eBPF validate
5、支持rte mbuf
6、支持在rx/tx流程(cheng)的處理
7、eBPF程序(xu)入口(kou)支持一(yi)個參數(void *)
不足之處:
1、不支持尾(wei)調(diao)用,即(ji)ebpf程序不能互相調(diao)用
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 )
前置(zhi):需(xu)使用dpdk進行(xing)收(shou)發包,才能使用。
(1)RX/TX加載eBPF的(de)接口
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卸載(zai)eBPF的接口(kou)
void rte_bpf_eth_rx_unload(uint16_t port, uint16_t queue);
void rte_bpf_eth_tx_unload(uint16_t port, uint16_t queue);
參(can)數:
| 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的接(jie)口
struct rte_bpf * rte_bpf_load(const struct rte_bpf_prm *prm); //通過prm得到bpf
備注:此接(jie)口需(xu)要填充(chong)好prm的(de)所有信息(eBPF字節碼,eBPF helper函數、eBPF程(cheng)序(xu)入口參(can)數的(de)描述)
流程見下文:
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的接口(kou)
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); //同一(yi)(yi)個eBPF程序,執(zhi)行一(yi)(yi)組context
備注(zhu):這兩個接口是直(zhi)接執行eBPF字節碼的(de)接口,在不要求(qiu)性能的(de)情況(kuang)下可(ke)以使(shi)(shi)用(yong),要求(qiu)性能時(shi)使(shi)(shi)用(yong)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); // 接(jie)口(kou)會銷毀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);
	}
}