Commit 373ce97b authored by bitcoffee's avatar bitcoffee Committed by Yue Haibing
Browse files

ipvlan: support use xdp native mode

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IAOZOH



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

Currently, the ipvlan doesn't support the xdp native mode. We want
to add the xdp native mode to the ipvlan in a simple way.

Because the working mode of ipvlan is special, it doesn't have the
concept of queue. Therefore, the packet data received by the ipvlan
is sent to the same xsk, and the function is processed from the
soft interrupt of the real dev. Because xdp_do_redirect/xdp_do_flush
are all lock-free mode, if xdp_do_redirect and xdp_do_flush are used
on the ipvlan, different packet receiving queues(from different cpus)
write data to the same xsk, the concurrency problem directly causes
the kernel to crash.

To fix this problem, we need to change the current packet receiving
mode of the ipvlan, or extend the xdp redirection and flush to provide
locked version. However, in fact, do_xdp_generic is already a locked
version, so using do_xdp_generic directly meets our requirements,
compared with running on xdp generic mode. Advance do_xdp_generic to
the NIC driver layer to reduce the invoking of a software interrupt
and slightly improve the performance. Each time an ipvlan is entered,
a single SKB is processed. In fact, multiple SKB of do_xdp_flush in
the ipvlane cannot be refreshed at the same time.

Signed-off-by: default avatarbitcoffee <liuxin350@huawei.com>
parent 313516d1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ struct ipvl_dev {
	unsigned long           local_timeout;
	struct timer_list       local_free_timer;
	struct sk_buff_head     local_xmit_queue;
	struct bpf_prog __rcu   *xdp_prog;
};

struct ipvl_addr {
+16 −0
Original line number Diff line number Diff line
@@ -314,11 +314,27 @@ static int ipvlan_rcv_frame(struct ipvl_addr *addr, struct sk_buff **pskb,
{
	struct ipvl_dev *ipvlan = addr->master;
	struct net_device *dev = ipvlan->dev;
	struct bpf_prog *xdp_prog = rtnl_dereference(ipvlan->xdp_prog);
	unsigned int len;
	rx_handler_result_t ret = RX_HANDLER_CONSUMED;
	bool success = false;
	struct sk_buff *skb = *pskb;
	struct net_device *old_dev = skb->dev;
	int xdp_ret;

	if (!xdp_prog)
		goto go_network_stack;
	skb->dev = dev;
#ifdef CONFIG_XSK_MULTI_BUF
	xdp_ret = do_xdp_generic_multi(xdp_prog, &skb);
#else
	xdp_ret = do_xdp_generic(xdp_prog, skb);
#endif
	if (xdp_ret != XDP_PASS)
		return ret;
	skb->dev = old_dev;

go_network_stack:
	len = skb->len + ETH_HLEN;
	/* Only packets exchanged between two local slaves need to have
	 * device-up check as well as skb-share check.
+22 −0
Original line number Diff line number Diff line
@@ -424,6 +424,27 @@ static int ipvlan_get_iflink(const struct net_device *dev)
	return ipvlan->phy_dev->ifindex;
}

static int ipvlan_xdp_set(struct net_device *dev, struct bpf_prog *prog,
			  struct netlink_ext_ack *extack)
{
	struct ipvl_dev *priv = netdev_priv(dev);
	struct bpf_prog *old_prog;

	old_prog = rtnl_dereference(priv->xdp_prog);
	rcu_assign_pointer(priv->xdp_prog, prog);
	return 0;
}

static int ipvlan_xdp(struct net_device *dev, struct netdev_bpf *xdp)
{
	switch (xdp->command) {
	case XDP_SETUP_PROG:
		return ipvlan_xdp_set(dev, xdp->prog, xdp->extack);
	default:
		return -EINVAL;
	}
}

static const struct net_device_ops ipvlan_netdev_ops = {
	.ndo_init		= ipvlan_init,
	.ndo_uninit		= ipvlan_uninit,
@@ -437,6 +458,7 @@ static const struct net_device_ops ipvlan_netdev_ops = {
	.ndo_vlan_rx_add_vid	= ipvlan_vlan_rx_add_vid,
	.ndo_vlan_rx_kill_vid	= ipvlan_vlan_rx_kill_vid,
	.ndo_get_iflink		= ipvlan_get_iflink,
	.ndo_bpf		= ipvlan_xdp,
};

static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev,