Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d447c9a authored by Haiyang Zhang's avatar Haiyang Zhang Committed by Greg Kroah-Hartman
Browse files

net/hyperv: Add support for jumbo frame up to 64KB



Allow the user set the MTU up to 65536 for Linux guests running on
Hyper-V 2008 R2 or later.

Signed-off-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent f157e78d
Loading
Loading
Loading
Loading
+3 −5
Original line number Original line Diff line number Diff line
@@ -456,12 +456,9 @@ struct nvsp_message {
} __packed;
} __packed;




#define NETVSC_MTU 65536



#define NETVSC_RECEIVE_BUFFER_SIZE		(1024*1024*2)	/* 2MB */
/* #define NVSC_MIN_PROTOCOL_VERSION		1 */
/* #define NVSC_MAX_PROTOCOL_VERSION		1 */

#define NETVSC_RECEIVE_BUFFER_SIZE		(1024*1024)	/* 1MB */


#define NETVSC_RECEIVE_BUFFER_ID		0xcafe
#define NETVSC_RECEIVE_BUFFER_ID		0xcafe


@@ -479,6 +476,7 @@ struct netvsc_device {
	u32 nvsp_version;
	u32 nvsp_version;


	atomic_t num_outstanding_sends;
	atomic_t num_outstanding_sends;
	bool start_remove;
	bool destroy;
	bool destroy;
	/*
	/*
	 * List of free preallocated hv_netvsc_packet to represent receive
	 * List of free preallocated hv_netvsc_packet to represent receive
+3 −3
Original line number Original line Diff line number Diff line
@@ -42,7 +42,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
	if (!net_device)
	if (!net_device)
		return NULL;
		return NULL;



	net_device->start_remove = false;
	net_device->destroy = false;
	net_device->destroy = false;
	net_device->dev = device;
	net_device->dev = device;
	net_device->ndev = ndev;
	net_device->ndev = ndev;
@@ -299,7 +299,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
	/* NVSPv2 only: Send NDIS config */
	/* NVSPv2 only: Send NDIS config */
	memset(init_packet, 0, sizeof(struct nvsp_message));
	memset(init_packet, 0, sizeof(struct nvsp_message));
	init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
	init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
	init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN;
	init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu;


	ret = vmbus_sendpacket(device->channel, init_packet,
	ret = vmbus_sendpacket(device->channel, init_packet,
				sizeof(struct nvsp_message),
				sizeof(struct nvsp_message),
@@ -464,7 +464,7 @@ static void netvsc_send_completion(struct hv_device *device,


		atomic_dec(&net_device->num_outstanding_sends);
		atomic_dec(&net_device->num_outstanding_sends);


		if (netif_queue_stopped(ndev))
		if (netif_queue_stopped(ndev) && !net_device->start_remove)
			netif_wake_queue(ndev);
			netif_wake_queue(ndev);
	} else {
	} else {
		netdev_err(ndev, "Unknown send completion packet type- "
		netdev_err(ndev, "Unknown send completion packet type- "
+61 −9
Original line number Original line Diff line number Diff line
@@ -148,10 +148,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
	struct net_device_context *net_device_ctx = netdev_priv(net);
	struct net_device_context *net_device_ctx = netdev_priv(net);
	struct hv_netvsc_packet *packet;
	struct hv_netvsc_packet *packet;
	int ret;
	int ret;
	unsigned int i, num_pages;
	unsigned int i, num_pages, npg_data;


	/* Add 1 for skb->data and additional one for RNDIS */
	/* Add multipage for skb->data and additional one for RNDIS */
	num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
	npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1)
		>> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1;
	num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1;


	/* Allocate a netvsc packet based on # of frags. */
	/* Allocate a netvsc packet based on # of frags. */
	packet = kzalloc(sizeof(struct hv_netvsc_packet) +
	packet = kzalloc(sizeof(struct hv_netvsc_packet) +
@@ -180,15 +182,30 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
	packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
	packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
	packet->page_buf[1].offset
	packet->page_buf[1].offset
		= (unsigned long)skb->data & (PAGE_SIZE - 1);
		= (unsigned long)skb->data & (PAGE_SIZE - 1);
	if (npg_data == 1)
		packet->page_buf[1].len = skb_headlen(skb);
		packet->page_buf[1].len = skb_headlen(skb);
	else
		packet->page_buf[1].len = PAGE_SIZE
			- packet->page_buf[1].offset;

	for (i = 2; i <= npg_data; i++) {
		packet->page_buf[i].pfn = virt_to_phys(skb->data
			+ PAGE_SIZE * (i-1)) >> PAGE_SHIFT;
		packet->page_buf[i].offset = 0;
		packet->page_buf[i].len = PAGE_SIZE;
	}
	if (npg_data > 1)
		packet->page_buf[npg_data].len = (((unsigned long)skb->data
			+ skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1;


	/* Additional fragments are after SKB data */
	/* Additional fragments are after SKB data */
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
		const skb_frag_t *f = &skb_shinfo(skb)->frags[i];


		packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f));
		packet->page_buf[i+npg_data+1].pfn =
		packet->page_buf[i+2].offset = f->page_offset;
			page_to_pfn(skb_frag_page(f));
		packet->page_buf[i+2].len = skb_frag_size(f);
		packet->page_buf[i+npg_data+1].offset = f->page_offset;
		packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
	}
	}


	/* Set the completion routine */
	/* Set the completion routine */
@@ -300,6 +317,39 @@ static void netvsc_get_drvinfo(struct net_device *net,
	strcpy(info->fw_version, "N/A");
	strcpy(info->fw_version, "N/A");
}
}


static int netvsc_change_mtu(struct net_device *ndev, int mtu)
{
	struct net_device_context *ndevctx = netdev_priv(ndev);
	struct hv_device *hdev =  ndevctx->device_ctx;
	struct netvsc_device *nvdev = hv_get_drvdata(hdev);
	struct netvsc_device_info device_info;
	int limit = ETH_DATA_LEN;

	if (nvdev == NULL || nvdev->destroy)
		return -ENODEV;

	if (nvdev->nvsp_version == NVSP_PROTOCOL_VERSION_2)
		limit = NETVSC_MTU;

	if (mtu < 68 || mtu > limit)
		return -EINVAL;

	nvdev->start_remove = true;
	cancel_delayed_work_sync(&ndevctx->dwork);
	netif_stop_queue(ndev);
	rndis_filter_device_remove(hdev);

	ndev->mtu = mtu;

	ndevctx->device_ctx = hdev;
	hv_set_drvdata(hdev, ndev);
	device_info.ring_size = ring_size;
	rndis_filter_device_add(hdev, &device_info);
	netif_wake_queue(ndev);

	return 0;
}

static const struct ethtool_ops ethtool_ops = {
static const struct ethtool_ops ethtool_ops = {
	.get_drvinfo	= netvsc_get_drvinfo,
	.get_drvinfo	= netvsc_get_drvinfo,
	.get_link	= ethtool_op_get_link,
	.get_link	= ethtool_op_get_link,
@@ -310,7 +360,7 @@ static const struct net_device_ops device_ops = {
	.ndo_stop =			netvsc_close,
	.ndo_stop =			netvsc_close,
	.ndo_start_xmit =		netvsc_start_xmit,
	.ndo_start_xmit =		netvsc_start_xmit,
	.ndo_set_rx_mode =		netvsc_set_multicast_list,
	.ndo_set_rx_mode =		netvsc_set_multicast_list,
	.ndo_change_mtu =		eth_change_mtu,
	.ndo_change_mtu =		netvsc_change_mtu,
	.ndo_validate_addr =		eth_validate_addr,
	.ndo_validate_addr =		eth_validate_addr,
	.ndo_set_mac_address =		eth_mac_addr,
	.ndo_set_mac_address =		eth_mac_addr,
};
};
@@ -403,6 +453,8 @@ static int netvsc_remove(struct hv_device *dev)
		return 0;
		return 0;
	}
	}


	net_device->start_remove = true;

	ndev_ctx = netdev_priv(net);
	ndev_ctx = netdev_priv(net);
	cancel_delayed_work_sync(&ndev_ctx->dwork);
	cancel_delayed_work_sync(&ndev_ctx->dwork);


+1 −1
Original line number Original line Diff line number Diff line
@@ -35,7 +35,7 @@
#include <linux/mod_devicetable.h>
#include <linux/mod_devicetable.h>




#define MAX_PAGE_BUFFER_COUNT				16
#define MAX_PAGE_BUFFER_COUNT				18
#define MAX_MULTIPAGE_BUFFER_COUNT			32 /* 128K */
#define MAX_MULTIPAGE_BUFFER_COUNT			32 /* 128K */


#pragma pack(push, 1)
#pragma pack(push, 1)