linux/drivers/net/hyperv/netvsc_drv.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2009, Microsoft Corporation.
 *
 * Authors:
 *   Haiyang Zhang <[email protected]>
 *   Hank Janssen  <[email protected]>
 */
#define pr_fmt(fmt)

#include <linux/init.h>
#include <linux/atomic.h>
#include <linux/ethtool.h>
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/netpoll.h>
#include <linux/bpf.h>

#include <net/arp.h>
#include <net/route.h>
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>

#include "hyperv_net.h"

#define RING_SIZE_MIN

#define LINKCHANGE_INT
#define VF_TAKEOVER_INT

/* Macros to define the context of vf registration */
#define VF_REG_IN_PROBE
#define VF_REG_IN_NOTIFIER

static unsigned int ring_size __ro_after_init =;
module_param(ring_size, uint, 0444);
MODULE_PARM_DESC();
unsigned int netvsc_ring_bytes __ro_after_init;

static const u32 default_msg =;

static int debug =;
module_param(debug, int, 0444);
MODULE_PARM_DESC();

static LIST_HEAD(netvsc_dev_list);

static void netvsc_change_rx_flags(struct net_device *net, int change)
{}

static void netvsc_set_rx_mode(struct net_device *net)
{}

static void netvsc_tx_enable(struct netvsc_device *nvscdev,
			     struct net_device *ndev)
{}

static int netvsc_open(struct net_device *net)
{}

static int netvsc_wait_until_empty(struct netvsc_device *nvdev)
{}

static void netvsc_tx_disable(struct netvsc_device *nvscdev,
			      struct net_device *ndev)
{}

static int netvsc_close(struct net_device *net)
{}

static inline void *init_ppi_data(struct rndis_message *msg,
				  u32 ppi_size, u32 pkt_type)
{}

static inline int netvsc_get_tx_queue(struct net_device *ndev,
				      struct sk_buff *skb, int old_idx)
{}

/*
 * Select queue for transmit.
 *
 * If a valid queue has already been assigned, then use that.
 * Otherwise compute tx queue based on hash and the send table.
 *
 * This is basically similar to default (netdev_pick_tx) with the added step
 * of using the host send_table when no other queue has been assigned.
 *
 * TODO support XPS - but get_xps_queue not exported
 */
static u16 netvsc_pick_tx(struct net_device *ndev, struct sk_buff *skb)
{}

static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb,
			       struct net_device *sb_dev)
{}

static u32 fill_pg_buf(unsigned long hvpfn, u32 offset, u32 len,
		       struct hv_page_buffer *pb)
{}

static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb,
			   struct hv_netvsc_packet *packet,
			   struct hv_page_buffer *pb)
{}

static int count_skb_frag_slots(struct sk_buff *skb)
{}

static int netvsc_get_slots(struct sk_buff *skb)
{}

static u32 net_checksum_info(struct sk_buff *skb)
{}

/* Send skb on the slave VF device. */
static int netvsc_vf_xmit(struct net_device *net, struct net_device *vf_netdev,
			  struct sk_buff *skb)
{}

static int netvsc_xmit(struct sk_buff *skb, struct net_device *net, bool xdp_tx)
{}

static netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
				     struct net_device *ndev)
{}

/*
 * netvsc_linkstatus_callback - Link up/down notification
 */
void netvsc_linkstatus_callback(struct net_device *net,
				struct rndis_message *resp,
				void *data, u32 data_buflen)
{}

/* This function should only be called after skb_record_rx_queue() */
void netvsc_xdp_xmit(struct sk_buff *skb, struct net_device *ndev)
{}

static void netvsc_comp_ipcsum(struct sk_buff *skb)
{}

static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
					     struct netvsc_channel *nvchan,
					     struct xdp_buff *xdp)
{}

/*
 * netvsc_recv_callback -  Callback when we receive a packet from the
 * "wire" on the specified device.
 */
int netvsc_recv_callback(struct net_device *net,
			 struct netvsc_device *net_device,
			 struct netvsc_channel *nvchan)
{}

static void netvsc_get_drvinfo(struct net_device *net,
			       struct ethtool_drvinfo *info)
{}

static void netvsc_get_channels(struct net_device *net,
				struct ethtool_channels *channel)
{}

/* Alloc struct netvsc_device_info, and initialize it from either existing
 * struct netvsc_device, or from default values.
 */
static
struct netvsc_device_info *netvsc_devinfo_get(struct netvsc_device *nvdev)
{}

/* Free struct netvsc_device_info */
static void netvsc_devinfo_put(struct netvsc_device_info *dev_info)
{}

static int netvsc_detach(struct net_device *ndev,
			 struct netvsc_device *nvdev)
{}

static int netvsc_attach(struct net_device *ndev,
			 struct netvsc_device_info *dev_info)
{}

static int netvsc_set_channels(struct net_device *net,
			       struct ethtool_channels *channels)
{}

static void netvsc_init_settings(struct net_device *dev)
{}

static int netvsc_get_link_ksettings(struct net_device *dev,
				     struct ethtool_link_ksettings *cmd)
{}

static int netvsc_set_link_ksettings(struct net_device *dev,
				     const struct ethtool_link_ksettings *cmd)
{}

static int netvsc_change_mtu(struct net_device *ndev, int mtu)
{}

static void netvsc_get_vf_stats(struct net_device *net,
				struct netvsc_vf_pcpu_stats *tot)
{}

static void netvsc_get_pcpu_stats(struct net_device *net,
				  struct netvsc_ethtool_pcpu_stats *pcpu_tot)
{}

static void netvsc_get_stats64(struct net_device *net,
			       struct rtnl_link_stats64 *t)
{}

static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
{}

static const struct {} netvsc_stats[] =, pcpu_stats[] =, vf_stats[] =;

#define NETVSC_GLOBAL_STATS_LEN
#define NETVSC_VF_STATS_LEN

/* statistics per queue (rx/tx packets/bytes) */
#define NETVSC_PCPU_STATS_LEN

/* 8 statistics per queue (rx/tx packets/bytes, XDP actions) */
#define NETVSC_QUEUE_STATS_LEN(dev)

static int netvsc_get_sset_count(struct net_device *dev, int string_set)
{}

static void netvsc_get_ethtool_stats(struct net_device *dev,
				     struct ethtool_stats *stats, u64 *data)
{}

static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{}

static int
netvsc_get_rss_hash_opts(struct net_device_context *ndc,
			 struct ethtool_rxnfc *info)
{}

static int
netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
		 u32 *rules)
{}

static int netvsc_set_rss_hash_opts(struct net_device_context *ndc,
				    struct ethtool_rxnfc *info)
{}

static int
netvsc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *info)
{}

static u32 netvsc_get_rxfh_key_size(struct net_device *dev)
{}

static u32 netvsc_rss_indir_size(struct net_device *dev)
{}

static int netvsc_get_rxfh(struct net_device *dev,
			   struct ethtool_rxfh_param *rxfh)
{}

static int netvsc_set_rxfh(struct net_device *dev,
			   struct ethtool_rxfh_param *rxfh,
			   struct netlink_ext_ack *extack)
{}

/* Hyper-V RNDIS protocol does not have ring in the HW sense.
 * It does have pre-allocated receive area which is divided into sections.
 */
static void __netvsc_get_ringparam(struct netvsc_device *nvdev,
				   struct ethtool_ringparam *ring)
{}

static void netvsc_get_ringparam(struct net_device *ndev,
				 struct ethtool_ringparam *ring,
				 struct kernel_ethtool_ringparam *kernel_ring,
				 struct netlink_ext_ack *extack)
{}

static int netvsc_set_ringparam(struct net_device *ndev,
				struct ethtool_ringparam *ring,
				struct kernel_ethtool_ringparam *kernel_ring,
				struct netlink_ext_ack *extack)
{}

static netdev_features_t netvsc_fix_features(struct net_device *ndev,
					     netdev_features_t features)
{}

static int netvsc_set_features(struct net_device *ndev,
			       netdev_features_t features)
{}

static int netvsc_get_regs_len(struct net_device *netdev)
{}

static void netvsc_get_regs(struct net_device *netdev,
			    struct ethtool_regs *regs, void *p)
{}

static u32 netvsc_get_msglevel(struct net_device *ndev)
{}

static void netvsc_set_msglevel(struct net_device *ndev, u32 val)
{}

static const struct ethtool_ops ethtool_ops =;

static const struct net_device_ops device_ops =;

/*
 * Handle link status changes. For RNDIS_STATUS_NETWORK_CHANGE emulate link
 * down/up sequence. In case of RNDIS_STATUS_MEDIA_CONNECT when carrier is
 * present send GARP packet to network peers with netif_notify_peers().
 */
static void netvsc_link_change(struct work_struct *w)
{}

static struct net_device *get_netvsc_byref(struct net_device *vf_netdev)
{}

/* Called when VF is injecting data into network stack.
 * Change the associated network device from VF to netvsc.
 * note: already called with rcu_read_lock
 */
static rx_handler_result_t netvsc_vf_handle_frame(struct sk_buff **pskb)
{}

static int netvsc_vf_join(struct net_device *vf_netdev,
			  struct net_device *ndev, int context)
{}

static void __netvsc_vf_setup(struct net_device *ndev,
			      struct net_device *vf_netdev)
{}

/* Setup VF as slave of the synthetic device.
 * Runs in workqueue to avoid recursion in netlink callbacks.
 */
static void netvsc_vf_setup(struct work_struct *w)
{}

/* Find netvsc by VF serial number.
 * The PCI hyperv controller records the serial number as the slot kobj name.
 */
static struct net_device *get_netvsc_byslot(const struct net_device *vf_netdev)
{}

static int netvsc_prepare_bonding(struct net_device *vf_netdev)
{}

static int netvsc_register_vf(struct net_device *vf_netdev, int context)
{}

/* Change the data path when VF UP/DOWN/CHANGE are detected.
 *
 * Typically a UP or DOWN event is followed by a CHANGE event, so
 * net_device_ctx->data_path_is_vf is used to cache the current data path
 * to avoid the duplicate call of netvsc_switch_datapath() and the duplicate
 * message.
 *
 * During hibernation, if a VF NIC driver (e.g. mlx5) preserves the network
 * interface, there is only the CHANGE event and no UP or DOWN event.
 */
static int netvsc_vf_changed(struct net_device *vf_netdev, unsigned long event)
{}

static int netvsc_unregister_vf(struct net_device *vf_netdev)
{}

static int check_dev_is_matching_vf(struct net_device *event_ndev)
{}

static int netvsc_probe(struct hv_device *dev,
			const struct hv_vmbus_device_id *dev_id)
{}

static void netvsc_remove(struct hv_device *dev)
{}

static int netvsc_suspend(struct hv_device *dev)
{}

static int netvsc_resume(struct hv_device *dev)
{}
static const struct hv_vmbus_device_id id_table[] =;

MODULE_DEVICE_TABLE(vmbus, id_table);

/* The one and only one */
static struct  hv_driver netvsc_drv =;

/*
 * On Hyper-V, every VF interface is matched with a corresponding
 * synthetic interface. The synthetic interface is presented first
 * to the guest. When the corresponding VF instance is registered,
 * we will take care of switching the data path.
 */
static int netvsc_netdev_event(struct notifier_block *this,
			       unsigned long event, void *ptr)
{}

static struct notifier_block netvsc_netdev_notifier =;

static void __exit netvsc_drv_exit(void)
{}

static int __init netvsc_drv_init(void)
{}

MODULE_LICENSE();
MODULE_DESCRIPTION();

module_init();
module_exit(netvsc_drv_exit);