From: bitcoffee liuxin350@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IAOZOH?from=project-issue
----------------------------------------------
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: bitcoffee liuxin350@huawei.com --- drivers/net/ipvlan/ipvlan.h | 1 + drivers/net/ipvlan/ipvlan_core.c | 16 ++++++++++++++++ drivers/net/ipvlan/ipvlan_main.c | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+)
diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 6796e742c470..11d81fb63cdb 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -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 { diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index d5ae4fd9f1c0..7c40580ad5a9 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -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. diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 503e9c0995fb..0b9d74ea3688 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -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,