XDP 支持三种操作模式:
Native XDP(XDP_FLAGS_DRV_MODE)
默认的工作模式,Native工作在网卡驱动的RX队列上。
Offloaded XDP(XDP_FLAGS_HW_MODE)
offloaded 模式,XDP BPF程序直接工作在NIC中处理报文,一般是智能网卡的模式。
Generic XDP(XDP_FLAGS_SKB_MODE)
通用模式,当上述两种模式都不支持的时候,还可以使用通用模式。
这三种模式分别工作在不同的层次,Native XDP在网卡驱动中,Offloaded XDP 硬件中,Generic XDP网络协议中。性能有很大的差别。
今天我们重点看看Native XDP的hook点,所以Native XDP是我们今天的主角。
看到xdp功能的时候,你可能已经猜到了xdp一定有在内核中有hook,你也可能猜到了xdp的hook应该在网卡程序中,但是你可能不知道xdp hook 具体在网卡驱动的那个流程中,今天走读了一遍xdp的代码逻辑,简单记录内容如下。
bpf_prog_run_xdp
bpf_prog_run_xdp函数是xdp的框架函数,你注册的xdp程序将会在这个函数中被调用。
xdp的hook是在内核驱动中 ? 你可以过滤一下这个函数,大概应该和我的差不多:
xxx@xxx$ grep -rn "bpf_prog_run_xdp" .
./net/core/dev.c:4324: act = bpf_prog_run_xdp(xdp_prog, xdp);
./drivers/net/tun.c:1634: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/ethernet/intel/i40e/i40e_txrx.c:2242: act = bpf_prog_run_xdp(xdp_prog, xdp);
./drivers/net/ethernet/intel/ixgbe/ixgbe_main.c:2214: act = bpf_prog_run_xdp(xdp_prog, xdp);
./drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c:1067: act = bpf_prog_run_xdp(xdp_prog, xdp);
./drivers/net/ethernet/qlogic/qede/qede_fp.c:1084: act = bpf_prog_run_xdp(prog, &xdp);
./drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c:106: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/ethernet/mellanox/mlx4/en_rx.c:775: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c:71: act = bpf_prog_run_xdp(prog, &xdp);
./drivers/net/ethernet/netronome/nfp/nfp_net_common.c:1744: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/ethernet/cavium/thunder/nicvf_main.c:538: action = bpf_prog_run_xdp(prog, &xdp);
./drivers/net/veth.c:399: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/veth.c:525: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/virtio_net.c:668: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./drivers/net/virtio_net.c:823: act = bpf_prog_run_xdp(xdp_prog, &xdp);
./include/linux/filter.h:619:static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
当然,这不是关键,这只是印证了前面的第二个 猜测。
下面我们具体看看这个函数:
static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog,
struct xdp_buff *xdp)
{
/* Caller needs to hold rcu_read_lock() (!), otherwise program
* can be released while still running, or map elements could be
* freed early while still having concurrent users. XDP fastpath
* already takes rcu_read_lock() when fetching the program, so
* it's not necessary here anymore.
*/
return BPF_PROG_RUN(prog, xdp);
}
#define BPF_PROG_RUN(filter, ctx) (*(filter)->bpf_func)(ctx, (filter)->insnsi)
BPF_PROG_RUN在执行你挂载的函数,看上去在xdp的框架函数中只能挂载有一个xdp程序?
当然你可能对怎么挂载感兴趣,下一篇再讲。
xdp的挂载点
顺着 bpf_prog_run_xdp 的线索,去查找它的调用关系,你很快就能找到xdp的真正挂载点。
bpf_prog_run_xdp
ixgbe_run_xdp
ixgbe_clean_rx_irq
ixgbe_poll
/**
* ixgbe_poll - NAPI Rx polling callback
* @napi: structure for representing this polling device
* @budget: how many packets driver is allowed to clean
*
* This function is used for legacy and MSI, NAPI mode
**/
int ixgbe_poll(struct napi_struct *napi, int budget)
看到这差不多已经知道他的挂载点了吗 ?
另外,上面的调用关系只在rx方向有逻辑,tx是没有的。所以xdp支持在rx的报文处理,如果想要在tx方向处理,你只能转而研究tc epbf 了,tc比xdp强大的是rx tx都有hook点,但是论性能就比xdp差了,为啥差呢,已经很明显了吧。