162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Haiyang Zhang <haiyangz@microsoft.com>
762306a36Sopenharmony_ci *   Hank Janssen  <hjanssen@microsoft.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/atomic.h>
1362306a36Sopenharmony_ci#include <linux/ethtool.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/highmem.h>
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/netdevice.h>
2062306a36Sopenharmony_ci#include <linux/inetdevice.h>
2162306a36Sopenharmony_ci#include <linux/etherdevice.h>
2262306a36Sopenharmony_ci#include <linux/pci.h>
2362306a36Sopenharmony_ci#include <linux/skbuff.h>
2462306a36Sopenharmony_ci#include <linux/if_vlan.h>
2562306a36Sopenharmony_ci#include <linux/in.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2862306a36Sopenharmony_ci#include <linux/netpoll.h>
2962306a36Sopenharmony_ci#include <linux/bpf.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <net/arp.h>
3262306a36Sopenharmony_ci#include <net/route.h>
3362306a36Sopenharmony_ci#include <net/sock.h>
3462306a36Sopenharmony_ci#include <net/pkt_sched.h>
3562306a36Sopenharmony_ci#include <net/checksum.h>
3662306a36Sopenharmony_ci#include <net/ip6_checksum.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "hyperv_net.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define RING_SIZE_MIN	64
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define LINKCHANGE_INT (2 * HZ)
4362306a36Sopenharmony_ci#define VF_TAKEOVER_INT (HZ / 10)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Macros to define the context of vf registration */
4662306a36Sopenharmony_ci#define VF_REG_IN_PROBE		1
4762306a36Sopenharmony_ci#define VF_REG_IN_NOTIFIER	2
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic unsigned int ring_size __ro_after_init = 128;
5062306a36Sopenharmony_cimodule_param(ring_size, uint, 0444);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(ring_size, "Ring buffer size (# of 4K pages)");
5262306a36Sopenharmony_ciunsigned int netvsc_ring_bytes __ro_after_init;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
5562306a36Sopenharmony_ci				NETIF_MSG_LINK | NETIF_MSG_IFUP |
5662306a36Sopenharmony_ci				NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR |
5762306a36Sopenharmony_ci				NETIF_MSG_TX_ERR;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int debug = -1;
6062306a36Sopenharmony_cimodule_param(debug, int, 0444);
6162306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic LIST_HEAD(netvsc_dev_list);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void netvsc_change_rx_flags(struct net_device *net, int change)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
6862306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
6962306a36Sopenharmony_ci	int inc;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (!vf_netdev)
7262306a36Sopenharmony_ci		return;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (change & IFF_PROMISC) {
7562306a36Sopenharmony_ci		inc = (net->flags & IFF_PROMISC) ? 1 : -1;
7662306a36Sopenharmony_ci		dev_set_promiscuity(vf_netdev, inc);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (change & IFF_ALLMULTI) {
8062306a36Sopenharmony_ci		inc = (net->flags & IFF_ALLMULTI) ? 1 : -1;
8162306a36Sopenharmony_ci		dev_set_allmulti(vf_netdev, inc);
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void netvsc_set_rx_mode(struct net_device *net)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
8862306a36Sopenharmony_ci	struct net_device *vf_netdev;
8962306a36Sopenharmony_ci	struct netvsc_device *nvdev;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	rcu_read_lock();
9262306a36Sopenharmony_ci	vf_netdev = rcu_dereference(ndev_ctx->vf_netdev);
9362306a36Sopenharmony_ci	if (vf_netdev) {
9462306a36Sopenharmony_ci		dev_uc_sync(vf_netdev, net);
9562306a36Sopenharmony_ci		dev_mc_sync(vf_netdev, net);
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	nvdev = rcu_dereference(ndev_ctx->nvdev);
9962306a36Sopenharmony_ci	if (nvdev)
10062306a36Sopenharmony_ci		rndis_filter_update(nvdev);
10162306a36Sopenharmony_ci	rcu_read_unlock();
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void netvsc_tx_enable(struct netvsc_device *nvscdev,
10562306a36Sopenharmony_ci			     struct net_device *ndev)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	nvscdev->tx_disable = false;
10862306a36Sopenharmony_ci	virt_wmb(); /* ensure queue wake up mechanism is on */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	netif_tx_wake_all_queues(ndev);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int netvsc_open(struct net_device *net)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
11662306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
11762306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndev_ctx->nvdev);
11862306a36Sopenharmony_ci	struct rndis_device *rdev;
11962306a36Sopenharmony_ci	int ret = 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	netif_carrier_off(net);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Open up the device */
12462306a36Sopenharmony_ci	ret = rndis_filter_open(nvdev);
12562306a36Sopenharmony_ci	if (ret != 0) {
12662306a36Sopenharmony_ci		netdev_err(net, "unable to open device (ret %d).\n", ret);
12762306a36Sopenharmony_ci		return ret;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	rdev = nvdev->extension;
13162306a36Sopenharmony_ci	if (!rdev->link_state) {
13262306a36Sopenharmony_ci		netif_carrier_on(net);
13362306a36Sopenharmony_ci		netvsc_tx_enable(nvdev, net);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (vf_netdev) {
13762306a36Sopenharmony_ci		/* Setting synthetic device up transparently sets
13862306a36Sopenharmony_ci		 * slave as up. If open fails, then slave will be
13962306a36Sopenharmony_ci		 * still be offline (and not used).
14062306a36Sopenharmony_ci		 */
14162306a36Sopenharmony_ci		ret = dev_open(vf_netdev, NULL);
14262306a36Sopenharmony_ci		if (ret)
14362306a36Sopenharmony_ci			netdev_warn(net,
14462306a36Sopenharmony_ci				    "unable to open slave: %s: %d\n",
14562306a36Sopenharmony_ci				    vf_netdev->name, ret);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int netvsc_wait_until_empty(struct netvsc_device *nvdev)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	unsigned int retry = 0;
15362306a36Sopenharmony_ci	int i;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Ensure pending bytes in ring are read */
15662306a36Sopenharmony_ci	for (;;) {
15762306a36Sopenharmony_ci		u32 aread = 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		for (i = 0; i < nvdev->num_chn; i++) {
16062306a36Sopenharmony_ci			struct vmbus_channel *chn
16162306a36Sopenharmony_ci				= nvdev->chan_table[i].channel;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci			if (!chn)
16462306a36Sopenharmony_ci				continue;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			/* make sure receive not running now */
16762306a36Sopenharmony_ci			napi_synchronize(&nvdev->chan_table[i].napi);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci			aread = hv_get_bytes_to_read(&chn->inbound);
17062306a36Sopenharmony_ci			if (aread)
17162306a36Sopenharmony_ci				break;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci			aread = hv_get_bytes_to_read(&chn->outbound);
17462306a36Sopenharmony_ci			if (aread)
17562306a36Sopenharmony_ci				break;
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		if (aread == 0)
17962306a36Sopenharmony_ci			return 0;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		if (++retry > RETRY_MAX)
18262306a36Sopenharmony_ci			return -ETIMEDOUT;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		usleep_range(RETRY_US_LO, RETRY_US_HI);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void netvsc_tx_disable(struct netvsc_device *nvscdev,
18962306a36Sopenharmony_ci			      struct net_device *ndev)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	if (nvscdev) {
19262306a36Sopenharmony_ci		nvscdev->tx_disable = true;
19362306a36Sopenharmony_ci		virt_wmb(); /* ensure txq will not wake up after stop */
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	netif_tx_disable(ndev);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int netvsc_close(struct net_device *net)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct net_device_context *net_device_ctx = netdev_priv(net);
20262306a36Sopenharmony_ci	struct net_device *vf_netdev
20362306a36Sopenharmony_ci		= rtnl_dereference(net_device_ctx->vf_netdev);
20462306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	netvsc_tx_disable(nvdev, net);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* No need to close rndis filter if it is removed already */
21062306a36Sopenharmony_ci	if (!nvdev)
21162306a36Sopenharmony_ci		return 0;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	ret = rndis_filter_close(nvdev);
21462306a36Sopenharmony_ci	if (ret != 0) {
21562306a36Sopenharmony_ci		netdev_err(net, "unable to close device (ret %d).\n", ret);
21662306a36Sopenharmony_ci		return ret;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ret = netvsc_wait_until_empty(nvdev);
22062306a36Sopenharmony_ci	if (ret)
22162306a36Sopenharmony_ci		netdev_err(net, "Ring buffer not empty after closing rndis\n");
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (vf_netdev)
22462306a36Sopenharmony_ci		dev_close(vf_netdev);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return ret;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic inline void *init_ppi_data(struct rndis_message *msg,
23062306a36Sopenharmony_ci				  u32 ppi_size, u32 pkt_type)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
23362306a36Sopenharmony_ci	struct rndis_per_packet_info *ppi;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	rndis_pkt->data_offset += ppi_size;
23662306a36Sopenharmony_ci	ppi = (void *)rndis_pkt + rndis_pkt->per_pkt_info_offset
23762306a36Sopenharmony_ci		+ rndis_pkt->per_pkt_info_len;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	ppi->size = ppi_size;
24062306a36Sopenharmony_ci	ppi->type = pkt_type;
24162306a36Sopenharmony_ci	ppi->internal = 0;
24262306a36Sopenharmony_ci	ppi->ppi_offset = sizeof(struct rndis_per_packet_info);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	rndis_pkt->per_pkt_info_len += ppi_size;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return ppi + 1;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic inline int netvsc_get_tx_queue(struct net_device *ndev,
25062306a36Sopenharmony_ci				      struct sk_buff *skb, int old_idx)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	const struct net_device_context *ndc = netdev_priv(ndev);
25362306a36Sopenharmony_ci	struct sock *sk = skb->sk;
25462306a36Sopenharmony_ci	int q_idx;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	q_idx = ndc->tx_table[netvsc_get_hash(skb, ndc) &
25762306a36Sopenharmony_ci			      (VRSS_SEND_TAB_SIZE - 1)];
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* If queue index changed record the new value */
26062306a36Sopenharmony_ci	if (q_idx != old_idx &&
26162306a36Sopenharmony_ci	    sk && sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache))
26262306a36Sopenharmony_ci		sk_tx_queue_set(sk, q_idx);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return q_idx;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/*
26862306a36Sopenharmony_ci * Select queue for transmit.
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * If a valid queue has already been assigned, then use that.
27162306a36Sopenharmony_ci * Otherwise compute tx queue based on hash and the send table.
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * This is basically similar to default (netdev_pick_tx) with the added step
27462306a36Sopenharmony_ci * of using the host send_table when no other queue has been assigned.
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * TODO support XPS - but get_xps_queue not exported
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_cistatic u16 netvsc_pick_tx(struct net_device *ndev, struct sk_buff *skb)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	int q_idx = sk_tx_queue_get(skb->sk);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (q_idx < 0 || skb->ooo_okay || q_idx >= ndev->real_num_tx_queues) {
28362306a36Sopenharmony_ci		/* If forwarding a packet, we use the recorded queue when
28462306a36Sopenharmony_ci		 * available for better cache locality.
28562306a36Sopenharmony_ci		 */
28662306a36Sopenharmony_ci		if (skb_rx_queue_recorded(skb))
28762306a36Sopenharmony_ci			q_idx = skb_get_rx_queue(skb);
28862306a36Sopenharmony_ci		else
28962306a36Sopenharmony_ci			q_idx = netvsc_get_tx_queue(ndev, skb, q_idx);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return q_idx;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
29662306a36Sopenharmony_ci			       struct net_device *sb_dev)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(ndev);
29962306a36Sopenharmony_ci	struct net_device *vf_netdev;
30062306a36Sopenharmony_ci	u16 txq;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	rcu_read_lock();
30362306a36Sopenharmony_ci	vf_netdev = rcu_dereference(ndc->vf_netdev);
30462306a36Sopenharmony_ci	if (vf_netdev) {
30562306a36Sopenharmony_ci		const struct net_device_ops *vf_ops = vf_netdev->netdev_ops;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (vf_ops->ndo_select_queue)
30862306a36Sopenharmony_ci			txq = vf_ops->ndo_select_queue(vf_netdev, skb, sb_dev);
30962306a36Sopenharmony_ci		else
31062306a36Sopenharmony_ci			txq = netdev_pick_tx(vf_netdev, skb, NULL);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		/* Record the queue selected by VF so that it can be
31362306a36Sopenharmony_ci		 * used for common case where VF has more queues than
31462306a36Sopenharmony_ci		 * the synthetic device.
31562306a36Sopenharmony_ci		 */
31662306a36Sopenharmony_ci		qdisc_skb_cb(skb)->slave_dev_queue_mapping = txq;
31762306a36Sopenharmony_ci	} else {
31862306a36Sopenharmony_ci		txq = netvsc_pick_tx(ndev, skb);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	rcu_read_unlock();
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	while (txq >= ndev->real_num_tx_queues)
32362306a36Sopenharmony_ci		txq -= ndev->real_num_tx_queues;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return txq;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic u32 fill_pg_buf(unsigned long hvpfn, u32 offset, u32 len,
32962306a36Sopenharmony_ci		       struct hv_page_buffer *pb)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	int j = 0;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	hvpfn += offset >> HV_HYP_PAGE_SHIFT;
33462306a36Sopenharmony_ci	offset = offset & ~HV_HYP_PAGE_MASK;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	while (len > 0) {
33762306a36Sopenharmony_ci		unsigned long bytes;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		bytes = HV_HYP_PAGE_SIZE - offset;
34062306a36Sopenharmony_ci		if (bytes > len)
34162306a36Sopenharmony_ci			bytes = len;
34262306a36Sopenharmony_ci		pb[j].pfn = hvpfn;
34362306a36Sopenharmony_ci		pb[j].offset = offset;
34462306a36Sopenharmony_ci		pb[j].len = bytes;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		offset += bytes;
34762306a36Sopenharmony_ci		len -= bytes;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		if (offset == HV_HYP_PAGE_SIZE && len) {
35062306a36Sopenharmony_ci			hvpfn++;
35162306a36Sopenharmony_ci			offset = 0;
35262306a36Sopenharmony_ci			j++;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return j + 1;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
36062306a36Sopenharmony_ci			   struct hv_netvsc_packet *packet,
36162306a36Sopenharmony_ci			   struct hv_page_buffer *pb)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	u32 slots_used = 0;
36462306a36Sopenharmony_ci	char *data = skb->data;
36562306a36Sopenharmony_ci	int frags = skb_shinfo(skb)->nr_frags;
36662306a36Sopenharmony_ci	int i;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* The packet is laid out thus:
36962306a36Sopenharmony_ci	 * 1. hdr: RNDIS header and PPI
37062306a36Sopenharmony_ci	 * 2. skb linear data
37162306a36Sopenharmony_ci	 * 3. skb fragment data
37262306a36Sopenharmony_ci	 */
37362306a36Sopenharmony_ci	slots_used += fill_pg_buf(virt_to_hvpfn(hdr),
37462306a36Sopenharmony_ci				  offset_in_hvpage(hdr),
37562306a36Sopenharmony_ci				  len,
37662306a36Sopenharmony_ci				  &pb[slots_used]);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	packet->rmsg_size = len;
37962306a36Sopenharmony_ci	packet->rmsg_pgcnt = slots_used;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	slots_used += fill_pg_buf(virt_to_hvpfn(data),
38262306a36Sopenharmony_ci				  offset_in_hvpage(data),
38362306a36Sopenharmony_ci				  skb_headlen(skb),
38462306a36Sopenharmony_ci				  &pb[slots_used]);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	for (i = 0; i < frags; i++) {
38762306a36Sopenharmony_ci		skb_frag_t *frag = skb_shinfo(skb)->frags + i;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		slots_used += fill_pg_buf(page_to_hvpfn(skb_frag_page(frag)),
39062306a36Sopenharmony_ci					  skb_frag_off(frag),
39162306a36Sopenharmony_ci					  skb_frag_size(frag),
39262306a36Sopenharmony_ci					  &pb[slots_used]);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	return slots_used;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int count_skb_frag_slots(struct sk_buff *skb)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	int i, frags = skb_shinfo(skb)->nr_frags;
40062306a36Sopenharmony_ci	int pages = 0;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	for (i = 0; i < frags; i++) {
40362306a36Sopenharmony_ci		skb_frag_t *frag = skb_shinfo(skb)->frags + i;
40462306a36Sopenharmony_ci		unsigned long size = skb_frag_size(frag);
40562306a36Sopenharmony_ci		unsigned long offset = skb_frag_off(frag);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		/* Skip unused frames from start of page */
40862306a36Sopenharmony_ci		offset &= ~HV_HYP_PAGE_MASK;
40962306a36Sopenharmony_ci		pages += HVPFN_UP(offset + size);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci	return pages;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int netvsc_get_slots(struct sk_buff *skb)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	char *data = skb->data;
41762306a36Sopenharmony_ci	unsigned int offset = offset_in_hvpage(data);
41862306a36Sopenharmony_ci	unsigned int len = skb_headlen(skb);
41962306a36Sopenharmony_ci	int slots;
42062306a36Sopenharmony_ci	int frag_slots;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	slots = DIV_ROUND_UP(offset + len, HV_HYP_PAGE_SIZE);
42362306a36Sopenharmony_ci	frag_slots = count_skb_frag_slots(skb);
42462306a36Sopenharmony_ci	return slots + frag_slots;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic u32 net_checksum_info(struct sk_buff *skb)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP)) {
43062306a36Sopenharmony_ci		struct iphdr *ip = ip_hdr(skb);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (ip->protocol == IPPROTO_TCP)
43362306a36Sopenharmony_ci			return TRANSPORT_INFO_IPV4_TCP;
43462306a36Sopenharmony_ci		else if (ip->protocol == IPPROTO_UDP)
43562306a36Sopenharmony_ci			return TRANSPORT_INFO_IPV4_UDP;
43662306a36Sopenharmony_ci	} else {
43762306a36Sopenharmony_ci		struct ipv6hdr *ip6 = ipv6_hdr(skb);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		if (ip6->nexthdr == IPPROTO_TCP)
44062306a36Sopenharmony_ci			return TRANSPORT_INFO_IPV6_TCP;
44162306a36Sopenharmony_ci		else if (ip6->nexthdr == IPPROTO_UDP)
44262306a36Sopenharmony_ci			return TRANSPORT_INFO_IPV6_UDP;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return TRANSPORT_INFO_NOT_IP;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* Send skb on the slave VF device. */
44962306a36Sopenharmony_cistatic int netvsc_vf_xmit(struct net_device *net, struct net_device *vf_netdev,
45062306a36Sopenharmony_ci			  struct sk_buff *skb)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
45362306a36Sopenharmony_ci	unsigned int len = skb->len;
45462306a36Sopenharmony_ci	int rc;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	skb->dev = vf_netdev;
45762306a36Sopenharmony_ci	skb_record_rx_queue(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	rc = dev_queue_xmit(skb);
46062306a36Sopenharmony_ci	if (likely(rc == NET_XMIT_SUCCESS || rc == NET_XMIT_CN)) {
46162306a36Sopenharmony_ci		struct netvsc_vf_pcpu_stats *pcpu_stats
46262306a36Sopenharmony_ci			= this_cpu_ptr(ndev_ctx->vf_stats);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		u64_stats_update_begin(&pcpu_stats->syncp);
46562306a36Sopenharmony_ci		pcpu_stats->tx_packets++;
46662306a36Sopenharmony_ci		pcpu_stats->tx_bytes += len;
46762306a36Sopenharmony_ci		u64_stats_update_end(&pcpu_stats->syncp);
46862306a36Sopenharmony_ci	} else {
46962306a36Sopenharmony_ci		this_cpu_inc(ndev_ctx->vf_stats->tx_dropped);
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return rc;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int netvsc_xmit(struct sk_buff *skb, struct net_device *net, bool xdp_tx)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct net_device_context *net_device_ctx = netdev_priv(net);
47862306a36Sopenharmony_ci	struct hv_netvsc_packet *packet = NULL;
47962306a36Sopenharmony_ci	int ret;
48062306a36Sopenharmony_ci	unsigned int num_data_pgs;
48162306a36Sopenharmony_ci	struct rndis_message *rndis_msg;
48262306a36Sopenharmony_ci	struct net_device *vf_netdev;
48362306a36Sopenharmony_ci	u32 rndis_msg_size;
48462306a36Sopenharmony_ci	u32 hash;
48562306a36Sopenharmony_ci	struct hv_page_buffer pb[MAX_PAGE_BUFFER_COUNT];
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* If VF is present and up then redirect packets to it.
48862306a36Sopenharmony_ci	 * Skip the VF if it is marked down or has no carrier.
48962306a36Sopenharmony_ci	 * If netpoll is in uses, then VF can not be used either.
49062306a36Sopenharmony_ci	 */
49162306a36Sopenharmony_ci	vf_netdev = rcu_dereference_bh(net_device_ctx->vf_netdev);
49262306a36Sopenharmony_ci	if (vf_netdev && netif_running(vf_netdev) &&
49362306a36Sopenharmony_ci	    netif_carrier_ok(vf_netdev) && !netpoll_tx_running(net) &&
49462306a36Sopenharmony_ci	    net_device_ctx->data_path_is_vf)
49562306a36Sopenharmony_ci		return netvsc_vf_xmit(net, vf_netdev, skb);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* We will atmost need two pages to describe the rndis
49862306a36Sopenharmony_ci	 * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
49962306a36Sopenharmony_ci	 * of pages in a single packet. If skb is scattered around
50062306a36Sopenharmony_ci	 * more pages we try linearizing it.
50162306a36Sopenharmony_ci	 */
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	num_data_pgs = netvsc_get_slots(skb) + 2;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (unlikely(num_data_pgs > MAX_PAGE_BUFFER_COUNT)) {
50662306a36Sopenharmony_ci		++net_device_ctx->eth_stats.tx_scattered;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if (skb_linearize(skb))
50962306a36Sopenharmony_ci			goto no_memory;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		num_data_pgs = netvsc_get_slots(skb) + 2;
51262306a36Sopenharmony_ci		if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
51362306a36Sopenharmony_ci			++net_device_ctx->eth_stats.tx_too_big;
51462306a36Sopenharmony_ci			goto drop;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/*
51962306a36Sopenharmony_ci	 * Place the rndis header in the skb head room and
52062306a36Sopenharmony_ci	 * the skb->cb will be used for hv_netvsc_packet
52162306a36Sopenharmony_ci	 * structure.
52262306a36Sopenharmony_ci	 */
52362306a36Sopenharmony_ci	ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE);
52462306a36Sopenharmony_ci	if (ret)
52562306a36Sopenharmony_ci		goto no_memory;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Use the skb control buffer for building up the packet */
52862306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) >
52962306a36Sopenharmony_ci			sizeof_field(struct sk_buff, cb));
53062306a36Sopenharmony_ci	packet = (struct hv_netvsc_packet *)skb->cb;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	packet->q_idx = skb_get_queue_mapping(skb);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	packet->total_data_buflen = skb->len;
53562306a36Sopenharmony_ci	packet->total_bytes = skb->len;
53662306a36Sopenharmony_ci	packet->total_packets = 1;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rndis_msg = (struct rndis_message *)skb->head;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Add the rndis header */
54162306a36Sopenharmony_ci	rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET;
54262306a36Sopenharmony_ci	rndis_msg->msg_len = packet->total_data_buflen;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	rndis_msg->msg.pkt = (struct rndis_packet) {
54562306a36Sopenharmony_ci		.data_offset = sizeof(struct rndis_packet),
54662306a36Sopenharmony_ci		.data_len = packet->total_data_buflen,
54762306a36Sopenharmony_ci		.per_pkt_info_offset = sizeof(struct rndis_packet),
54862306a36Sopenharmony_ci	};
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	hash = skb_get_hash_raw(skb);
55362306a36Sopenharmony_ci	if (hash != 0 && net->real_num_tx_queues > 1) {
55462306a36Sopenharmony_ci		u32 *hash_info;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		rndis_msg_size += NDIS_HASH_PPI_SIZE;
55762306a36Sopenharmony_ci		hash_info = init_ppi_data(rndis_msg, NDIS_HASH_PPI_SIZE,
55862306a36Sopenharmony_ci					  NBL_HASH_VALUE);
55962306a36Sopenharmony_ci		*hash_info = hash;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* When using AF_PACKET we need to drop VLAN header from
56362306a36Sopenharmony_ci	 * the frame and update the SKB to allow the HOST OS
56462306a36Sopenharmony_ci	 * to transmit the 802.1Q packet
56562306a36Sopenharmony_ci	 */
56662306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_8021Q)) {
56762306a36Sopenharmony_ci		u16 vlan_tci;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		skb_reset_mac_header(skb);
57062306a36Sopenharmony_ci		if (eth_type_vlan(eth_hdr(skb)->h_proto)) {
57162306a36Sopenharmony_ci			if (unlikely(__skb_vlan_pop(skb, &vlan_tci) != 0)) {
57262306a36Sopenharmony_ci				++net_device_ctx->eth_stats.vlan_error;
57362306a36Sopenharmony_ci				goto drop;
57462306a36Sopenharmony_ci			}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tci);
57762306a36Sopenharmony_ci			/* Update the NDIS header pkt lengths */
57862306a36Sopenharmony_ci			packet->total_data_buflen -= VLAN_HLEN;
57962306a36Sopenharmony_ci			packet->total_bytes -= VLAN_HLEN;
58062306a36Sopenharmony_ci			rndis_msg->msg_len = packet->total_data_buflen;
58162306a36Sopenharmony_ci			rndis_msg->msg.pkt.data_len = packet->total_data_buflen;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
58662306a36Sopenharmony_ci		struct ndis_pkt_8021q_info *vlan;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		rndis_msg_size += NDIS_VLAN_PPI_SIZE;
58962306a36Sopenharmony_ci		vlan = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE,
59062306a36Sopenharmony_ci				     IEEE_8021Q_INFO);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		vlan->value = 0;
59362306a36Sopenharmony_ci		vlan->vlanid = skb_vlan_tag_get_id(skb);
59462306a36Sopenharmony_ci		vlan->cfi = skb_vlan_tag_get_cfi(skb);
59562306a36Sopenharmony_ci		vlan->pri = skb_vlan_tag_get_prio(skb);
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
59962306a36Sopenharmony_ci		struct ndis_tcp_lso_info *lso_info;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		rndis_msg_size += NDIS_LSO_PPI_SIZE;
60262306a36Sopenharmony_ci		lso_info = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE,
60362306a36Sopenharmony_ci					 TCP_LARGESEND_PKTINFO);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		lso_info->value = 0;
60662306a36Sopenharmony_ci		lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE;
60762306a36Sopenharmony_ci		if (skb->protocol == htons(ETH_P_IP)) {
60862306a36Sopenharmony_ci			lso_info->lso_v2_transmit.ip_version =
60962306a36Sopenharmony_ci				NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4;
61062306a36Sopenharmony_ci			ip_hdr(skb)->tot_len = 0;
61162306a36Sopenharmony_ci			ip_hdr(skb)->check = 0;
61262306a36Sopenharmony_ci			tcp_hdr(skb)->check =
61362306a36Sopenharmony_ci				~csum_tcpudp_magic(ip_hdr(skb)->saddr,
61462306a36Sopenharmony_ci						   ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0);
61562306a36Sopenharmony_ci		} else {
61662306a36Sopenharmony_ci			lso_info->lso_v2_transmit.ip_version =
61762306a36Sopenharmony_ci				NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6;
61862306a36Sopenharmony_ci			tcp_v6_gso_csum_prep(skb);
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci		lso_info->lso_v2_transmit.tcp_header_offset = skb_transport_offset(skb);
62162306a36Sopenharmony_ci		lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
62262306a36Sopenharmony_ci	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
62362306a36Sopenharmony_ci		if (net_checksum_info(skb) & net_device_ctx->tx_checksum_mask) {
62462306a36Sopenharmony_ci			struct ndis_tcp_ip_checksum_info *csum_info;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci			rndis_msg_size += NDIS_CSUM_PPI_SIZE;
62762306a36Sopenharmony_ci			csum_info = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
62862306a36Sopenharmony_ci						  TCPIP_CHKSUM_PKTINFO);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci			csum_info->value = 0;
63162306a36Sopenharmony_ci			csum_info->transmit.tcp_header_offset = skb_transport_offset(skb);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			if (skb->protocol == htons(ETH_P_IP)) {
63462306a36Sopenharmony_ci				csum_info->transmit.is_ipv4 = 1;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci				if (ip_hdr(skb)->protocol == IPPROTO_TCP)
63762306a36Sopenharmony_ci					csum_info->transmit.tcp_checksum = 1;
63862306a36Sopenharmony_ci				else
63962306a36Sopenharmony_ci					csum_info->transmit.udp_checksum = 1;
64062306a36Sopenharmony_ci			} else {
64162306a36Sopenharmony_ci				csum_info->transmit.is_ipv6 = 1;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci				if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP)
64462306a36Sopenharmony_ci					csum_info->transmit.tcp_checksum = 1;
64562306a36Sopenharmony_ci				else
64662306a36Sopenharmony_ci					csum_info->transmit.udp_checksum = 1;
64762306a36Sopenharmony_ci			}
64862306a36Sopenharmony_ci		} else {
64962306a36Sopenharmony_ci			/* Can't do offload of this type of checksum */
65062306a36Sopenharmony_ci			if (skb_checksum_help(skb))
65162306a36Sopenharmony_ci				goto drop;
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Start filling in the page buffers with the rndis hdr */
65662306a36Sopenharmony_ci	rndis_msg->msg_len += rndis_msg_size;
65762306a36Sopenharmony_ci	packet->total_data_buflen = rndis_msg->msg_len;
65862306a36Sopenharmony_ci	packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size,
65962306a36Sopenharmony_ci					       skb, packet, pb);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* timestamp packet in software */
66262306a36Sopenharmony_ci	skb_tx_timestamp(skb);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	ret = netvsc_send(net, packet, rndis_msg, pb, skb, xdp_tx);
66562306a36Sopenharmony_ci	if (likely(ret == 0))
66662306a36Sopenharmony_ci		return NETDEV_TX_OK;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (ret == -EAGAIN) {
66962306a36Sopenharmony_ci		++net_device_ctx->eth_stats.tx_busy;
67062306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (ret == -ENOSPC)
67462306a36Sopenharmony_ci		++net_device_ctx->eth_stats.tx_no_space;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cidrop:
67762306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
67862306a36Sopenharmony_ci	net->stats.tx_dropped++;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return NETDEV_TX_OK;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cino_memory:
68362306a36Sopenharmony_ci	++net_device_ctx->eth_stats.tx_no_memory;
68462306a36Sopenharmony_ci	goto drop;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
68862306a36Sopenharmony_ci				     struct net_device *ndev)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	return netvsc_xmit(skb, ndev, false);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/*
69462306a36Sopenharmony_ci * netvsc_linkstatus_callback - Link up/down notification
69562306a36Sopenharmony_ci */
69662306a36Sopenharmony_civoid netvsc_linkstatus_callback(struct net_device *net,
69762306a36Sopenharmony_ci				struct rndis_message *resp,
69862306a36Sopenharmony_ci				void *data, u32 data_buflen)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
70162306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
70262306a36Sopenharmony_ci	struct netvsc_reconfig *event;
70362306a36Sopenharmony_ci	unsigned long flags;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/* Ensure the packet is big enough to access its fields */
70662306a36Sopenharmony_ci	if (resp->msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_indicate_status)) {
70762306a36Sopenharmony_ci		netdev_err(net, "invalid rndis_indicate_status packet, len: %u\n",
70862306a36Sopenharmony_ci			   resp->msg_len);
70962306a36Sopenharmony_ci		return;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* Copy the RNDIS indicate status into nvchan->recv_buf */
71362306a36Sopenharmony_ci	memcpy(indicate, data + RNDIS_HEADER_SIZE, sizeof(*indicate));
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* Update the physical link speed when changing to another vSwitch */
71662306a36Sopenharmony_ci	if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
71762306a36Sopenharmony_ci		u32 speed;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		/* Validate status_buf_offset and status_buflen.
72062306a36Sopenharmony_ci		 *
72162306a36Sopenharmony_ci		 * Certain (pre-Fe) implementations of Hyper-V's vSwitch didn't account
72262306a36Sopenharmony_ci		 * for the status buffer field in resp->msg_len; perform the validation
72362306a36Sopenharmony_ci		 * using data_buflen (>= resp->msg_len).
72462306a36Sopenharmony_ci		 */
72562306a36Sopenharmony_ci		if (indicate->status_buflen < sizeof(speed) ||
72662306a36Sopenharmony_ci		    indicate->status_buf_offset < sizeof(*indicate) ||
72762306a36Sopenharmony_ci		    data_buflen - RNDIS_HEADER_SIZE < indicate->status_buf_offset ||
72862306a36Sopenharmony_ci		    data_buflen - RNDIS_HEADER_SIZE - indicate->status_buf_offset
72962306a36Sopenharmony_ci				< indicate->status_buflen) {
73062306a36Sopenharmony_ci			netdev_err(net, "invalid rndis_indicate_status packet\n");
73162306a36Sopenharmony_ci			return;
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		speed = *(u32 *)(data + RNDIS_HEADER_SIZE + indicate->status_buf_offset) / 10000;
73562306a36Sopenharmony_ci		ndev_ctx->speed = speed;
73662306a36Sopenharmony_ci		return;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* Handle these link change statuses below */
74062306a36Sopenharmony_ci	if (indicate->status != RNDIS_STATUS_NETWORK_CHANGE &&
74162306a36Sopenharmony_ci	    indicate->status != RNDIS_STATUS_MEDIA_CONNECT &&
74262306a36Sopenharmony_ci	    indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT)
74362306a36Sopenharmony_ci		return;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (net->reg_state != NETREG_REGISTERED)
74662306a36Sopenharmony_ci		return;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	event = kzalloc(sizeof(*event), GFP_ATOMIC);
74962306a36Sopenharmony_ci	if (!event)
75062306a36Sopenharmony_ci		return;
75162306a36Sopenharmony_ci	event->event = indicate->status;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	spin_lock_irqsave(&ndev_ctx->lock, flags);
75462306a36Sopenharmony_ci	list_add_tail(&event->list, &ndev_ctx->reconfig_events);
75562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ndev_ctx->lock, flags);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	schedule_delayed_work(&ndev_ctx->dwork, 0);
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci/* This function should only be called after skb_record_rx_queue() */
76162306a36Sopenharmony_civoid netvsc_xdp_xmit(struct sk_buff *skb, struct net_device *ndev)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	int rc;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	skb->queue_mapping = skb_get_rx_queue(skb);
76662306a36Sopenharmony_ci	__skb_push(skb, ETH_HLEN);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	rc = netvsc_xmit(skb, ndev, true);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (dev_xmit_complete(rc))
77162306a36Sopenharmony_ci		return;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
77462306a36Sopenharmony_ci	ndev->stats.tx_dropped++;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic void netvsc_comp_ipcsum(struct sk_buff *skb)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct iphdr *iph = (struct iphdr *)skb->data;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	iph->check = 0;
78262306a36Sopenharmony_ci	iph->check = ip_fast_csum(iph, iph->ihl);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
78662306a36Sopenharmony_ci					     struct netvsc_channel *nvchan,
78762306a36Sopenharmony_ci					     struct xdp_buff *xdp)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct napi_struct *napi = &nvchan->napi;
79062306a36Sopenharmony_ci	const struct ndis_pkt_8021q_info *vlan = &nvchan->rsc.vlan;
79162306a36Sopenharmony_ci	const struct ndis_tcp_ip_checksum_info *csum_info =
79262306a36Sopenharmony_ci						&nvchan->rsc.csum_info;
79362306a36Sopenharmony_ci	const u32 *hash_info = &nvchan->rsc.hash_info;
79462306a36Sopenharmony_ci	u8 ppi_flags = nvchan->rsc.ppi_flags;
79562306a36Sopenharmony_ci	struct sk_buff *skb;
79662306a36Sopenharmony_ci	void *xbuf = xdp->data_hard_start;
79762306a36Sopenharmony_ci	int i;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (xbuf) {
80062306a36Sopenharmony_ci		unsigned int hdroom = xdp->data - xdp->data_hard_start;
80162306a36Sopenharmony_ci		unsigned int xlen = xdp->data_end - xdp->data;
80262306a36Sopenharmony_ci		unsigned int frag_size = xdp->frame_sz;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		skb = build_skb(xbuf, frag_size);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci		if (!skb) {
80762306a36Sopenharmony_ci			__free_page(virt_to_page(xbuf));
80862306a36Sopenharmony_ci			return NULL;
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		skb_reserve(skb, hdroom);
81262306a36Sopenharmony_ci		skb_put(skb, xlen);
81362306a36Sopenharmony_ci		skb->dev = napi->dev;
81462306a36Sopenharmony_ci	} else {
81562306a36Sopenharmony_ci		skb = napi_alloc_skb(napi, nvchan->rsc.pktlen);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		if (!skb)
81862306a36Sopenharmony_ci			return NULL;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		/* Copy to skb. This copy is needed here since the memory
82162306a36Sopenharmony_ci		 * pointed by hv_netvsc_packet cannot be deallocated.
82262306a36Sopenharmony_ci		 */
82362306a36Sopenharmony_ci		for (i = 0; i < nvchan->rsc.cnt; i++)
82462306a36Sopenharmony_ci			skb_put_data(skb, nvchan->rsc.data[i],
82562306a36Sopenharmony_ci				     nvchan->rsc.len[i]);
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, net);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* skb is already created with CHECKSUM_NONE */
83162306a36Sopenharmony_ci	skb_checksum_none_assert(skb);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* Incoming packets may have IP header checksum verified by the host.
83462306a36Sopenharmony_ci	 * They may not have IP header checksum computed after coalescing.
83562306a36Sopenharmony_ci	 * We compute it here if the flags are set, because on Linux, the IP
83662306a36Sopenharmony_ci	 * checksum is always checked.
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	if ((ppi_flags & NVSC_RSC_CSUM_INFO) && csum_info->receive.ip_checksum_value_invalid &&
83962306a36Sopenharmony_ci	    csum_info->receive.ip_checksum_succeeded &&
84062306a36Sopenharmony_ci	    skb->protocol == htons(ETH_P_IP)) {
84162306a36Sopenharmony_ci		/* Check that there is enough space to hold the IP header. */
84262306a36Sopenharmony_ci		if (skb_headlen(skb) < sizeof(struct iphdr)) {
84362306a36Sopenharmony_ci			kfree_skb(skb);
84462306a36Sopenharmony_ci			return NULL;
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci		netvsc_comp_ipcsum(skb);
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/* Do L4 checksum offload if enabled and present. */
85062306a36Sopenharmony_ci	if ((ppi_flags & NVSC_RSC_CSUM_INFO) && (net->features & NETIF_F_RXCSUM)) {
85162306a36Sopenharmony_ci		if (csum_info->receive.tcp_checksum_succeeded ||
85262306a36Sopenharmony_ci		    csum_info->receive.udp_checksum_succeeded)
85362306a36Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if ((ppi_flags & NVSC_RSC_HASH_INFO) && (net->features & NETIF_F_RXHASH))
85762306a36Sopenharmony_ci		skb_set_hash(skb, *hash_info, PKT_HASH_TYPE_L4);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (ppi_flags & NVSC_RSC_VLAN) {
86062306a36Sopenharmony_ci		u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
86162306a36Sopenharmony_ci			(vlan->cfi ? VLAN_CFI_MASK : 0);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
86462306a36Sopenharmony_ci				       vlan_tci);
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return skb;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci/*
87162306a36Sopenharmony_ci * netvsc_recv_callback -  Callback when we receive a packet from the
87262306a36Sopenharmony_ci * "wire" on the specified device.
87362306a36Sopenharmony_ci */
87462306a36Sopenharmony_ciint netvsc_recv_callback(struct net_device *net,
87562306a36Sopenharmony_ci			 struct netvsc_device *net_device,
87662306a36Sopenharmony_ci			 struct netvsc_channel *nvchan)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	struct net_device_context *net_device_ctx = netdev_priv(net);
87962306a36Sopenharmony_ci	struct vmbus_channel *channel = nvchan->channel;
88062306a36Sopenharmony_ci	u16 q_idx = channel->offermsg.offer.sub_channel_index;
88162306a36Sopenharmony_ci	struct sk_buff *skb;
88262306a36Sopenharmony_ci	struct netvsc_stats_rx *rx_stats = &nvchan->rx_stats;
88362306a36Sopenharmony_ci	struct xdp_buff xdp;
88462306a36Sopenharmony_ci	u32 act;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (net->reg_state != NETREG_REGISTERED)
88762306a36Sopenharmony_ci		return NVSP_STAT_FAIL;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	act = netvsc_run_xdp(net, nvchan, &xdp);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (act == XDP_REDIRECT)
89262306a36Sopenharmony_ci		return NVSP_STAT_SUCCESS;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (act != XDP_PASS && act != XDP_TX) {
89562306a36Sopenharmony_ci		u64_stats_update_begin(&rx_stats->syncp);
89662306a36Sopenharmony_ci		rx_stats->xdp_drop++;
89762306a36Sopenharmony_ci		u64_stats_update_end(&rx_stats->syncp);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		return NVSP_STAT_SUCCESS; /* consumed by XDP */
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* Allocate a skb - TODO direct I/O to pages? */
90362306a36Sopenharmony_ci	skb = netvsc_alloc_recv_skb(net, nvchan, &xdp);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (unlikely(!skb)) {
90662306a36Sopenharmony_ci		++net_device_ctx->eth_stats.rx_no_memory;
90762306a36Sopenharmony_ci		return NVSP_STAT_FAIL;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	skb_record_rx_queue(skb, q_idx);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/*
91362306a36Sopenharmony_ci	 * Even if injecting the packet, record the statistics
91462306a36Sopenharmony_ci	 * on the synthetic device because modifying the VF device
91562306a36Sopenharmony_ci	 * statistics will not work correctly.
91662306a36Sopenharmony_ci	 */
91762306a36Sopenharmony_ci	u64_stats_update_begin(&rx_stats->syncp);
91862306a36Sopenharmony_ci	if (act == XDP_TX)
91962306a36Sopenharmony_ci		rx_stats->xdp_tx++;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	rx_stats->packets++;
92262306a36Sopenharmony_ci	rx_stats->bytes += nvchan->rsc.pktlen;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (skb->pkt_type == PACKET_BROADCAST)
92562306a36Sopenharmony_ci		++rx_stats->broadcast;
92662306a36Sopenharmony_ci	else if (skb->pkt_type == PACKET_MULTICAST)
92762306a36Sopenharmony_ci		++rx_stats->multicast;
92862306a36Sopenharmony_ci	u64_stats_update_end(&rx_stats->syncp);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (act == XDP_TX) {
93162306a36Sopenharmony_ci		netvsc_xdp_xmit(skb, net);
93262306a36Sopenharmony_ci		return NVSP_STAT_SUCCESS;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	napi_gro_receive(&nvchan->napi, skb);
93662306a36Sopenharmony_ci	return NVSP_STAT_SUCCESS;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic void netvsc_get_drvinfo(struct net_device *net,
94062306a36Sopenharmony_ci			       struct ethtool_drvinfo *info)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
94362306a36Sopenharmony_ci	strscpy(info->fw_version, "N/A", sizeof(info->fw_version));
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic void netvsc_get_channels(struct net_device *net,
94762306a36Sopenharmony_ci				struct ethtool_channels *channel)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct net_device_context *net_device_ctx = netdev_priv(net);
95062306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (nvdev) {
95362306a36Sopenharmony_ci		channel->max_combined	= nvdev->max_chn;
95462306a36Sopenharmony_ci		channel->combined_count = nvdev->num_chn;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/* Alloc struct netvsc_device_info, and initialize it from either existing
95962306a36Sopenharmony_ci * struct netvsc_device, or from default values.
96062306a36Sopenharmony_ci */
96162306a36Sopenharmony_cistatic
96262306a36Sopenharmony_cistruct netvsc_device_info *netvsc_devinfo_get(struct netvsc_device *nvdev)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct netvsc_device_info *dev_info;
96562306a36Sopenharmony_ci	struct bpf_prog *prog;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	dev_info = kzalloc(sizeof(*dev_info), GFP_ATOMIC);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (!dev_info)
97062306a36Sopenharmony_ci		return NULL;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (nvdev) {
97362306a36Sopenharmony_ci		ASSERT_RTNL();
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		dev_info->num_chn = nvdev->num_chn;
97662306a36Sopenharmony_ci		dev_info->send_sections = nvdev->send_section_cnt;
97762306a36Sopenharmony_ci		dev_info->send_section_size = nvdev->send_section_size;
97862306a36Sopenharmony_ci		dev_info->recv_sections = nvdev->recv_section_cnt;
97962306a36Sopenharmony_ci		dev_info->recv_section_size = nvdev->recv_section_size;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		memcpy(dev_info->rss_key, nvdev->extension->rss_key,
98262306a36Sopenharmony_ci		       NETVSC_HASH_KEYLEN);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		prog = netvsc_xdp_get(nvdev);
98562306a36Sopenharmony_ci		if (prog) {
98662306a36Sopenharmony_ci			bpf_prog_inc(prog);
98762306a36Sopenharmony_ci			dev_info->bprog = prog;
98862306a36Sopenharmony_ci		}
98962306a36Sopenharmony_ci	} else {
99062306a36Sopenharmony_ci		dev_info->num_chn = VRSS_CHANNEL_DEFAULT;
99162306a36Sopenharmony_ci		dev_info->send_sections = NETVSC_DEFAULT_TX;
99262306a36Sopenharmony_ci		dev_info->send_section_size = NETVSC_SEND_SECTION_SIZE;
99362306a36Sopenharmony_ci		dev_info->recv_sections = NETVSC_DEFAULT_RX;
99462306a36Sopenharmony_ci		dev_info->recv_section_size = NETVSC_RECV_SECTION_SIZE;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	return dev_info;
99862306a36Sopenharmony_ci}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci/* Free struct netvsc_device_info */
100162306a36Sopenharmony_cistatic void netvsc_devinfo_put(struct netvsc_device_info *dev_info)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	if (dev_info->bprog) {
100462306a36Sopenharmony_ci		ASSERT_RTNL();
100562306a36Sopenharmony_ci		bpf_prog_put(dev_info->bprog);
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	kfree(dev_info);
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int netvsc_detach(struct net_device *ndev,
101262306a36Sopenharmony_ci			 struct netvsc_device *nvdev)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
101562306a36Sopenharmony_ci	struct hv_device *hdev = ndev_ctx->device_ctx;
101662306a36Sopenharmony_ci	int ret;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* Don't try continuing to try and setup sub channels */
101962306a36Sopenharmony_ci	if (cancel_work_sync(&nvdev->subchan_work))
102062306a36Sopenharmony_ci		nvdev->num_chn = 1;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	netvsc_xdp_set(ndev, NULL, NULL, nvdev);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	/* If device was up (receiving) then shutdown */
102562306a36Sopenharmony_ci	if (netif_running(ndev)) {
102662306a36Sopenharmony_ci		netvsc_tx_disable(nvdev, ndev);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		ret = rndis_filter_close(nvdev);
102962306a36Sopenharmony_ci		if (ret) {
103062306a36Sopenharmony_ci			netdev_err(ndev,
103162306a36Sopenharmony_ci				   "unable to close device (ret %d).\n", ret);
103262306a36Sopenharmony_ci			return ret;
103362306a36Sopenharmony_ci		}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci		ret = netvsc_wait_until_empty(nvdev);
103662306a36Sopenharmony_ci		if (ret) {
103762306a36Sopenharmony_ci			netdev_err(ndev,
103862306a36Sopenharmony_ci				   "Ring buffer not empty after closing rndis\n");
103962306a36Sopenharmony_ci			return ret;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	netif_device_detach(ndev);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	rndis_filter_device_remove(hdev, nvdev);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	return 0;
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic int netvsc_attach(struct net_device *ndev,
105162306a36Sopenharmony_ci			 struct netvsc_device_info *dev_info)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
105462306a36Sopenharmony_ci	struct hv_device *hdev = ndev_ctx->device_ctx;
105562306a36Sopenharmony_ci	struct netvsc_device *nvdev;
105662306a36Sopenharmony_ci	struct rndis_device *rdev;
105762306a36Sopenharmony_ci	struct bpf_prog *prog;
105862306a36Sopenharmony_ci	int ret = 0;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	nvdev = rndis_filter_device_add(hdev, dev_info);
106162306a36Sopenharmony_ci	if (IS_ERR(nvdev))
106262306a36Sopenharmony_ci		return PTR_ERR(nvdev);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (nvdev->num_chn > 1) {
106562306a36Sopenharmony_ci		ret = rndis_set_subchannel(ndev, nvdev, dev_info);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		/* if unavailable, just proceed with one queue */
106862306a36Sopenharmony_ci		if (ret) {
106962306a36Sopenharmony_ci			nvdev->max_chn = 1;
107062306a36Sopenharmony_ci			nvdev->num_chn = 1;
107162306a36Sopenharmony_ci		}
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	prog = dev_info->bprog;
107562306a36Sopenharmony_ci	if (prog) {
107662306a36Sopenharmony_ci		bpf_prog_inc(prog);
107762306a36Sopenharmony_ci		ret = netvsc_xdp_set(ndev, prog, NULL, nvdev);
107862306a36Sopenharmony_ci		if (ret) {
107962306a36Sopenharmony_ci			bpf_prog_put(prog);
108062306a36Sopenharmony_ci			goto err1;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* In any case device is now ready */
108562306a36Sopenharmony_ci	nvdev->tx_disable = false;
108662306a36Sopenharmony_ci	netif_device_attach(ndev);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/* Note: enable and attach happen when sub-channels setup */
108962306a36Sopenharmony_ci	netif_carrier_off(ndev);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (netif_running(ndev)) {
109262306a36Sopenharmony_ci		ret = rndis_filter_open(nvdev);
109362306a36Sopenharmony_ci		if (ret)
109462306a36Sopenharmony_ci			goto err2;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci		rdev = nvdev->extension;
109762306a36Sopenharmony_ci		if (!rdev->link_state)
109862306a36Sopenharmony_ci			netif_carrier_on(ndev);
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	return 0;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cierr2:
110462306a36Sopenharmony_ci	netif_device_detach(ndev);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cierr1:
110762306a36Sopenharmony_ci	rndis_filter_device_remove(hdev, nvdev);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return ret;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic int netvsc_set_channels(struct net_device *net,
111362306a36Sopenharmony_ci			       struct ethtool_channels *channels)
111462306a36Sopenharmony_ci{
111562306a36Sopenharmony_ci	struct net_device_context *net_device_ctx = netdev_priv(net);
111662306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
111762306a36Sopenharmony_ci	unsigned int orig, count = channels->combined_count;
111862306a36Sopenharmony_ci	struct netvsc_device_info *device_info;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	/* We do not support separate count for rx, tx, or other */
112262306a36Sopenharmony_ci	if (count == 0 ||
112362306a36Sopenharmony_ci	    channels->rx_count || channels->tx_count || channels->other_count)
112462306a36Sopenharmony_ci		return -EINVAL;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy)
112762306a36Sopenharmony_ci		return -ENODEV;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5)
113062306a36Sopenharmony_ci		return -EINVAL;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (count > nvdev->max_chn)
113362306a36Sopenharmony_ci		return -EINVAL;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	orig = nvdev->num_chn;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	device_info = netvsc_devinfo_get(nvdev);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (!device_info)
114062306a36Sopenharmony_ci		return -ENOMEM;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	device_info->num_chn = count;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	ret = netvsc_detach(net, nvdev);
114562306a36Sopenharmony_ci	if (ret)
114662306a36Sopenharmony_ci		goto out;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	ret = netvsc_attach(net, device_info);
114962306a36Sopenharmony_ci	if (ret) {
115062306a36Sopenharmony_ci		device_info->num_chn = orig;
115162306a36Sopenharmony_ci		if (netvsc_attach(net, device_info))
115262306a36Sopenharmony_ci			netdev_err(net, "restoring channel setting failed\n");
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ciout:
115662306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
115762306a36Sopenharmony_ci	return ret;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic void netvsc_init_settings(struct net_device *dev)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	ndc->l4_hash = HV_DEFAULT_L4HASH;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ndc->speed = SPEED_UNKNOWN;
116762306a36Sopenharmony_ci	ndc->duplex = DUPLEX_FULL;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	dev->features = NETIF_F_LRO;
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int netvsc_get_link_ksettings(struct net_device *dev,
117362306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
117662306a36Sopenharmony_ci	struct net_device *vf_netdev;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	vf_netdev = rtnl_dereference(ndc->vf_netdev);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if (vf_netdev)
118162306a36Sopenharmony_ci		return __ethtool_get_link_ksettings(vf_netdev, cmd);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	cmd->base.speed = ndc->speed;
118462306a36Sopenharmony_ci	cmd->base.duplex = ndc->duplex;
118562306a36Sopenharmony_ci	cmd->base.port = PORT_OTHER;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	return 0;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_cistatic int netvsc_set_link_ksettings(struct net_device *dev,
119162306a36Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
119462306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndc->vf_netdev);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (vf_netdev) {
119762306a36Sopenharmony_ci		if (!vf_netdev->ethtool_ops->set_link_ksettings)
119862306a36Sopenharmony_ci			return -EOPNOTSUPP;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		return vf_netdev->ethtool_ops->set_link_ksettings(vf_netdev,
120162306a36Sopenharmony_ci								  cmd);
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return ethtool_virtdev_set_link_ksettings(dev, cmd,
120562306a36Sopenharmony_ci						  &ndc->speed, &ndc->duplex);
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic int netvsc_change_mtu(struct net_device *ndev, int mtu)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(ndev);
121162306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev);
121262306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
121362306a36Sopenharmony_ci	int orig_mtu = ndev->mtu;
121462306a36Sopenharmony_ci	struct netvsc_device_info *device_info;
121562306a36Sopenharmony_ci	int ret = 0;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy)
121862306a36Sopenharmony_ci		return -ENODEV;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	device_info = netvsc_devinfo_get(nvdev);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (!device_info)
122362306a36Sopenharmony_ci		return -ENOMEM;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* Change MTU of underlying VF netdev first. */
122662306a36Sopenharmony_ci	if (vf_netdev) {
122762306a36Sopenharmony_ci		ret = dev_set_mtu(vf_netdev, mtu);
122862306a36Sopenharmony_ci		if (ret)
122962306a36Sopenharmony_ci			goto out;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	ret = netvsc_detach(ndev, nvdev);
123362306a36Sopenharmony_ci	if (ret)
123462306a36Sopenharmony_ci		goto rollback_vf;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	ndev->mtu = mtu;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	ret = netvsc_attach(ndev, device_info);
123962306a36Sopenharmony_ci	if (!ret)
124062306a36Sopenharmony_ci		goto out;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* Attempt rollback to original MTU */
124362306a36Sopenharmony_ci	ndev->mtu = orig_mtu;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	if (netvsc_attach(ndev, device_info))
124662306a36Sopenharmony_ci		netdev_err(ndev, "restoring mtu failed\n");
124762306a36Sopenharmony_cirollback_vf:
124862306a36Sopenharmony_ci	if (vf_netdev)
124962306a36Sopenharmony_ci		dev_set_mtu(vf_netdev, orig_mtu);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ciout:
125262306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
125362306a36Sopenharmony_ci	return ret;
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_cistatic void netvsc_get_vf_stats(struct net_device *net,
125762306a36Sopenharmony_ci				struct netvsc_vf_pcpu_stats *tot)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
126062306a36Sopenharmony_ci	int i;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	memset(tot, 0, sizeof(*tot));
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	for_each_possible_cpu(i) {
126562306a36Sopenharmony_ci		const struct netvsc_vf_pcpu_stats *stats
126662306a36Sopenharmony_ci			= per_cpu_ptr(ndev_ctx->vf_stats, i);
126762306a36Sopenharmony_ci		u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
126862306a36Sopenharmony_ci		unsigned int start;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		do {
127162306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&stats->syncp);
127262306a36Sopenharmony_ci			rx_packets = stats->rx_packets;
127362306a36Sopenharmony_ci			tx_packets = stats->tx_packets;
127462306a36Sopenharmony_ci			rx_bytes = stats->rx_bytes;
127562306a36Sopenharmony_ci			tx_bytes = stats->tx_bytes;
127662306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&stats->syncp, start));
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci		tot->rx_packets += rx_packets;
127962306a36Sopenharmony_ci		tot->tx_packets += tx_packets;
128062306a36Sopenharmony_ci		tot->rx_bytes   += rx_bytes;
128162306a36Sopenharmony_ci		tot->tx_bytes   += tx_bytes;
128262306a36Sopenharmony_ci		tot->tx_dropped += stats->tx_dropped;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic void netvsc_get_pcpu_stats(struct net_device *net,
128762306a36Sopenharmony_ci				  struct netvsc_ethtool_pcpu_stats *pcpu_tot)
128862306a36Sopenharmony_ci{
128962306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
129062306a36Sopenharmony_ci	struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev);
129162306a36Sopenharmony_ci	int i;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* fetch percpu stats of vf */
129462306a36Sopenharmony_ci	for_each_possible_cpu(i) {
129562306a36Sopenharmony_ci		const struct netvsc_vf_pcpu_stats *stats =
129662306a36Sopenharmony_ci			per_cpu_ptr(ndev_ctx->vf_stats, i);
129762306a36Sopenharmony_ci		struct netvsc_ethtool_pcpu_stats *this_tot = &pcpu_tot[i];
129862306a36Sopenharmony_ci		unsigned int start;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		do {
130162306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&stats->syncp);
130262306a36Sopenharmony_ci			this_tot->vf_rx_packets = stats->rx_packets;
130362306a36Sopenharmony_ci			this_tot->vf_tx_packets = stats->tx_packets;
130462306a36Sopenharmony_ci			this_tot->vf_rx_bytes = stats->rx_bytes;
130562306a36Sopenharmony_ci			this_tot->vf_tx_bytes = stats->tx_bytes;
130662306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&stats->syncp, start));
130762306a36Sopenharmony_ci		this_tot->rx_packets = this_tot->vf_rx_packets;
130862306a36Sopenharmony_ci		this_tot->tx_packets = this_tot->vf_tx_packets;
130962306a36Sopenharmony_ci		this_tot->rx_bytes   = this_tot->vf_rx_bytes;
131062306a36Sopenharmony_ci		this_tot->tx_bytes   = this_tot->vf_tx_bytes;
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	/* fetch percpu stats of netvsc */
131462306a36Sopenharmony_ci	for (i = 0; i < nvdev->num_chn; i++) {
131562306a36Sopenharmony_ci		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
131662306a36Sopenharmony_ci		const struct netvsc_stats_tx *tx_stats;
131762306a36Sopenharmony_ci		const struct netvsc_stats_rx *rx_stats;
131862306a36Sopenharmony_ci		struct netvsc_ethtool_pcpu_stats *this_tot =
131962306a36Sopenharmony_ci			&pcpu_tot[nvchan->channel->target_cpu];
132062306a36Sopenharmony_ci		u64 packets, bytes;
132162306a36Sopenharmony_ci		unsigned int start;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		tx_stats = &nvchan->tx_stats;
132462306a36Sopenharmony_ci		do {
132562306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&tx_stats->syncp);
132662306a36Sopenharmony_ci			packets = tx_stats->packets;
132762306a36Sopenharmony_ci			bytes = tx_stats->bytes;
132862306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&tx_stats->syncp, start));
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		this_tot->tx_bytes	+= bytes;
133162306a36Sopenharmony_ci		this_tot->tx_packets	+= packets;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		rx_stats = &nvchan->rx_stats;
133462306a36Sopenharmony_ci		do {
133562306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&rx_stats->syncp);
133662306a36Sopenharmony_ci			packets = rx_stats->packets;
133762306a36Sopenharmony_ci			bytes = rx_stats->bytes;
133862306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&rx_stats->syncp, start));
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		this_tot->rx_bytes	+= bytes;
134162306a36Sopenharmony_ci		this_tot->rx_packets	+= packets;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic void netvsc_get_stats64(struct net_device *net,
134662306a36Sopenharmony_ci			       struct rtnl_link_stats64 *t)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(net);
134962306a36Sopenharmony_ci	struct netvsc_device *nvdev;
135062306a36Sopenharmony_ci	struct netvsc_vf_pcpu_stats vf_tot;
135162306a36Sopenharmony_ci	int i;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	rcu_read_lock();
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	nvdev = rcu_dereference(ndev_ctx->nvdev);
135662306a36Sopenharmony_ci	if (!nvdev)
135762306a36Sopenharmony_ci		goto out;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	netdev_stats_to_stats64(t, &net->stats);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	netvsc_get_vf_stats(net, &vf_tot);
136262306a36Sopenharmony_ci	t->rx_packets += vf_tot.rx_packets;
136362306a36Sopenharmony_ci	t->tx_packets += vf_tot.tx_packets;
136462306a36Sopenharmony_ci	t->rx_bytes   += vf_tot.rx_bytes;
136562306a36Sopenharmony_ci	t->tx_bytes   += vf_tot.tx_bytes;
136662306a36Sopenharmony_ci	t->tx_dropped += vf_tot.tx_dropped;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	for (i = 0; i < nvdev->num_chn; i++) {
136962306a36Sopenharmony_ci		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
137062306a36Sopenharmony_ci		const struct netvsc_stats_tx *tx_stats;
137162306a36Sopenharmony_ci		const struct netvsc_stats_rx *rx_stats;
137262306a36Sopenharmony_ci		u64 packets, bytes, multicast;
137362306a36Sopenharmony_ci		unsigned int start;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		tx_stats = &nvchan->tx_stats;
137662306a36Sopenharmony_ci		do {
137762306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&tx_stats->syncp);
137862306a36Sopenharmony_ci			packets = tx_stats->packets;
137962306a36Sopenharmony_ci			bytes = tx_stats->bytes;
138062306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&tx_stats->syncp, start));
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		t->tx_bytes	+= bytes;
138362306a36Sopenharmony_ci		t->tx_packets	+= packets;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci		rx_stats = &nvchan->rx_stats;
138662306a36Sopenharmony_ci		do {
138762306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&rx_stats->syncp);
138862306a36Sopenharmony_ci			packets = rx_stats->packets;
138962306a36Sopenharmony_ci			bytes = rx_stats->bytes;
139062306a36Sopenharmony_ci			multicast = rx_stats->multicast + rx_stats->broadcast;
139162306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&rx_stats->syncp, start));
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci		t->rx_bytes	+= bytes;
139462306a36Sopenharmony_ci		t->rx_packets	+= packets;
139562306a36Sopenharmony_ci		t->multicast	+= multicast;
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ciout:
139862306a36Sopenharmony_ci	rcu_read_unlock();
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic int netvsc_set_mac_addr(struct net_device *ndev, void *p)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(ndev);
140462306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndc->vf_netdev);
140562306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
140662306a36Sopenharmony_ci	struct sockaddr *addr = p;
140762306a36Sopenharmony_ci	int err;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	err = eth_prepare_mac_addr_change(ndev, p);
141062306a36Sopenharmony_ci	if (err)
141162306a36Sopenharmony_ci		return err;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	if (!nvdev)
141462306a36Sopenharmony_ci		return -ENODEV;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	if (vf_netdev) {
141762306a36Sopenharmony_ci		err = dev_set_mac_address(vf_netdev, addr, NULL);
141862306a36Sopenharmony_ci		if (err)
141962306a36Sopenharmony_ci			return err;
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	err = rndis_filter_set_device_mac(nvdev, addr->sa_data);
142362306a36Sopenharmony_ci	if (!err) {
142462306a36Sopenharmony_ci		eth_commit_mac_addr_change(ndev, p);
142562306a36Sopenharmony_ci	} else if (vf_netdev) {
142662306a36Sopenharmony_ci		/* rollback change on VF */
142762306a36Sopenharmony_ci		memcpy(addr->sa_data, ndev->dev_addr, ETH_ALEN);
142862306a36Sopenharmony_ci		dev_set_mac_address(vf_netdev, addr, NULL);
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return err;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic const struct {
143562306a36Sopenharmony_ci	char name[ETH_GSTRING_LEN];
143662306a36Sopenharmony_ci	u16 offset;
143762306a36Sopenharmony_ci} netvsc_stats[] = {
143862306a36Sopenharmony_ci	{ "tx_scattered", offsetof(struct netvsc_ethtool_stats, tx_scattered) },
143962306a36Sopenharmony_ci	{ "tx_no_memory", offsetof(struct netvsc_ethtool_stats, tx_no_memory) },
144062306a36Sopenharmony_ci	{ "tx_no_space",  offsetof(struct netvsc_ethtool_stats, tx_no_space) },
144162306a36Sopenharmony_ci	{ "tx_too_big",	  offsetof(struct netvsc_ethtool_stats, tx_too_big) },
144262306a36Sopenharmony_ci	{ "tx_busy",	  offsetof(struct netvsc_ethtool_stats, tx_busy) },
144362306a36Sopenharmony_ci	{ "tx_send_full", offsetof(struct netvsc_ethtool_stats, tx_send_full) },
144462306a36Sopenharmony_ci	{ "rx_comp_busy", offsetof(struct netvsc_ethtool_stats, rx_comp_busy) },
144562306a36Sopenharmony_ci	{ "rx_no_memory", offsetof(struct netvsc_ethtool_stats, rx_no_memory) },
144662306a36Sopenharmony_ci	{ "stop_queue", offsetof(struct netvsc_ethtool_stats, stop_queue) },
144762306a36Sopenharmony_ci	{ "wake_queue", offsetof(struct netvsc_ethtool_stats, wake_queue) },
144862306a36Sopenharmony_ci	{ "vlan_error", offsetof(struct netvsc_ethtool_stats, vlan_error) },
144962306a36Sopenharmony_ci}, pcpu_stats[] = {
145062306a36Sopenharmony_ci	{ "cpu%u_rx_packets",
145162306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, rx_packets) },
145262306a36Sopenharmony_ci	{ "cpu%u_rx_bytes",
145362306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, rx_bytes) },
145462306a36Sopenharmony_ci	{ "cpu%u_tx_packets",
145562306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, tx_packets) },
145662306a36Sopenharmony_ci	{ "cpu%u_tx_bytes",
145762306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, tx_bytes) },
145862306a36Sopenharmony_ci	{ "cpu%u_vf_rx_packets",
145962306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_packets) },
146062306a36Sopenharmony_ci	{ "cpu%u_vf_rx_bytes",
146162306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, vf_rx_bytes) },
146262306a36Sopenharmony_ci	{ "cpu%u_vf_tx_packets",
146362306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_packets) },
146462306a36Sopenharmony_ci	{ "cpu%u_vf_tx_bytes",
146562306a36Sopenharmony_ci		offsetof(struct netvsc_ethtool_pcpu_stats, vf_tx_bytes) },
146662306a36Sopenharmony_ci}, vf_stats[] = {
146762306a36Sopenharmony_ci	{ "vf_rx_packets", offsetof(struct netvsc_vf_pcpu_stats, rx_packets) },
146862306a36Sopenharmony_ci	{ "vf_rx_bytes",   offsetof(struct netvsc_vf_pcpu_stats, rx_bytes) },
146962306a36Sopenharmony_ci	{ "vf_tx_packets", offsetof(struct netvsc_vf_pcpu_stats, tx_packets) },
147062306a36Sopenharmony_ci	{ "vf_tx_bytes",   offsetof(struct netvsc_vf_pcpu_stats, tx_bytes) },
147162306a36Sopenharmony_ci	{ "vf_tx_dropped", offsetof(struct netvsc_vf_pcpu_stats, tx_dropped) },
147262306a36Sopenharmony_ci};
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci#define NETVSC_GLOBAL_STATS_LEN	ARRAY_SIZE(netvsc_stats)
147562306a36Sopenharmony_ci#define NETVSC_VF_STATS_LEN	ARRAY_SIZE(vf_stats)
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci/* statistics per queue (rx/tx packets/bytes) */
147862306a36Sopenharmony_ci#define NETVSC_PCPU_STATS_LEN (num_present_cpus() * ARRAY_SIZE(pcpu_stats))
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci/* 8 statistics per queue (rx/tx packets/bytes, XDP actions) */
148162306a36Sopenharmony_ci#define NETVSC_QUEUE_STATS_LEN(dev) ((dev)->num_chn * 8)
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic int netvsc_get_sset_count(struct net_device *dev, int string_set)
148462306a36Sopenharmony_ci{
148562306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
148662306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (!nvdev)
148962306a36Sopenharmony_ci		return -ENODEV;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	switch (string_set) {
149262306a36Sopenharmony_ci	case ETH_SS_STATS:
149362306a36Sopenharmony_ci		return NETVSC_GLOBAL_STATS_LEN
149462306a36Sopenharmony_ci			+ NETVSC_VF_STATS_LEN
149562306a36Sopenharmony_ci			+ NETVSC_QUEUE_STATS_LEN(nvdev)
149662306a36Sopenharmony_ci			+ NETVSC_PCPU_STATS_LEN;
149762306a36Sopenharmony_ci	default:
149862306a36Sopenharmony_ci		return -EINVAL;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic void netvsc_get_ethtool_stats(struct net_device *dev,
150362306a36Sopenharmony_ci				     struct ethtool_stats *stats, u64 *data)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
150662306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
150762306a36Sopenharmony_ci	const void *nds = &ndc->eth_stats;
150862306a36Sopenharmony_ci	const struct netvsc_stats_tx *tx_stats;
150962306a36Sopenharmony_ci	const struct netvsc_stats_rx *rx_stats;
151062306a36Sopenharmony_ci	struct netvsc_vf_pcpu_stats sum;
151162306a36Sopenharmony_ci	struct netvsc_ethtool_pcpu_stats *pcpu_sum;
151262306a36Sopenharmony_ci	unsigned int start;
151362306a36Sopenharmony_ci	u64 packets, bytes;
151462306a36Sopenharmony_ci	u64 xdp_drop;
151562306a36Sopenharmony_ci	u64 xdp_redirect;
151662306a36Sopenharmony_ci	u64 xdp_tx;
151762306a36Sopenharmony_ci	u64 xdp_xmit;
151862306a36Sopenharmony_ci	int i, j, cpu;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (!nvdev)
152162306a36Sopenharmony_ci		return;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	for (i = 0; i < NETVSC_GLOBAL_STATS_LEN; i++)
152462306a36Sopenharmony_ci		data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	netvsc_get_vf_stats(dev, &sum);
152762306a36Sopenharmony_ci	for (j = 0; j < NETVSC_VF_STATS_LEN; j++)
152862306a36Sopenharmony_ci		data[i++] = *(u64 *)((void *)&sum + vf_stats[j].offset);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	for (j = 0; j < nvdev->num_chn; j++) {
153162306a36Sopenharmony_ci		tx_stats = &nvdev->chan_table[j].tx_stats;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci		do {
153462306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&tx_stats->syncp);
153562306a36Sopenharmony_ci			packets = tx_stats->packets;
153662306a36Sopenharmony_ci			bytes = tx_stats->bytes;
153762306a36Sopenharmony_ci			xdp_xmit = tx_stats->xdp_xmit;
153862306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&tx_stats->syncp, start));
153962306a36Sopenharmony_ci		data[i++] = packets;
154062306a36Sopenharmony_ci		data[i++] = bytes;
154162306a36Sopenharmony_ci		data[i++] = xdp_xmit;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci		rx_stats = &nvdev->chan_table[j].rx_stats;
154462306a36Sopenharmony_ci		do {
154562306a36Sopenharmony_ci			start = u64_stats_fetch_begin(&rx_stats->syncp);
154662306a36Sopenharmony_ci			packets = rx_stats->packets;
154762306a36Sopenharmony_ci			bytes = rx_stats->bytes;
154862306a36Sopenharmony_ci			xdp_drop = rx_stats->xdp_drop;
154962306a36Sopenharmony_ci			xdp_redirect = rx_stats->xdp_redirect;
155062306a36Sopenharmony_ci			xdp_tx = rx_stats->xdp_tx;
155162306a36Sopenharmony_ci		} while (u64_stats_fetch_retry(&rx_stats->syncp, start));
155262306a36Sopenharmony_ci		data[i++] = packets;
155362306a36Sopenharmony_ci		data[i++] = bytes;
155462306a36Sopenharmony_ci		data[i++] = xdp_drop;
155562306a36Sopenharmony_ci		data[i++] = xdp_redirect;
155662306a36Sopenharmony_ci		data[i++] = xdp_tx;
155762306a36Sopenharmony_ci	}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	pcpu_sum = kvmalloc_array(num_possible_cpus(),
156062306a36Sopenharmony_ci				  sizeof(struct netvsc_ethtool_pcpu_stats),
156162306a36Sopenharmony_ci				  GFP_KERNEL);
156262306a36Sopenharmony_ci	if (!pcpu_sum)
156362306a36Sopenharmony_ci		return;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	netvsc_get_pcpu_stats(dev, pcpu_sum);
156662306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
156762306a36Sopenharmony_ci		struct netvsc_ethtool_pcpu_stats *this_sum = &pcpu_sum[cpu];
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(pcpu_stats); j++)
157062306a36Sopenharmony_ci			data[i++] = *(u64 *)((void *)this_sum
157162306a36Sopenharmony_ci					     + pcpu_stats[j].offset);
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci	kvfree(pcpu_sum);
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_cistatic void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
157962306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
158062306a36Sopenharmony_ci	u8 *p = data;
158162306a36Sopenharmony_ci	int i, cpu;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (!nvdev)
158462306a36Sopenharmony_ci		return;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	switch (stringset) {
158762306a36Sopenharmony_ci	case ETH_SS_STATS:
158862306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
158962306a36Sopenharmony_ci			ethtool_sprintf(&p, netvsc_stats[i].name);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(vf_stats); i++)
159262306a36Sopenharmony_ci			ethtool_sprintf(&p, vf_stats[i].name);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci		for (i = 0; i < nvdev->num_chn; i++) {
159562306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_packets", i);
159662306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
159762306a36Sopenharmony_ci			ethtool_sprintf(&p, "tx_queue_%u_xdp_xmit", i);
159862306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_packets", i);
159962306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
160062306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_xdp_drop", i);
160162306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_xdp_redirect", i);
160262306a36Sopenharmony_ci			ethtool_sprintf(&p, "rx_queue_%u_xdp_tx", i);
160362306a36Sopenharmony_ci		}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci		for_each_present_cpu(cpu) {
160662306a36Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(pcpu_stats); i++)
160762306a36Sopenharmony_ci				ethtool_sprintf(&p, pcpu_stats[i].name, cpu);
160862306a36Sopenharmony_ci		}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci		break;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic int
161562306a36Sopenharmony_cinetvsc_get_rss_hash_opts(struct net_device_context *ndc,
161662306a36Sopenharmony_ci			 struct ethtool_rxnfc *info)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	const u32 l4_flag = RXH_L4_B_0_1 | RXH_L4_B_2_3;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	info->data = RXH_IP_SRC | RXH_IP_DST;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	switch (info->flow_type) {
162362306a36Sopenharmony_ci	case TCP_V4_FLOW:
162462306a36Sopenharmony_ci		if (ndc->l4_hash & HV_TCP4_L4HASH)
162562306a36Sopenharmony_ci			info->data |= l4_flag;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci		break;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	case TCP_V6_FLOW:
163062306a36Sopenharmony_ci		if (ndc->l4_hash & HV_TCP6_L4HASH)
163162306a36Sopenharmony_ci			info->data |= l4_flag;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci		break;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	case UDP_V4_FLOW:
163662306a36Sopenharmony_ci		if (ndc->l4_hash & HV_UDP4_L4HASH)
163762306a36Sopenharmony_ci			info->data |= l4_flag;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci		break;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	case UDP_V6_FLOW:
164262306a36Sopenharmony_ci		if (ndc->l4_hash & HV_UDP6_L4HASH)
164362306a36Sopenharmony_ci			info->data |= l4_flag;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci		break;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	case IPV4_FLOW:
164862306a36Sopenharmony_ci	case IPV6_FLOW:
164962306a36Sopenharmony_ci		break;
165062306a36Sopenharmony_ci	default:
165162306a36Sopenharmony_ci		info->data = 0;
165262306a36Sopenharmony_ci		break;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	return 0;
165662306a36Sopenharmony_ci}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_cistatic int
165962306a36Sopenharmony_cinetvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
166062306a36Sopenharmony_ci		 u32 *rules)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
166362306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (!nvdev)
166662306a36Sopenharmony_ci		return -ENODEV;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	switch (info->cmd) {
166962306a36Sopenharmony_ci	case ETHTOOL_GRXRINGS:
167062306a36Sopenharmony_ci		info->data = nvdev->num_chn;
167162306a36Sopenharmony_ci		return 0;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	case ETHTOOL_GRXFH:
167462306a36Sopenharmony_ci		return netvsc_get_rss_hash_opts(ndc, info);
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci	return -EOPNOTSUPP;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
168062306a36Sopenharmony_ci				    struct ethtool_rxnfc *info)
168162306a36Sopenharmony_ci{
168262306a36Sopenharmony_ci	if (info->data == (RXH_IP_SRC | RXH_IP_DST |
168362306a36Sopenharmony_ci			   RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
168462306a36Sopenharmony_ci		switch (info->flow_type) {
168562306a36Sopenharmony_ci		case TCP_V4_FLOW:
168662306a36Sopenharmony_ci			ndc->l4_hash |= HV_TCP4_L4HASH;
168762306a36Sopenharmony_ci			break;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		case TCP_V6_FLOW:
169062306a36Sopenharmony_ci			ndc->l4_hash |= HV_TCP6_L4HASH;
169162306a36Sopenharmony_ci			break;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci		case UDP_V4_FLOW:
169462306a36Sopenharmony_ci			ndc->l4_hash |= HV_UDP4_L4HASH;
169562306a36Sopenharmony_ci			break;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		case UDP_V6_FLOW:
169862306a36Sopenharmony_ci			ndc->l4_hash |= HV_UDP6_L4HASH;
169962306a36Sopenharmony_ci			break;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		default:
170262306a36Sopenharmony_ci			return -EOPNOTSUPP;
170362306a36Sopenharmony_ci		}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci		return 0;
170662306a36Sopenharmony_ci	}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	if (info->data == (RXH_IP_SRC | RXH_IP_DST)) {
170962306a36Sopenharmony_ci		switch (info->flow_type) {
171062306a36Sopenharmony_ci		case TCP_V4_FLOW:
171162306a36Sopenharmony_ci			ndc->l4_hash &= ~HV_TCP4_L4HASH;
171262306a36Sopenharmony_ci			break;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci		case TCP_V6_FLOW:
171562306a36Sopenharmony_ci			ndc->l4_hash &= ~HV_TCP6_L4HASH;
171662306a36Sopenharmony_ci			break;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci		case UDP_V4_FLOW:
171962306a36Sopenharmony_ci			ndc->l4_hash &= ~HV_UDP4_L4HASH;
172062306a36Sopenharmony_ci			break;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		case UDP_V6_FLOW:
172362306a36Sopenharmony_ci			ndc->l4_hash &= ~HV_UDP6_L4HASH;
172462306a36Sopenharmony_ci			break;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		default:
172762306a36Sopenharmony_ci			return -EOPNOTSUPP;
172862306a36Sopenharmony_ci		}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		return 0;
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	return -EOPNOTSUPP;
173462306a36Sopenharmony_ci}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_cistatic int
173762306a36Sopenharmony_cinetvsc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(ndev);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (info->cmd == ETHTOOL_SRXFH)
174262306a36Sopenharmony_ci		return netvsc_set_rss_hash_opts(ndc, info);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	return -EOPNOTSUPP;
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_cistatic u32 netvsc_get_rxfh_key_size(struct net_device *dev)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	return NETVSC_HASH_KEYLEN;
175062306a36Sopenharmony_ci}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_cistatic u32 netvsc_rss_indir_size(struct net_device *dev)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	return ndc->rx_table_sz;
175762306a36Sopenharmony_ci}
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key,
176062306a36Sopenharmony_ci			   u8 *hfunc)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
176362306a36Sopenharmony_ci	struct netvsc_device *ndev = rtnl_dereference(ndc->nvdev);
176462306a36Sopenharmony_ci	struct rndis_device *rndis_dev;
176562306a36Sopenharmony_ci	int i;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (!ndev)
176862306a36Sopenharmony_ci		return -ENODEV;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (hfunc)
177162306a36Sopenharmony_ci		*hfunc = ETH_RSS_HASH_TOP;	/* Toeplitz */
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	rndis_dev = ndev->extension;
177462306a36Sopenharmony_ci	if (indir) {
177562306a36Sopenharmony_ci		for (i = 0; i < ndc->rx_table_sz; i++)
177662306a36Sopenharmony_ci			indir[i] = ndc->rx_table[i];
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if (key)
178062306a36Sopenharmony_ci		memcpy(key, rndis_dev->rss_key, NETVSC_HASH_KEYLEN);
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	return 0;
178362306a36Sopenharmony_ci}
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_cistatic int netvsc_set_rxfh(struct net_device *dev, const u32 *indir,
178662306a36Sopenharmony_ci			   const u8 *key, const u8 hfunc)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(dev);
178962306a36Sopenharmony_ci	struct netvsc_device *ndev = rtnl_dereference(ndc->nvdev);
179062306a36Sopenharmony_ci	struct rndis_device *rndis_dev;
179162306a36Sopenharmony_ci	int i;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	if (!ndev)
179462306a36Sopenharmony_ci		return -ENODEV;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
179762306a36Sopenharmony_ci		return -EOPNOTSUPP;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	rndis_dev = ndev->extension;
180062306a36Sopenharmony_ci	if (indir) {
180162306a36Sopenharmony_ci		for (i = 0; i < ndc->rx_table_sz; i++)
180262306a36Sopenharmony_ci			if (indir[i] >= ndev->num_chn)
180362306a36Sopenharmony_ci				return -EINVAL;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci		for (i = 0; i < ndc->rx_table_sz; i++)
180662306a36Sopenharmony_ci			ndc->rx_table[i] = indir[i];
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (!key) {
181062306a36Sopenharmony_ci		if (!indir)
181162306a36Sopenharmony_ci			return 0;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		key = rndis_dev->rss_key;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	return rndis_filter_set_rss_param(rndis_dev, key);
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci/* Hyper-V RNDIS protocol does not have ring in the HW sense.
182062306a36Sopenharmony_ci * It does have pre-allocated receive area which is divided into sections.
182162306a36Sopenharmony_ci */
182262306a36Sopenharmony_cistatic void __netvsc_get_ringparam(struct netvsc_device *nvdev,
182362306a36Sopenharmony_ci				   struct ethtool_ringparam *ring)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	u32 max_buf_size;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	ring->rx_pending = nvdev->recv_section_cnt;
182862306a36Sopenharmony_ci	ring->tx_pending = nvdev->send_section_cnt;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (nvdev->nvsp_version <= NVSP_PROTOCOL_VERSION_2)
183162306a36Sopenharmony_ci		max_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
183262306a36Sopenharmony_ci	else
183362306a36Sopenharmony_ci		max_buf_size = NETVSC_RECEIVE_BUFFER_SIZE;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	ring->rx_max_pending = max_buf_size / nvdev->recv_section_size;
183662306a36Sopenharmony_ci	ring->tx_max_pending = NETVSC_SEND_BUFFER_SIZE
183762306a36Sopenharmony_ci		/ nvdev->send_section_size;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void netvsc_get_ringparam(struct net_device *ndev,
184162306a36Sopenharmony_ci				 struct ethtool_ringparam *ring,
184262306a36Sopenharmony_ci				 struct kernel_ethtool_ringparam *kernel_ring,
184362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(ndev);
184662306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (!nvdev)
184962306a36Sopenharmony_ci		return;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	__netvsc_get_ringparam(nvdev, ring);
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic int netvsc_set_ringparam(struct net_device *ndev,
185562306a36Sopenharmony_ci				struct ethtool_ringparam *ring,
185662306a36Sopenharmony_ci				struct kernel_ethtool_ringparam *kernel_ring,
185762306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
185862306a36Sopenharmony_ci{
185962306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(ndev);
186062306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
186162306a36Sopenharmony_ci	struct netvsc_device_info *device_info;
186262306a36Sopenharmony_ci	struct ethtool_ringparam orig;
186362306a36Sopenharmony_ci	u32 new_tx, new_rx;
186462306a36Sopenharmony_ci	int ret = 0;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy)
186762306a36Sopenharmony_ci		return -ENODEV;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	memset(&orig, 0, sizeof(orig));
187062306a36Sopenharmony_ci	__netvsc_get_ringparam(nvdev, &orig);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	new_tx = clamp_t(u32, ring->tx_pending,
187362306a36Sopenharmony_ci			 NETVSC_MIN_TX_SECTIONS, orig.tx_max_pending);
187462306a36Sopenharmony_ci	new_rx = clamp_t(u32, ring->rx_pending,
187562306a36Sopenharmony_ci			 NETVSC_MIN_RX_SECTIONS, orig.rx_max_pending);
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	if (new_tx == orig.tx_pending &&
187862306a36Sopenharmony_ci	    new_rx == orig.rx_pending)
187962306a36Sopenharmony_ci		return 0;	 /* no change */
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	device_info = netvsc_devinfo_get(nvdev);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (!device_info)
188462306a36Sopenharmony_ci		return -ENOMEM;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	device_info->send_sections = new_tx;
188762306a36Sopenharmony_ci	device_info->recv_sections = new_rx;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	ret = netvsc_detach(ndev, nvdev);
189062306a36Sopenharmony_ci	if (ret)
189162306a36Sopenharmony_ci		goto out;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ret = netvsc_attach(ndev, device_info);
189462306a36Sopenharmony_ci	if (ret) {
189562306a36Sopenharmony_ci		device_info->send_sections = orig.tx_pending;
189662306a36Sopenharmony_ci		device_info->recv_sections = orig.rx_pending;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		if (netvsc_attach(ndev, device_info))
189962306a36Sopenharmony_ci			netdev_err(ndev, "restoring ringparam failed");
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ciout:
190362306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
190462306a36Sopenharmony_ci	return ret;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic netdev_features_t netvsc_fix_features(struct net_device *ndev,
190862306a36Sopenharmony_ci					     netdev_features_t features)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(ndev);
191162306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy)
191462306a36Sopenharmony_ci		return features;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if ((features & NETIF_F_LRO) && netvsc_xdp_get(nvdev)) {
191762306a36Sopenharmony_ci		features ^= NETIF_F_LRO;
191862306a36Sopenharmony_ci		netdev_info(ndev, "Skip LRO - unsupported with XDP\n");
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	return features;
192262306a36Sopenharmony_ci}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_cistatic int netvsc_set_features(struct net_device *ndev,
192562306a36Sopenharmony_ci			       netdev_features_t features)
192662306a36Sopenharmony_ci{
192762306a36Sopenharmony_ci	netdev_features_t change = features ^ ndev->features;
192862306a36Sopenharmony_ci	struct net_device_context *ndevctx = netdev_priv(ndev);
192962306a36Sopenharmony_ci	struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
193062306a36Sopenharmony_ci	struct net_device *vf_netdev = rtnl_dereference(ndevctx->vf_netdev);
193162306a36Sopenharmony_ci	struct ndis_offload_params offloads;
193262306a36Sopenharmony_ci	int ret = 0;
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (!nvdev || nvdev->destroy)
193562306a36Sopenharmony_ci		return -ENODEV;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	if (!(change & NETIF_F_LRO))
193862306a36Sopenharmony_ci		goto syncvf;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	memset(&offloads, 0, sizeof(struct ndis_offload_params));
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	if (features & NETIF_F_LRO) {
194362306a36Sopenharmony_ci		offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
194462306a36Sopenharmony_ci		offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED;
194562306a36Sopenharmony_ci	} else {
194662306a36Sopenharmony_ci		offloads.rsc_ip_v4 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
194762306a36Sopenharmony_ci		offloads.rsc_ip_v6 = NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED;
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	ret = rndis_filter_set_offload_params(ndev, nvdev, &offloads);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	if (ret) {
195362306a36Sopenharmony_ci		features ^= NETIF_F_LRO;
195462306a36Sopenharmony_ci		ndev->features = features;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cisyncvf:
195862306a36Sopenharmony_ci	if (!vf_netdev)
195962306a36Sopenharmony_ci		return ret;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	vf_netdev->wanted_features = features;
196262306a36Sopenharmony_ci	netdev_update_features(vf_netdev);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	return ret;
196562306a36Sopenharmony_ci}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic int netvsc_get_regs_len(struct net_device *netdev)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	return VRSS_SEND_TAB_SIZE * sizeof(u32);
197062306a36Sopenharmony_ci}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_cistatic void netvsc_get_regs(struct net_device *netdev,
197362306a36Sopenharmony_ci			    struct ethtool_regs *regs, void *p)
197462306a36Sopenharmony_ci{
197562306a36Sopenharmony_ci	struct net_device_context *ndc = netdev_priv(netdev);
197662306a36Sopenharmony_ci	u32 *regs_buff = p;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	/* increase the version, if buffer format is changed. */
197962306a36Sopenharmony_ci	regs->version = 1;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	memcpy(regs_buff, ndc->tx_table, VRSS_SEND_TAB_SIZE * sizeof(u32));
198262306a36Sopenharmony_ci}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_cistatic u32 netvsc_get_msglevel(struct net_device *ndev)
198562306a36Sopenharmony_ci{
198662306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	return ndev_ctx->msg_enable;
198962306a36Sopenharmony_ci}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_cistatic void netvsc_set_msglevel(struct net_device *ndev, u32 val)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	ndev_ctx->msg_enable = val;
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = {
199962306a36Sopenharmony_ci	.get_drvinfo	= netvsc_get_drvinfo,
200062306a36Sopenharmony_ci	.get_regs_len	= netvsc_get_regs_len,
200162306a36Sopenharmony_ci	.get_regs	= netvsc_get_regs,
200262306a36Sopenharmony_ci	.get_msglevel	= netvsc_get_msglevel,
200362306a36Sopenharmony_ci	.set_msglevel	= netvsc_set_msglevel,
200462306a36Sopenharmony_ci	.get_link	= ethtool_op_get_link,
200562306a36Sopenharmony_ci	.get_ethtool_stats = netvsc_get_ethtool_stats,
200662306a36Sopenharmony_ci	.get_sset_count = netvsc_get_sset_count,
200762306a36Sopenharmony_ci	.get_strings	= netvsc_get_strings,
200862306a36Sopenharmony_ci	.get_channels   = netvsc_get_channels,
200962306a36Sopenharmony_ci	.set_channels   = netvsc_set_channels,
201062306a36Sopenharmony_ci	.get_ts_info	= ethtool_op_get_ts_info,
201162306a36Sopenharmony_ci	.get_rxnfc	= netvsc_get_rxnfc,
201262306a36Sopenharmony_ci	.set_rxnfc	= netvsc_set_rxnfc,
201362306a36Sopenharmony_ci	.get_rxfh_key_size = netvsc_get_rxfh_key_size,
201462306a36Sopenharmony_ci	.get_rxfh_indir_size = netvsc_rss_indir_size,
201562306a36Sopenharmony_ci	.get_rxfh	= netvsc_get_rxfh,
201662306a36Sopenharmony_ci	.set_rxfh	= netvsc_set_rxfh,
201762306a36Sopenharmony_ci	.get_link_ksettings = netvsc_get_link_ksettings,
201862306a36Sopenharmony_ci	.set_link_ksettings = netvsc_set_link_ksettings,
201962306a36Sopenharmony_ci	.get_ringparam	= netvsc_get_ringparam,
202062306a36Sopenharmony_ci	.set_ringparam	= netvsc_set_ringparam,
202162306a36Sopenharmony_ci};
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_cistatic const struct net_device_ops device_ops = {
202462306a36Sopenharmony_ci	.ndo_open =			netvsc_open,
202562306a36Sopenharmony_ci	.ndo_stop =			netvsc_close,
202662306a36Sopenharmony_ci	.ndo_start_xmit =		netvsc_start_xmit,
202762306a36Sopenharmony_ci	.ndo_change_rx_flags =		netvsc_change_rx_flags,
202862306a36Sopenharmony_ci	.ndo_set_rx_mode =		netvsc_set_rx_mode,
202962306a36Sopenharmony_ci	.ndo_fix_features =		netvsc_fix_features,
203062306a36Sopenharmony_ci	.ndo_set_features =		netvsc_set_features,
203162306a36Sopenharmony_ci	.ndo_change_mtu =		netvsc_change_mtu,
203262306a36Sopenharmony_ci	.ndo_validate_addr =		eth_validate_addr,
203362306a36Sopenharmony_ci	.ndo_set_mac_address =		netvsc_set_mac_addr,
203462306a36Sopenharmony_ci	.ndo_select_queue =		netvsc_select_queue,
203562306a36Sopenharmony_ci	.ndo_get_stats64 =		netvsc_get_stats64,
203662306a36Sopenharmony_ci	.ndo_bpf =			netvsc_bpf,
203762306a36Sopenharmony_ci	.ndo_xdp_xmit =			netvsc_ndoxdp_xmit,
203862306a36Sopenharmony_ci};
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci/*
204162306a36Sopenharmony_ci * Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link
204262306a36Sopenharmony_ci * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is
204362306a36Sopenharmony_ci * present send GARP packet to network peers with netif_notify_peers().
204462306a36Sopenharmony_ci */
204562306a36Sopenharmony_cistatic void netvsc_link_change(struct work_struct *w)
204662306a36Sopenharmony_ci{
204762306a36Sopenharmony_ci	struct net_device_context *ndev_ctx =
204862306a36Sopenharmony_ci		container_of(w, struct net_device_context, dwork.work);
204962306a36Sopenharmony_ci	struct hv_device *device_obj = ndev_ctx->device_ctx;
205062306a36Sopenharmony_ci	struct net_device *net = hv_get_drvdata(device_obj);
205162306a36Sopenharmony_ci	unsigned long flags, next_reconfig, delay;
205262306a36Sopenharmony_ci	struct netvsc_reconfig *event = NULL;
205362306a36Sopenharmony_ci	struct netvsc_device *net_device;
205462306a36Sopenharmony_ci	struct rndis_device *rdev;
205562306a36Sopenharmony_ci	bool reschedule = false;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	/* if changes are happening, comeback later */
205862306a36Sopenharmony_ci	if (!rtnl_trylock()) {
205962306a36Sopenharmony_ci		schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
206062306a36Sopenharmony_ci		return;
206162306a36Sopenharmony_ci	}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	net_device = rtnl_dereference(ndev_ctx->nvdev);
206462306a36Sopenharmony_ci	if (!net_device)
206562306a36Sopenharmony_ci		goto out_unlock;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	rdev = net_device->extension;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT;
207062306a36Sopenharmony_ci	if (time_is_after_jiffies(next_reconfig)) {
207162306a36Sopenharmony_ci		/* link_watch only sends one notification with current state
207262306a36Sopenharmony_ci		 * per second, avoid doing reconfig more frequently. Handle
207362306a36Sopenharmony_ci		 * wrap around.
207462306a36Sopenharmony_ci		 */
207562306a36Sopenharmony_ci		delay = next_reconfig - jiffies;
207662306a36Sopenharmony_ci		delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT;
207762306a36Sopenharmony_ci		schedule_delayed_work(&ndev_ctx->dwork, delay);
207862306a36Sopenharmony_ci		goto out_unlock;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci	ndev_ctx->last_reconfig = jiffies;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	spin_lock_irqsave(&ndev_ctx->lock, flags);
208362306a36Sopenharmony_ci	if (!list_empty(&ndev_ctx->reconfig_events)) {
208462306a36Sopenharmony_ci		event = list_first_entry(&ndev_ctx->reconfig_events,
208562306a36Sopenharmony_ci					 struct netvsc_reconfig, list);
208662306a36Sopenharmony_ci		list_del(&event->list);
208762306a36Sopenharmony_ci		reschedule = !list_empty(&ndev_ctx->reconfig_events);
208862306a36Sopenharmony_ci	}
208962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ndev_ctx->lock, flags);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	if (!event)
209262306a36Sopenharmony_ci		goto out_unlock;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	switch (event->event) {
209562306a36Sopenharmony_ci		/* Only the following events are possible due to the check in
209662306a36Sopenharmony_ci		 * netvsc_linkstatus_callback()
209762306a36Sopenharmony_ci		 */
209862306a36Sopenharmony_ci	case RNDIS_STATUS_MEDIA_CONNECT:
209962306a36Sopenharmony_ci		if (rdev->link_state) {
210062306a36Sopenharmony_ci			rdev->link_state = false;
210162306a36Sopenharmony_ci			netif_carrier_on(net);
210262306a36Sopenharmony_ci			netvsc_tx_enable(net_device, net);
210362306a36Sopenharmony_ci		} else {
210462306a36Sopenharmony_ci			__netdev_notify_peers(net);
210562306a36Sopenharmony_ci		}
210662306a36Sopenharmony_ci		kfree(event);
210762306a36Sopenharmony_ci		break;
210862306a36Sopenharmony_ci	case RNDIS_STATUS_MEDIA_DISCONNECT:
210962306a36Sopenharmony_ci		if (!rdev->link_state) {
211062306a36Sopenharmony_ci			rdev->link_state = true;
211162306a36Sopenharmony_ci			netif_carrier_off(net);
211262306a36Sopenharmony_ci			netvsc_tx_disable(net_device, net);
211362306a36Sopenharmony_ci		}
211462306a36Sopenharmony_ci		kfree(event);
211562306a36Sopenharmony_ci		break;
211662306a36Sopenharmony_ci	case RNDIS_STATUS_NETWORK_CHANGE:
211762306a36Sopenharmony_ci		/* Only makes sense if carrier is present */
211862306a36Sopenharmony_ci		if (!rdev->link_state) {
211962306a36Sopenharmony_ci			rdev->link_state = true;
212062306a36Sopenharmony_ci			netif_carrier_off(net);
212162306a36Sopenharmony_ci			netvsc_tx_disable(net_device, net);
212262306a36Sopenharmony_ci			event->event = RNDIS_STATUS_MEDIA_CONNECT;
212362306a36Sopenharmony_ci			spin_lock_irqsave(&ndev_ctx->lock, flags);
212462306a36Sopenharmony_ci			list_add(&event->list, &ndev_ctx->reconfig_events);
212562306a36Sopenharmony_ci			spin_unlock_irqrestore(&ndev_ctx->lock, flags);
212662306a36Sopenharmony_ci			reschedule = true;
212762306a36Sopenharmony_ci		}
212862306a36Sopenharmony_ci		break;
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	rtnl_unlock();
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	/* link_watch only sends one notification with current state per
213462306a36Sopenharmony_ci	 * second, handle next reconfig event in 2 seconds.
213562306a36Sopenharmony_ci	 */
213662306a36Sopenharmony_ci	if (reschedule)
213762306a36Sopenharmony_ci		schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	return;
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ciout_unlock:
214262306a36Sopenharmony_ci	rtnl_unlock();
214362306a36Sopenharmony_ci}
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_cistatic struct net_device *get_netvsc_byref(struct net_device *vf_netdev)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
214862306a36Sopenharmony_ci	struct net_device *dev;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	dev = netdev_master_upper_dev_get(vf_netdev);
215162306a36Sopenharmony_ci	if (!dev || dev->netdev_ops != &device_ops)
215262306a36Sopenharmony_ci		return NULL;	/* not a netvsc device */
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	net_device_ctx = netdev_priv(dev);
215562306a36Sopenharmony_ci	if (!rtnl_dereference(net_device_ctx->nvdev))
215662306a36Sopenharmony_ci		return NULL;	/* device is removed */
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	return dev;
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci/* Called when VF is injecting data into network stack.
216262306a36Sopenharmony_ci * Change the associated network device from VF to netvsc.
216362306a36Sopenharmony_ci * note: already called with rcu_read_lock
216462306a36Sopenharmony_ci */
216562306a36Sopenharmony_cistatic rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb)
216662306a36Sopenharmony_ci{
216762306a36Sopenharmony_ci	struct sk_buff *skb = *pskb;
216862306a36Sopenharmony_ci	struct net_device *ndev = rcu_dereference(skb->dev->rx_handler_data);
216962306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
217062306a36Sopenharmony_ci	struct netvsc_vf_pcpu_stats *pcpu_stats
217162306a36Sopenharmony_ci		 = this_cpu_ptr(ndev_ctx->vf_stats);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	skb = skb_share_check(skb, GFP_ATOMIC);
217462306a36Sopenharmony_ci	if (unlikely(!skb))
217562306a36Sopenharmony_ci		return RX_HANDLER_CONSUMED;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	*pskb = skb;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	skb->dev = ndev;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	u64_stats_update_begin(&pcpu_stats->syncp);
218262306a36Sopenharmony_ci	pcpu_stats->rx_packets++;
218362306a36Sopenharmony_ci	pcpu_stats->rx_bytes += skb->len;
218462306a36Sopenharmony_ci	u64_stats_update_end(&pcpu_stats->syncp);
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	return RX_HANDLER_ANOTHER;
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_cistatic int netvsc_vf_join(struct net_device *vf_netdev,
219062306a36Sopenharmony_ci			  struct net_device *ndev, int context)
219162306a36Sopenharmony_ci{
219262306a36Sopenharmony_ci	struct net_device_context *ndev_ctx = netdev_priv(ndev);
219362306a36Sopenharmony_ci	int ret;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	ret = netdev_rx_handler_register(vf_netdev,
219662306a36Sopenharmony_ci					 netvsc_vf_handle_frame, ndev);
219762306a36Sopenharmony_ci	if (ret != 0) {
219862306a36Sopenharmony_ci		netdev_err(vf_netdev,
219962306a36Sopenharmony_ci			   "can not register netvsc VF receive handler (err = %d)\n",
220062306a36Sopenharmony_ci			   ret);
220162306a36Sopenharmony_ci		goto rx_handler_failed;
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	ret = netdev_master_upper_dev_link(vf_netdev, ndev,
220562306a36Sopenharmony_ci					   NULL, NULL, NULL);
220662306a36Sopenharmony_ci	if (ret != 0) {
220762306a36Sopenharmony_ci		netdev_err(vf_netdev,
220862306a36Sopenharmony_ci			   "can not set master device %s (err = %d)\n",
220962306a36Sopenharmony_ci			   ndev->name, ret);
221062306a36Sopenharmony_ci		goto upper_link_failed;
221162306a36Sopenharmony_ci	}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	/* If this registration is called from probe context vf_takeover
221462306a36Sopenharmony_ci	 * is taken care of later in probe itself.
221562306a36Sopenharmony_ci	 */
221662306a36Sopenharmony_ci	if (context == VF_REG_IN_NOTIFIER)
221762306a36Sopenharmony_ci		schedule_delayed_work(&ndev_ctx->vf_takeover, VF_TAKEOVER_INT);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	call_netdevice_notifiers(NETDEV_JOIN, vf_netdev);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	netdev_info(vf_netdev, "joined to %s\n", ndev->name);
222262306a36Sopenharmony_ci	return 0;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ciupper_link_failed:
222562306a36Sopenharmony_ci	netdev_rx_handler_unregister(vf_netdev);
222662306a36Sopenharmony_cirx_handler_failed:
222762306a36Sopenharmony_ci	return ret;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_cistatic void __netvsc_vf_setup(struct net_device *ndev,
223162306a36Sopenharmony_ci			      struct net_device *vf_netdev)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	int ret;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	/* Align MTU of VF with master */
223662306a36Sopenharmony_ci	ret = dev_set_mtu(vf_netdev, ndev->mtu);
223762306a36Sopenharmony_ci	if (ret)
223862306a36Sopenharmony_ci		netdev_warn(vf_netdev,
223962306a36Sopenharmony_ci			    "unable to change mtu to %u\n", ndev->mtu);
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	/* set multicast etc flags on VF */
224262306a36Sopenharmony_ci	dev_change_flags(vf_netdev, ndev->flags | IFF_SLAVE, NULL);
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	/* sync address list from ndev to VF */
224562306a36Sopenharmony_ci	netif_addr_lock_bh(ndev);
224662306a36Sopenharmony_ci	dev_uc_sync(vf_netdev, ndev);
224762306a36Sopenharmony_ci	dev_mc_sync(vf_netdev, ndev);
224862306a36Sopenharmony_ci	netif_addr_unlock_bh(ndev);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	if (netif_running(ndev)) {
225162306a36Sopenharmony_ci		ret = dev_open(vf_netdev, NULL);
225262306a36Sopenharmony_ci		if (ret)
225362306a36Sopenharmony_ci			netdev_warn(vf_netdev,
225462306a36Sopenharmony_ci				    "unable to open: %d\n", ret);
225562306a36Sopenharmony_ci	}
225662306a36Sopenharmony_ci}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci/* Setup VF as slave of the synthetic device.
225962306a36Sopenharmony_ci * Runs in workqueue to avoid recursion in netlink callbacks.
226062306a36Sopenharmony_ci */
226162306a36Sopenharmony_cistatic void netvsc_vf_setup(struct work_struct *w)
226262306a36Sopenharmony_ci{
226362306a36Sopenharmony_ci	struct net_device_context *ndev_ctx
226462306a36Sopenharmony_ci		= container_of(w, struct net_device_context, vf_takeover.work);
226562306a36Sopenharmony_ci	struct net_device *ndev = hv_get_drvdata(ndev_ctx->device_ctx);
226662306a36Sopenharmony_ci	struct net_device *vf_netdev;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	if (!rtnl_trylock()) {
226962306a36Sopenharmony_ci		schedule_delayed_work(&ndev_ctx->vf_takeover, 0);
227062306a36Sopenharmony_ci		return;
227162306a36Sopenharmony_ci	}
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
227462306a36Sopenharmony_ci	if (vf_netdev)
227562306a36Sopenharmony_ci		__netvsc_vf_setup(ndev, vf_netdev);
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	rtnl_unlock();
227862306a36Sopenharmony_ci}
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci/* Find netvsc by VF serial number.
228162306a36Sopenharmony_ci * The PCI hyperv controller records the serial number as the slot kobj name.
228262306a36Sopenharmony_ci */
228362306a36Sopenharmony_cistatic struct net_device *get_netvsc_byslot(const struct net_device *vf_netdev)
228462306a36Sopenharmony_ci{
228562306a36Sopenharmony_ci	struct device *parent = vf_netdev->dev.parent;
228662306a36Sopenharmony_ci	struct net_device_context *ndev_ctx;
228762306a36Sopenharmony_ci	struct net_device *ndev;
228862306a36Sopenharmony_ci	struct pci_dev *pdev;
228962306a36Sopenharmony_ci	u32 serial;
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	if (!parent || !dev_is_pci(parent))
229262306a36Sopenharmony_ci		return NULL; /* not a PCI device */
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	pdev = to_pci_dev(parent);
229562306a36Sopenharmony_ci	if (!pdev->slot) {
229662306a36Sopenharmony_ci		netdev_notice(vf_netdev, "no PCI slot information\n");
229762306a36Sopenharmony_ci		return NULL;
229862306a36Sopenharmony_ci	}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (kstrtou32(pci_slot_name(pdev->slot), 10, &serial)) {
230162306a36Sopenharmony_ci		netdev_notice(vf_netdev, "Invalid vf serial:%s\n",
230262306a36Sopenharmony_ci			      pci_slot_name(pdev->slot));
230362306a36Sopenharmony_ci		return NULL;
230462306a36Sopenharmony_ci	}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) {
230762306a36Sopenharmony_ci		if (!ndev_ctx->vf_alloc)
230862306a36Sopenharmony_ci			continue;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci		if (ndev_ctx->vf_serial != serial)
231162306a36Sopenharmony_ci			continue;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		ndev = hv_get_drvdata(ndev_ctx->device_ctx);
231462306a36Sopenharmony_ci		if (ndev->addr_len != vf_netdev->addr_len ||
231562306a36Sopenharmony_ci		    memcmp(ndev->perm_addr, vf_netdev->perm_addr,
231662306a36Sopenharmony_ci			   ndev->addr_len) != 0)
231762306a36Sopenharmony_ci			continue;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci		return ndev;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	/* Fallback path to check synthetic vf with help of mac addr.
232462306a36Sopenharmony_ci	 * Because this function can be called before vf_netdev is
232562306a36Sopenharmony_ci	 * initialized (NETDEV_POST_INIT) when its perm_addr has not been copied
232662306a36Sopenharmony_ci	 * from dev_addr, also try to match to its dev_addr.
232762306a36Sopenharmony_ci	 * Note: On Hyper-V and Azure, it's not possible to set a MAC address
232862306a36Sopenharmony_ci	 * on a VF that matches to the MAC of a unrelated NETVSC device.
232962306a36Sopenharmony_ci	 */
233062306a36Sopenharmony_ci	list_for_each_entry(ndev_ctx, &netvsc_dev_list, list) {
233162306a36Sopenharmony_ci		ndev = hv_get_drvdata(ndev_ctx->device_ctx);
233262306a36Sopenharmony_ci		if (ether_addr_equal(vf_netdev->perm_addr, ndev->perm_addr) ||
233362306a36Sopenharmony_ci		    ether_addr_equal(vf_netdev->dev_addr, ndev->perm_addr))
233462306a36Sopenharmony_ci			return ndev;
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	netdev_notice(vf_netdev,
233862306a36Sopenharmony_ci		      "no netdev found for vf serial:%u\n", serial);
233962306a36Sopenharmony_ci	return NULL;
234062306a36Sopenharmony_ci}
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_cistatic int netvsc_prepare_bonding(struct net_device *vf_netdev)
234362306a36Sopenharmony_ci{
234462306a36Sopenharmony_ci	struct net_device *ndev;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	ndev = get_netvsc_byslot(vf_netdev);
234762306a36Sopenharmony_ci	if (!ndev)
234862306a36Sopenharmony_ci		return NOTIFY_DONE;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	/* set slave flag before open to prevent IPv6 addrconf */
235162306a36Sopenharmony_ci	vf_netdev->flags |= IFF_SLAVE;
235262306a36Sopenharmony_ci	return NOTIFY_DONE;
235362306a36Sopenharmony_ci}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_cistatic int netvsc_register_vf(struct net_device *vf_netdev, int context)
235662306a36Sopenharmony_ci{
235762306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
235862306a36Sopenharmony_ci	struct netvsc_device *netvsc_dev;
235962306a36Sopenharmony_ci	struct bpf_prog *prog;
236062306a36Sopenharmony_ci	struct net_device *ndev;
236162306a36Sopenharmony_ci	int ret;
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	if (vf_netdev->addr_len != ETH_ALEN)
236462306a36Sopenharmony_ci		return NOTIFY_DONE;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	ndev = get_netvsc_byslot(vf_netdev);
236762306a36Sopenharmony_ci	if (!ndev)
236862306a36Sopenharmony_ci		return NOTIFY_DONE;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	net_device_ctx = netdev_priv(ndev);
237162306a36Sopenharmony_ci	netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
237262306a36Sopenharmony_ci	if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev))
237362306a36Sopenharmony_ci		return NOTIFY_DONE;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	/* if synthetic interface is a different namespace,
237662306a36Sopenharmony_ci	 * then move the VF to that namespace; join will be
237762306a36Sopenharmony_ci	 * done again in that context.
237862306a36Sopenharmony_ci	 */
237962306a36Sopenharmony_ci	if (!net_eq(dev_net(ndev), dev_net(vf_netdev))) {
238062306a36Sopenharmony_ci		ret = dev_change_net_namespace(vf_netdev,
238162306a36Sopenharmony_ci					       dev_net(ndev), "eth%d");
238262306a36Sopenharmony_ci		if (ret)
238362306a36Sopenharmony_ci			netdev_err(vf_netdev,
238462306a36Sopenharmony_ci				   "could not move to same namespace as %s: %d\n",
238562306a36Sopenharmony_ci				   ndev->name, ret);
238662306a36Sopenharmony_ci		else
238762306a36Sopenharmony_ci			netdev_info(vf_netdev,
238862306a36Sopenharmony_ci				    "VF moved to namespace with: %s\n",
238962306a36Sopenharmony_ci				    ndev->name);
239062306a36Sopenharmony_ci		return NOTIFY_DONE;
239162306a36Sopenharmony_ci	}
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	netdev_info(ndev, "VF registering: %s\n", vf_netdev->name);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	if (netvsc_vf_join(vf_netdev, ndev, context) != 0)
239662306a36Sopenharmony_ci		return NOTIFY_DONE;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	dev_hold(vf_netdev);
239962306a36Sopenharmony_ci	rcu_assign_pointer(net_device_ctx->vf_netdev, vf_netdev);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	if (ndev->needed_headroom < vf_netdev->needed_headroom)
240262306a36Sopenharmony_ci		ndev->needed_headroom = vf_netdev->needed_headroom;
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	vf_netdev->wanted_features = ndev->features;
240562306a36Sopenharmony_ci	netdev_update_features(vf_netdev);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	prog = netvsc_xdp_get(netvsc_dev);
240862306a36Sopenharmony_ci	netvsc_vf_setxdp(vf_netdev, prog);
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	return NOTIFY_OK;
241162306a36Sopenharmony_ci}
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci/* Change the data path when VF UP/DOWN/CHANGE are detected.
241462306a36Sopenharmony_ci *
241562306a36Sopenharmony_ci * Typically a UP or DOWN event is followed by a CHANGE event, so
241662306a36Sopenharmony_ci * net_device_ctx->data_path_is_vf is used to cache the current data path
241762306a36Sopenharmony_ci * to avoid the duplicate call of netvsc_switch_datapath() and the duplicate
241862306a36Sopenharmony_ci * message.
241962306a36Sopenharmony_ci *
242062306a36Sopenharmony_ci * During hibernation, if a VF NIC driver (e.g. mlx5) preserves the network
242162306a36Sopenharmony_ci * interface, there is only the CHANGE event and no UP or DOWN event.
242262306a36Sopenharmony_ci */
242362306a36Sopenharmony_cistatic int netvsc_vf_changed(struct net_device *vf_netdev, unsigned long event)
242462306a36Sopenharmony_ci{
242562306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
242662306a36Sopenharmony_ci	struct netvsc_device *netvsc_dev;
242762306a36Sopenharmony_ci	struct net_device *ndev;
242862306a36Sopenharmony_ci	bool vf_is_up = false;
242962306a36Sopenharmony_ci	int ret;
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci	if (event != NETDEV_GOING_DOWN)
243262306a36Sopenharmony_ci		vf_is_up = netif_running(vf_netdev);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	ndev = get_netvsc_byref(vf_netdev);
243562306a36Sopenharmony_ci	if (!ndev)
243662306a36Sopenharmony_ci		return NOTIFY_DONE;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	net_device_ctx = netdev_priv(ndev);
243962306a36Sopenharmony_ci	netvsc_dev = rtnl_dereference(net_device_ctx->nvdev);
244062306a36Sopenharmony_ci	if (!netvsc_dev)
244162306a36Sopenharmony_ci		return NOTIFY_DONE;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (net_device_ctx->data_path_is_vf == vf_is_up)
244462306a36Sopenharmony_ci		return NOTIFY_OK;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	if (vf_is_up && !net_device_ctx->vf_alloc) {
244762306a36Sopenharmony_ci		netdev_info(ndev, "Waiting for the VF association from host\n");
244862306a36Sopenharmony_ci		wait_for_completion(&net_device_ctx->vf_add);
244962306a36Sopenharmony_ci	}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	ret = netvsc_switch_datapath(ndev, vf_is_up);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	if (ret) {
245462306a36Sopenharmony_ci		netdev_err(ndev,
245562306a36Sopenharmony_ci			   "Data path failed to switch %s VF: %s, err: %d\n",
245662306a36Sopenharmony_ci			   vf_is_up ? "to" : "from", vf_netdev->name, ret);
245762306a36Sopenharmony_ci		return NOTIFY_DONE;
245862306a36Sopenharmony_ci	} else {
245962306a36Sopenharmony_ci		netdev_info(ndev, "Data path switched %s VF: %s\n",
246062306a36Sopenharmony_ci			    vf_is_up ? "to" : "from", vf_netdev->name);
246162306a36Sopenharmony_ci	}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	return NOTIFY_OK;
246462306a36Sopenharmony_ci}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_cistatic int netvsc_unregister_vf(struct net_device *vf_netdev)
246762306a36Sopenharmony_ci{
246862306a36Sopenharmony_ci	struct net_device *ndev;
246962306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	ndev = get_netvsc_byref(vf_netdev);
247262306a36Sopenharmony_ci	if (!ndev)
247362306a36Sopenharmony_ci		return NOTIFY_DONE;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	net_device_ctx = netdev_priv(ndev);
247662306a36Sopenharmony_ci	cancel_delayed_work_sync(&net_device_ctx->vf_takeover);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name);
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	netvsc_vf_setxdp(vf_netdev, NULL);
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	reinit_completion(&net_device_ctx->vf_add);
248362306a36Sopenharmony_ci	netdev_rx_handler_unregister(vf_netdev);
248462306a36Sopenharmony_ci	netdev_upper_dev_unlink(vf_netdev, ndev);
248562306a36Sopenharmony_ci	RCU_INIT_POINTER(net_device_ctx->vf_netdev, NULL);
248662306a36Sopenharmony_ci	dev_put(vf_netdev);
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	ndev->needed_headroom = RNDIS_AND_PPI_SIZE;
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	return NOTIFY_OK;
249162306a36Sopenharmony_ci}
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_cistatic int check_dev_is_matching_vf(struct net_device *event_ndev)
249462306a36Sopenharmony_ci{
249562306a36Sopenharmony_ci	/* Skip NetVSC interfaces */
249662306a36Sopenharmony_ci	if (event_ndev->netdev_ops == &device_ops)
249762306a36Sopenharmony_ci		return -ENODEV;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	/* Avoid non-Ethernet type devices */
250062306a36Sopenharmony_ci	if (event_ndev->type != ARPHRD_ETHER)
250162306a36Sopenharmony_ci		return -ENODEV;
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	/* Avoid Vlan dev with same MAC registering as VF */
250462306a36Sopenharmony_ci	if (is_vlan_dev(event_ndev))
250562306a36Sopenharmony_ci		return -ENODEV;
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	/* Avoid Bonding master dev with same MAC registering as VF */
250862306a36Sopenharmony_ci	if (netif_is_bond_master(event_ndev))
250962306a36Sopenharmony_ci		return -ENODEV;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	return 0;
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_cistatic int netvsc_probe(struct hv_device *dev,
251562306a36Sopenharmony_ci			const struct hv_vmbus_device_id *dev_id)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	struct net_device *net = NULL, *vf_netdev;
251862306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
251962306a36Sopenharmony_ci	struct netvsc_device_info *device_info = NULL;
252062306a36Sopenharmony_ci	struct netvsc_device *nvdev;
252162306a36Sopenharmony_ci	int ret = -ENOMEM;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	net = alloc_etherdev_mq(sizeof(struct net_device_context),
252462306a36Sopenharmony_ci				VRSS_CHANNEL_MAX);
252562306a36Sopenharmony_ci	if (!net)
252662306a36Sopenharmony_ci		goto no_net;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	netif_carrier_off(net);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	netvsc_init_settings(net);
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	net_device_ctx = netdev_priv(net);
253362306a36Sopenharmony_ci	net_device_ctx->device_ctx = dev;
253462306a36Sopenharmony_ci	net_device_ctx->msg_enable = netif_msg_init(debug, default_msg);
253562306a36Sopenharmony_ci	if (netif_msg_probe(net_device_ctx))
253662306a36Sopenharmony_ci		netdev_dbg(net, "netvsc msg_enable: %d\n",
253762306a36Sopenharmony_ci			   net_device_ctx->msg_enable);
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	hv_set_drvdata(dev, net);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	init_completion(&net_device_ctx->vf_add);
254462306a36Sopenharmony_ci	spin_lock_init(&net_device_ctx->lock);
254562306a36Sopenharmony_ci	INIT_LIST_HEAD(&net_device_ctx->reconfig_events);
254662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&net_device_ctx->vf_takeover, netvsc_vf_setup);
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	net_device_ctx->vf_stats
254962306a36Sopenharmony_ci		= netdev_alloc_pcpu_stats(struct netvsc_vf_pcpu_stats);
255062306a36Sopenharmony_ci	if (!net_device_ctx->vf_stats)
255162306a36Sopenharmony_ci		goto no_stats;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	net->netdev_ops = &device_ops;
255462306a36Sopenharmony_ci	net->ethtool_ops = &ethtool_ops;
255562306a36Sopenharmony_ci	SET_NETDEV_DEV(net, &dev->device);
255662306a36Sopenharmony_ci	dma_set_min_align_mask(&dev->device, HV_HYP_PAGE_SIZE - 1);
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	/* We always need headroom for rndis header */
255962306a36Sopenharmony_ci	net->needed_headroom = RNDIS_AND_PPI_SIZE;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	/* Initialize the number of queues to be 1, we may change it if more
256262306a36Sopenharmony_ci	 * channels are offered later.
256362306a36Sopenharmony_ci	 */
256462306a36Sopenharmony_ci	netif_set_real_num_tx_queues(net, 1);
256562306a36Sopenharmony_ci	netif_set_real_num_rx_queues(net, 1);
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	/* Notify the netvsc driver of the new device */
256862306a36Sopenharmony_ci	device_info = netvsc_devinfo_get(NULL);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	if (!device_info) {
257162306a36Sopenharmony_ci		ret = -ENOMEM;
257262306a36Sopenharmony_ci		goto devinfo_failed;
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	/* We must get rtnl lock before scheduling nvdev->subchan_work,
257662306a36Sopenharmony_ci	 * otherwise netvsc_subchan_work() can get rtnl lock first and wait
257762306a36Sopenharmony_ci	 * all subchannels to show up, but that may not happen because
257862306a36Sopenharmony_ci	 * netvsc_probe() can't get rtnl lock and as a result vmbus_onoffer()
257962306a36Sopenharmony_ci	 * -> ... -> device_add() -> ... -> __device_attach() can't get
258062306a36Sopenharmony_ci	 * the device lock, so all the subchannels can't be processed --
258162306a36Sopenharmony_ci	 * finally netvsc_subchan_work() hangs forever.
258262306a36Sopenharmony_ci	 *
258362306a36Sopenharmony_ci	 * The rtnl lock also needs to be held before rndis_filter_device_add()
258462306a36Sopenharmony_ci	 * which advertises nvsp_2_vsc_capability / sriov bit, and triggers
258562306a36Sopenharmony_ci	 * VF NIC offering and registering. If VF NIC finished register_netdev()
258662306a36Sopenharmony_ci	 * earlier it may cause name based config failure.
258762306a36Sopenharmony_ci	 */
258862306a36Sopenharmony_ci	rtnl_lock();
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	nvdev = rndis_filter_device_add(dev, device_info);
259162306a36Sopenharmony_ci	if (IS_ERR(nvdev)) {
259262306a36Sopenharmony_ci		ret = PTR_ERR(nvdev);
259362306a36Sopenharmony_ci		netdev_err(net, "unable to add netvsc device (ret %d)\n", ret);
259462306a36Sopenharmony_ci		goto rndis_failed;
259562306a36Sopenharmony_ci	}
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	eth_hw_addr_set(net, device_info->mac_adr);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	if (nvdev->num_chn > 1)
260062306a36Sopenharmony_ci		schedule_work(&nvdev->subchan_work);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	/* hw_features computed in rndis_netdev_set_hwcaps() */
260362306a36Sopenharmony_ci	net->features = net->hw_features |
260462306a36Sopenharmony_ci		NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX |
260562306a36Sopenharmony_ci		NETIF_F_HW_VLAN_CTAG_RX;
260662306a36Sopenharmony_ci	net->vlan_features = net->features;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	netdev_lockdep_set_classes(net);
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	net->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
261162306a36Sopenharmony_ci			    NETDEV_XDP_ACT_NDO_XMIT;
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	/* MTU range: 68 - 1500 or 65521 */
261462306a36Sopenharmony_ci	net->min_mtu = NETVSC_MTU_MIN;
261562306a36Sopenharmony_ci	if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
261662306a36Sopenharmony_ci		net->max_mtu = NETVSC_MTU - ETH_HLEN;
261762306a36Sopenharmony_ci	else
261862306a36Sopenharmony_ci		net->max_mtu = ETH_DATA_LEN;
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	nvdev->tx_disable = false;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	ret = register_netdevice(net);
262362306a36Sopenharmony_ci	if (ret != 0) {
262462306a36Sopenharmony_ci		pr_err("Unable to register netdev.\n");
262562306a36Sopenharmony_ci		goto register_failed;
262662306a36Sopenharmony_ci	}
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	list_add(&net_device_ctx->list, &netvsc_dev_list);
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	/* When the hv_netvsc driver is unloaded and reloaded, the
263162306a36Sopenharmony_ci	 * NET_DEVICE_REGISTER for the vf device is replayed before probe
263262306a36Sopenharmony_ci	 * is complete. This is because register_netdevice_notifier() gets
263362306a36Sopenharmony_ci	 * registered before vmbus_driver_register() so that callback func
263462306a36Sopenharmony_ci	 * is set before probe and we don't miss events like NETDEV_POST_INIT
263562306a36Sopenharmony_ci	 * So, in this section we try to register the matching vf device that
263662306a36Sopenharmony_ci	 * is present as a netdevice, knowing that its register call is not
263762306a36Sopenharmony_ci	 * processed in the netvsc_netdev_notifier(as probing is progress and
263862306a36Sopenharmony_ci	 * get_netvsc_byslot fails).
263962306a36Sopenharmony_ci	 */
264062306a36Sopenharmony_ci	for_each_netdev(dev_net(net), vf_netdev) {
264162306a36Sopenharmony_ci		ret = check_dev_is_matching_vf(vf_netdev);
264262306a36Sopenharmony_ci		if (ret != 0)
264362306a36Sopenharmony_ci			continue;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci		if (net != get_netvsc_byslot(vf_netdev))
264662306a36Sopenharmony_ci			continue;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci		netvsc_prepare_bonding(vf_netdev);
264962306a36Sopenharmony_ci		netvsc_register_vf(vf_netdev, VF_REG_IN_PROBE);
265062306a36Sopenharmony_ci		__netvsc_vf_setup(net, vf_netdev);
265162306a36Sopenharmony_ci		break;
265262306a36Sopenharmony_ci	}
265362306a36Sopenharmony_ci	rtnl_unlock();
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
265662306a36Sopenharmony_ci	return 0;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ciregister_failed:
265962306a36Sopenharmony_ci	rndis_filter_device_remove(dev, nvdev);
266062306a36Sopenharmony_cirndis_failed:
266162306a36Sopenharmony_ci	rtnl_unlock();
266262306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
266362306a36Sopenharmony_cidevinfo_failed:
266462306a36Sopenharmony_ci	free_percpu(net_device_ctx->vf_stats);
266562306a36Sopenharmony_cino_stats:
266662306a36Sopenharmony_ci	hv_set_drvdata(dev, NULL);
266762306a36Sopenharmony_ci	free_netdev(net);
266862306a36Sopenharmony_cino_net:
266962306a36Sopenharmony_ci	return ret;
267062306a36Sopenharmony_ci}
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_cistatic void netvsc_remove(struct hv_device *dev)
267362306a36Sopenharmony_ci{
267462306a36Sopenharmony_ci	struct net_device_context *ndev_ctx;
267562306a36Sopenharmony_ci	struct net_device *vf_netdev, *net;
267662306a36Sopenharmony_ci	struct netvsc_device *nvdev;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	net = hv_get_drvdata(dev);
267962306a36Sopenharmony_ci	if (net == NULL) {
268062306a36Sopenharmony_ci		dev_err(&dev->device, "No net device to remove\n");
268162306a36Sopenharmony_ci		return;
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	ndev_ctx = netdev_priv(net);
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci	cancel_delayed_work_sync(&ndev_ctx->dwork);
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	rtnl_lock();
268962306a36Sopenharmony_ci	nvdev = rtnl_dereference(ndev_ctx->nvdev);
269062306a36Sopenharmony_ci	if (nvdev) {
269162306a36Sopenharmony_ci		cancel_work_sync(&nvdev->subchan_work);
269262306a36Sopenharmony_ci		netvsc_xdp_set(net, NULL, NULL, nvdev);
269362306a36Sopenharmony_ci	}
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	/*
269662306a36Sopenharmony_ci	 * Call to the vsc driver to let it know that the device is being
269762306a36Sopenharmony_ci	 * removed. Also blocks mtu and channel changes.
269862306a36Sopenharmony_ci	 */
269962306a36Sopenharmony_ci	vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev);
270062306a36Sopenharmony_ci	if (vf_netdev)
270162306a36Sopenharmony_ci		netvsc_unregister_vf(vf_netdev);
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	if (nvdev)
270462306a36Sopenharmony_ci		rndis_filter_device_remove(dev, nvdev);
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	unregister_netdevice(net);
270762306a36Sopenharmony_ci	list_del(&ndev_ctx->list);
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	rtnl_unlock();
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	hv_set_drvdata(dev, NULL);
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	free_percpu(ndev_ctx->vf_stats);
271462306a36Sopenharmony_ci	free_netdev(net);
271562306a36Sopenharmony_ci}
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_cistatic int netvsc_suspend(struct hv_device *dev)
271862306a36Sopenharmony_ci{
271962306a36Sopenharmony_ci	struct net_device_context *ndev_ctx;
272062306a36Sopenharmony_ci	struct netvsc_device *nvdev;
272162306a36Sopenharmony_ci	struct net_device *net;
272262306a36Sopenharmony_ci	int ret;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	net = hv_get_drvdata(dev);
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	ndev_ctx = netdev_priv(net);
272762306a36Sopenharmony_ci	cancel_delayed_work_sync(&ndev_ctx->dwork);
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	rtnl_lock();
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	nvdev = rtnl_dereference(ndev_ctx->nvdev);
273262306a36Sopenharmony_ci	if (nvdev == NULL) {
273362306a36Sopenharmony_ci		ret = -ENODEV;
273462306a36Sopenharmony_ci		goto out;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	/* Save the current config info */
273862306a36Sopenharmony_ci	ndev_ctx->saved_netvsc_dev_info = netvsc_devinfo_get(nvdev);
273962306a36Sopenharmony_ci	if (!ndev_ctx->saved_netvsc_dev_info) {
274062306a36Sopenharmony_ci		ret = -ENOMEM;
274162306a36Sopenharmony_ci		goto out;
274262306a36Sopenharmony_ci	}
274362306a36Sopenharmony_ci	ret = netvsc_detach(net, nvdev);
274462306a36Sopenharmony_ciout:
274562306a36Sopenharmony_ci	rtnl_unlock();
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	return ret;
274862306a36Sopenharmony_ci}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_cistatic int netvsc_resume(struct hv_device *dev)
275162306a36Sopenharmony_ci{
275262306a36Sopenharmony_ci	struct net_device *net = hv_get_drvdata(dev);
275362306a36Sopenharmony_ci	struct net_device_context *net_device_ctx;
275462306a36Sopenharmony_ci	struct netvsc_device_info *device_info;
275562306a36Sopenharmony_ci	int ret;
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci	rtnl_lock();
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	net_device_ctx = netdev_priv(net);
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	/* Reset the data path to the netvsc NIC before re-opening the vmbus
276262306a36Sopenharmony_ci	 * channel. Later netvsc_netdev_event() will switch the data path to
276362306a36Sopenharmony_ci	 * the VF upon the UP or CHANGE event.
276462306a36Sopenharmony_ci	 */
276562306a36Sopenharmony_ci	net_device_ctx->data_path_is_vf = false;
276662306a36Sopenharmony_ci	device_info = net_device_ctx->saved_netvsc_dev_info;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	ret = netvsc_attach(net, device_info);
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	netvsc_devinfo_put(device_info);
277162306a36Sopenharmony_ci	net_device_ctx->saved_netvsc_dev_info = NULL;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	rtnl_unlock();
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	return ret;
277662306a36Sopenharmony_ci}
277762306a36Sopenharmony_cistatic const struct hv_vmbus_device_id id_table[] = {
277862306a36Sopenharmony_ci	/* Network guid */
277962306a36Sopenharmony_ci	{ HV_NIC_GUID, },
278062306a36Sopenharmony_ci	{ },
278162306a36Sopenharmony_ci};
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(vmbus, id_table);
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci/* The one and only one */
278662306a36Sopenharmony_cistatic struct  hv_driver netvsc_drv = {
278762306a36Sopenharmony_ci	.name = KBUILD_MODNAME,
278862306a36Sopenharmony_ci	.id_table = id_table,
278962306a36Sopenharmony_ci	.probe = netvsc_probe,
279062306a36Sopenharmony_ci	.remove = netvsc_remove,
279162306a36Sopenharmony_ci	.suspend = netvsc_suspend,
279262306a36Sopenharmony_ci	.resume = netvsc_resume,
279362306a36Sopenharmony_ci	.driver = {
279462306a36Sopenharmony_ci		.probe_type = PROBE_FORCE_SYNCHRONOUS,
279562306a36Sopenharmony_ci	},
279662306a36Sopenharmony_ci};
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci/*
279962306a36Sopenharmony_ci * On Hyper-V, every VF interface is matched with a corresponding
280062306a36Sopenharmony_ci * synthetic interface. The synthetic interface is presented first
280162306a36Sopenharmony_ci * to the guest. When the corresponding VF instance is registered,
280262306a36Sopenharmony_ci * we will take care of switching the data path.
280362306a36Sopenharmony_ci */
280462306a36Sopenharmony_cistatic int netvsc_netdev_event(struct notifier_block *this,
280562306a36Sopenharmony_ci			       unsigned long event, void *ptr)
280662306a36Sopenharmony_ci{
280762306a36Sopenharmony_ci	struct net_device *event_dev = netdev_notifier_info_to_dev(ptr);
280862306a36Sopenharmony_ci	int ret = 0;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	ret = check_dev_is_matching_vf(event_dev);
281162306a36Sopenharmony_ci	if (ret != 0)
281262306a36Sopenharmony_ci		return NOTIFY_DONE;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	switch (event) {
281562306a36Sopenharmony_ci	case NETDEV_POST_INIT:
281662306a36Sopenharmony_ci		return netvsc_prepare_bonding(event_dev);
281762306a36Sopenharmony_ci	case NETDEV_REGISTER:
281862306a36Sopenharmony_ci		return netvsc_register_vf(event_dev, VF_REG_IN_NOTIFIER);
281962306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
282062306a36Sopenharmony_ci		return netvsc_unregister_vf(event_dev);
282162306a36Sopenharmony_ci	case NETDEV_UP:
282262306a36Sopenharmony_ci	case NETDEV_DOWN:
282362306a36Sopenharmony_ci	case NETDEV_CHANGE:
282462306a36Sopenharmony_ci	case NETDEV_GOING_DOWN:
282562306a36Sopenharmony_ci		return netvsc_vf_changed(event_dev, event);
282662306a36Sopenharmony_ci	default:
282762306a36Sopenharmony_ci		return NOTIFY_DONE;
282862306a36Sopenharmony_ci	}
282962306a36Sopenharmony_ci}
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_cistatic struct notifier_block netvsc_netdev_notifier = {
283262306a36Sopenharmony_ci	.notifier_call = netvsc_netdev_event,
283362306a36Sopenharmony_ci};
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_cistatic void __exit netvsc_drv_exit(void)
283662306a36Sopenharmony_ci{
283762306a36Sopenharmony_ci	unregister_netdevice_notifier(&netvsc_netdev_notifier);
283862306a36Sopenharmony_ci	vmbus_driver_unregister(&netvsc_drv);
283962306a36Sopenharmony_ci}
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_cistatic int __init netvsc_drv_init(void)
284262306a36Sopenharmony_ci{
284362306a36Sopenharmony_ci	int ret;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	if (ring_size < RING_SIZE_MIN) {
284662306a36Sopenharmony_ci		ring_size = RING_SIZE_MIN;
284762306a36Sopenharmony_ci		pr_info("Increased ring_size to %u (min allowed)\n",
284862306a36Sopenharmony_ci			ring_size);
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci	netvsc_ring_bytes = VMBUS_RING_SIZE(ring_size * 4096);
285162306a36Sopenharmony_ci
285262306a36Sopenharmony_ci	register_netdevice_notifier(&netvsc_netdev_notifier);
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	ret = vmbus_driver_register(&netvsc_drv);
285562306a36Sopenharmony_ci	if (ret)
285662306a36Sopenharmony_ci		goto err_vmbus_reg;
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	return 0;
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_cierr_vmbus_reg:
286162306a36Sopenharmony_ci	unregister_netdevice_notifier(&netvsc_netdev_notifier);
286262306a36Sopenharmony_ci	return ret;
286362306a36Sopenharmony_ci}
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
286662306a36Sopenharmony_ciMODULE_DESCRIPTION("Microsoft Hyper-V network driver");
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cimodule_init(netvsc_drv_init);
286962306a36Sopenharmony_cimodule_exit(netvsc_drv_exit);
2870