xdp 程序如何挂载

上一篇讲了xdp的hook点,我猜你肯定想到了xdp程序是如何与网卡驱动关联上的,也即如何在bpf_prog_run_xdp中被成功调用。

先看看bpf_prog_run_xdp函数的代码:

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)

看到这个代码,可能会有疑问,xdp程序只能挂载一个功能函数 ?是的,确实是只能挂载一个,等你看到struct bpf_prog对象更新的逻辑就会明白的。

很明显,我们要跟踪struct bpf_prog *prog 对象来自哪里,下面我们看看bpf_prog_run_xdp的上级函数ixgbe_run_xdp,ixgbe_run_xdp是ixgbe网卡xdp最关键的函数了,贴出来大家看看。

static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter,
                                     struct ixgbe_ring *rx_ring,
                                     struct xdp_buff *xdp)
{
        int err, result = IXGBE_XDP_PASS;
        struct bpf_prog *xdp_prog;
        struct xdp_frame *xdpf;
        u32 act;

        rcu_read_lock();
        xdp_prog = READ_ONCE(rx_ring->xdp_prog);

        if (!xdp_prog)
                goto xdp_out;

        prefetchw(xdp->data_hard_start); /* xdp_frame write */

        act = bpf_prog_run_xdp(xdp_prog, xdp);
        switch (act) {
        case XDP_PASS:
                break;
        case XDP_TX:
                xdpf = convert_to_xdp_frame(xdp);
                if (unlikely(!xdpf)) {
                        result = IXGBE_XDP_CONSUMED;
                        break;
                }
                result = ixgbe_xmit_xdp_ring(adapter, xdpf);
                break;
        case XDP_REDIRECT:
                err = xdp_do_redirect(adapter->netdev, xdp, xdp_prog);
                if (!err)
                        result = IXGBE_XDP_REDIR;
                else
                        result = IXGBE_XDP_CONSUMED;
                break;
        default:
                bpf_warn_invalid_xdp_action(act);
                /* fallthrough */
        case XDP_ABORTED:
                trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
                /* fallthrough -- handle aborts by dropping packet */
        case XDP_DROP:
                result = IXGBE_XDP_CONSUMED;
                break;
        }
xdp_out:
        rcu_read_unlock();
        return ERR_PTR(-result);
}

你一定看过很多xdp的其他文章,对于XDP_PASS/XDP_TX/XDP_REDIRECT等字眼很熟,这个函数就是ixbge xdp最关键的函数,我们前面讲的bpf_prog_run_xdp也是在这个函数中被唯一调用。

当然这些都不是重点,重点是我们想跟踪bpf_prog_run_xdp的struct bpf_prog *xdp_prog对象是哪里来的,所以我们还要继续往下看。

xdp_prog = READ_ONCE(rx_ring->xdp_prog) 这行代码已经告诉你bpf_prog_run_xdp的struct bpf_prog *xdp_prog对象来自于struct ixgbe_ring *rx_ring对象的xdp_prog对象。

继续跟踪代码,发现struct ixgbe_ring *rx_ring对象的xdp_prog对象又来自于struct ixgbe_adapter *对象的xdp_prog。而struct ixgbe_adapter *对象xdp_prog实在函数 ixgbe_xdp_setup初始化的。

static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog)
{
        int i, frame_size = dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
        struct ixgbe_adapter *adapter = netdev_priv(dev);
        struct bpf_prog *old_prog;

        if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)
                return -EINVAL;

        if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
                return -EINVAL;

        /* verify ixgbe ring attributes are sufficient for XDP */
        for (i = 0; i < adapter->num_rx_queues; i++) {
                struct ixgbe_ring *ring = adapter->rx_ring[i];

                if (ring_is_rsc_enabled(ring))
                        return -EINVAL;

                if (frame_size > ixgbe_rx_bufsz(ring))
                        return -EINVAL;
        }

        if (nr_cpu_ids > MAX_XDP_QUEUES)
                return -ENOMEM;

        old_prog = xchg(&adapter->xdp_prog, prog);

        /* If transitioning XDP modes reconfigure rings */
        if (!!prog != !!old_prog) {
                int err = ixgbe_setup_tc(dev, adapter->hw_tcs);

                if (err) {
                        rcu_assign_pointer(adapter->xdp_prog, old_prog);
                        return -EINVAL;
                }
        } else {
                for (i = 0; i < adapter->num_rx_queues; i++)
                        (void)xchg(&adapter->rx_ring[i]->xdp_prog,
                            adapter->xdp_prog);
        }

        if (old_prog)
                bpf_prog_put(old_prog);

        return 0;
}

而struct ixgbe_adapter *对象xdp_prog实在函数 ixgbe_xdp_setup中初始化的,所以我们的问题就变成了ixgbe_xdp_setup在什么时候调用。当日前面也提过了xdp只能挂载一个功能性函数,也在上面的ixgbe_xdp_setup函数中得到了印证。

继续ixgbe_xdp_setup函数的调用用关系:

ixgbe_xdp_setup

ixgbe_xdp(.ndo_bpf = ixgbe_xdp)

dev_xdp_install

dev_change_xdp_fd

do_setlink

--------------

bpf_set_link_xdp_fd

main (samples/bpf/xdp1_user.c)

好了,到这里,你肯定知道怎么回事了。

很明显在xdp的用户态程序中(比如samples/bpf/xdp1_user.c),一定会调用bpf_set_link_xdp_fd这个用户态函数,这个函数 通过netlink方式实现了网卡的xdp功能函数设置和替换。

这个函数中真正调用了.ndo_bpf = ixgbe_xdp,实现了网卡的prog对象切换。

static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op,
                           struct netlink_ext_ack *extack, u32 flags,
                           struct bpf_prog *prog)
{
        struct netdev_bpf xdp;

        memset(&xdp, 0, sizeof(xdp));
        if (flags & XDP_FLAGS_HW_MODE)
                xdp.command = XDP_SETUP_PROG_HW;
        else
                xdp.command = XDP_SETUP_PROG;
        xdp.extack = extack;
        xdp.flags = flags;
        xdp.prog = prog;

        return bpf_op(dev, &xdp);
}

好了,讲到这里就结束了,估计你还有疑问,xdp的功能程序存储在哪里,它又是怎么被找到的。

下一篇文章再讲。 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值