162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2019, Microsoft Corporation.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author:
562306a36Sopenharmony_ci *   Haiyang Zhang <haiyangz@microsoft.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/etherdevice.h>
1262306a36Sopenharmony_ci#include <linux/ethtool.h>
1362306a36Sopenharmony_ci#include <linux/netpoll.h>
1462306a36Sopenharmony_ci#include <linux/bpf.h>
1562306a36Sopenharmony_ci#include <linux/bpf_trace.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <net/xdp.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/mutex.h>
2062306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "hyperv_net.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ciu32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan,
2562306a36Sopenharmony_ci		   struct xdp_buff *xdp)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct netvsc_stats_rx *rx_stats = &nvchan->rx_stats;
2862306a36Sopenharmony_ci	void *data = nvchan->rsc.data[0];
2962306a36Sopenharmony_ci	u32 len = nvchan->rsc.len[0];
3062306a36Sopenharmony_ci	struct page *page = NULL;
3162306a36Sopenharmony_ci	struct bpf_prog *prog;
3262306a36Sopenharmony_ci	u32 act = XDP_PASS;
3362306a36Sopenharmony_ci	bool drop = true;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	xdp->data_hard_start = NULL;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	rcu_read_lock();
3862306a36Sopenharmony_ci	prog = rcu_dereference(nvchan->bpf_prog);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (!prog)
4162306a36Sopenharmony_ci		goto out;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* Ensure that the below memcpy() won't overflow the page buffer. */
4462306a36Sopenharmony_ci	if (len > ndev->mtu + ETH_HLEN) {
4562306a36Sopenharmony_ci		act = XDP_DROP;
4662306a36Sopenharmony_ci		goto out;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* allocate page buffer for data */
5062306a36Sopenharmony_ci	page = alloc_page(GFP_ATOMIC);
5162306a36Sopenharmony_ci	if (!page) {
5262306a36Sopenharmony_ci		act = XDP_DROP;
5362306a36Sopenharmony_ci		goto out;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	xdp_init_buff(xdp, PAGE_SIZE, &nvchan->xdp_rxq);
5762306a36Sopenharmony_ci	xdp_prepare_buff(xdp, page_address(page), NETVSC_XDP_HDRM, len, false);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	memcpy(xdp->data, data, len);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	switch (act) {
6462306a36Sopenharmony_ci	case XDP_PASS:
6562306a36Sopenharmony_ci	case XDP_TX:
6662306a36Sopenharmony_ci		drop = false;
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	case XDP_DROP:
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	case XDP_REDIRECT:
7362306a36Sopenharmony_ci		if (!xdp_do_redirect(ndev, xdp, prog)) {
7462306a36Sopenharmony_ci			nvchan->xdp_flush = true;
7562306a36Sopenharmony_ci			drop = false;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci			u64_stats_update_begin(&rx_stats->syncp);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci			rx_stats->xdp_redirect++;
8062306a36Sopenharmony_ci			rx_stats->packets++;
8162306a36Sopenharmony_ci			rx_stats->bytes += nvchan->rsc.pktlen;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci			u64_stats_update_end(&rx_stats->syncp);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci		} else {
8762306a36Sopenharmony_ci			u64_stats_update_begin(&rx_stats->syncp);
8862306a36Sopenharmony_ci			rx_stats->xdp_drop++;
8962306a36Sopenharmony_ci			u64_stats_update_end(&rx_stats->syncp);
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		fallthrough;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	case XDP_ABORTED:
9562306a36Sopenharmony_ci		trace_xdp_exception(ndev, prog, act);
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	default:
9962306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(ndev, prog, act);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciout:
10362306a36Sopenharmony_ci	rcu_read_unlock();
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (page && drop) {
10662306a36Sopenharmony_ci		__free_page(page);
10762306a36Sopenharmony_ci		xdp->data_hard_start = NULL;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return act;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciunsigned int netvsc_xdp_fraglen(unsigned int len)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	return SKB_DATA_ALIGN(len) +
11662306a36Sopenharmony_ci	       SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct bpf_prog *netvsc_xdp_get(struct netvsc_device *nvdev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return rtnl_dereference(nvdev->chan_table[0].bpf_prog);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint netvsc_xdp_set(struct net_device *dev, struct bpf_prog *prog,
12562306a36Sopenharmony_ci		   struct netlink_ext_ack *extack,
12662306a36Sopenharmony_ci		   struct netvsc_device *nvdev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct bpf_prog *old_prog;
12962306a36Sopenharmony_ci	int buf_max, i;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	old_prog = netvsc_xdp_get(nvdev);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (!old_prog && !prog)
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	buf_max = NETVSC_XDP_HDRM + netvsc_xdp_fraglen(dev->mtu + ETH_HLEN);
13762306a36Sopenharmony_ci	if (prog && buf_max > PAGE_SIZE) {
13862306a36Sopenharmony_ci		netdev_err(dev, "XDP: mtu:%u too large, buf_max:%u\n",
13962306a36Sopenharmony_ci			   dev->mtu, buf_max);
14062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "XDP: mtu too large");
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		return -EOPNOTSUPP;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (prog && (dev->features & NETIF_F_LRO)) {
14662306a36Sopenharmony_ci		netdev_err(dev, "XDP: not support LRO\n");
14762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "XDP: not support LRO");
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		return -EOPNOTSUPP;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (prog)
15362306a36Sopenharmony_ci		bpf_prog_add(prog, nvdev->num_chn - 1);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 0; i < nvdev->num_chn; i++)
15662306a36Sopenharmony_ci		rcu_assign_pointer(nvdev->chan_table[i].bpf_prog, prog);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (old_prog)
15962306a36Sopenharmony_ci		for (i = 0; i < nvdev->num_chn; i++)
16062306a36Sopenharmony_ci			bpf_prog_put(old_prog);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciint netvsc_vf_setxdp(struct net_device *vf_netdev, struct bpf_prog *prog)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct netdev_bpf xdp;
16862306a36Sopenharmony_ci	int ret;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ASSERT_RTNL();
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!vf_netdev)
17362306a36Sopenharmony_ci		return 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!vf_netdev->netdev_ops->ndo_bpf)
17662306a36Sopenharmony_ci		return 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	memset(&xdp, 0, sizeof(xdp));
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (prog)
18162306a36Sopenharmony_ci		bpf_prog_inc(prog);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	xdp.command = XDP_SETUP_PROG;
18462306a36Sopenharmony_ci	xdp.prog = prog;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = vf_netdev->netdev_ops->ndo_bpf(vf_netdev, &xdp);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (ret && prog)
18962306a36Sopenharmony_ci		bpf_prog_put(prog);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return ret;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciint netvsc_bpf(struct net_device *dev, struct netdev_bpf *bpf)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(dev);
19762306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
19862306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev);
19962306a36Sopenharmony_ci	struct netlink_ext_ack *extack = bpf->extack;
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy) {
20362306a36Sopenharmony_ci		return -ENODEV;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	switch (bpf->command) {
20762306a36Sopenharmony_ci	case XDP_SETUP_PROG:
20862306a36Sopenharmony_ci		ret = netvsc_xdp_set(dev, bpf->prog, extack, nvdev);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		if (ret)
21162306a36Sopenharmony_ci			return ret;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		ret = netvsc_vf_setxdp(vf_netdev, bpf->prog);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		if (ret) {
21662306a36Sopenharmony_ci			netdev_err(dev, "vf_setxdp failed:%d\n", ret);
21762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "vf_setxdp failed");
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci			netvsc_xdp_set(dev, NULL, extack, nvdev);
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		return ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	default:
22562306a36Sopenharmony_ci		return -EINVAL;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int netvsc_ndoxdp_xmit_fm(struct net_device *ndev,
23062306a36Sopenharmony_ci				 struct xdp_frame *frame, u16 q_idx)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct sk_buff *skb;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	skb = xdp_build_skb_from_frame(frame, ndev);
23562306a36Sopenharmony_ci	if (unlikely(!skb))
23662306a36Sopenharmony_ci		return -ENOMEM;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	netvsc_get_hash(skb, netdev_priv(ndev));
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	skb_record_rx_queue(skb, q_idx);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	netvsc_xdp_xmit(skb, ndev);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciint netvsc_ndoxdp_xmit(struct net_device *ndev, int n,
24862306a36Sopenharmony_ci		       struct xdp_frame **frames, u32 flags)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
25162306a36Sopenharmony_ci	const struct net_device_ops *vf_ops;
25262306a36Sopenharmony_ci	struct netvsc_stats_tx *tx_stats;
25362306a36Sopenharmony_ci	struct netvsc_device *nvsc_dev;
25462306a36Sopenharmony_ci	struct net_device *vf_netdev;
25562306a36Sopenharmony_ci	int i, count = 0;
25662306a36Sopenharmony_ci	u16 q_idx;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Don't transmit if netvsc_device is gone */
25962306a36Sopenharmony_ci	nvsc_dev = rcu_dereference_bh(ndev_ctx->nvdev);
26062306a36Sopenharmony_ci	if (unlikely(!nvsc_dev || nvsc_dev->destroy))
26162306a36Sopenharmony_ci		return 0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/* If VF is present and up then redirect packets to it.
26462306a36Sopenharmony_ci	 * Skip the VF if it is marked down or has no carrier.
26562306a36Sopenharmony_ci	 * If netpoll is in uses, then VF can not be used either.
26662306a36Sopenharmony_ci	 */
26762306a36Sopenharmony_ci	vf_netdev = rcu_dereference_bh(ndev_ctx->vf_netdev);
26862306a36Sopenharmony_ci	if (vf_netdev && netif_running(vf_netdev) &&
26962306a36Sopenharmony_ci	    netif_carrier_ok(vf_netdev) && !netpoll_tx_running(ndev) &&
27062306a36Sopenharmony_ci	    vf_netdev->netdev_ops->ndo_xdp_xmit &&
27162306a36Sopenharmony_ci	    ndev_ctx->data_path_is_vf) {
27262306a36Sopenharmony_ci		vf_ops = vf_netdev->netdev_ops;
27362306a36Sopenharmony_ci		return vf_ops->ndo_xdp_xmit(vf_netdev, n, frames, flags);
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	q_idx = smp_processor_id() % ndev->real_num_tx_queues;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
27962306a36Sopenharmony_ci		if (netvsc_ndoxdp_xmit_fm(ndev, frames[i], q_idx))
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		count++;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	tx_stats = &nvsc_dev->chan_table[q_idx].tx_stats;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	u64_stats_update_begin(&tx_stats->syncp);
28862306a36Sopenharmony_ci	tx_stats->xdp_xmit += count;
28962306a36Sopenharmony_ci	u64_stats_update_end(&tx_stats->syncp);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return count;
29262306a36Sopenharmony_ci}
293