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);
}
}