Skip to content

Commit

Permalink
net/hyperv: Add support for jumbo frame up to 64KB
Browse files Browse the repository at this point in the history
Allow the user set the MTU up to 65536 for Linux guests running on
Hyper-V 2008 R2 or later.

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Haiyang Zhang authored and Greg Kroah-Hartman committed Jan 5, 2012
1 parent f157e78 commit 4d447c9
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 18 deletions.
8 changes: 3 additions & 5 deletions drivers/net/hyperv/hyperv_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,9 @@ struct nvsp_message {
} __packed;


#define NETVSC_MTU 65536


/* #define NVSC_MIN_PROTOCOL_VERSION 1 */
/* #define NVSC_MAX_PROTOCOL_VERSION 1 */

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

#define NETVSC_RECEIVE_BUFFER_ID 0xcafe

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

atomic_t num_outstanding_sends;
bool start_remove;
bool destroy;
/*
* List of free preallocated hv_netvsc_packet to represent receive
Expand Down
6 changes: 3 additions & 3 deletions drivers/net/hyperv/netvsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
if (!net_device)
return NULL;


net_device->start_remove = false;
net_device->destroy = false;
net_device->dev = device;
net_device->ndev = ndev;
Expand Down Expand Up @@ -299,7 +299,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
/* NVSPv2 only: Send NDIS config */
memset(init_packet, 0, sizeof(struct nvsp_message));
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,
sizeof(struct nvsp_message),
Expand Down Expand Up @@ -464,7 +464,7 @@ static void netvsc_send_completion(struct hv_device *device,

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);
} else {
netdev_err(ndev, "Unknown send completion packet type- "
Expand Down
70 changes: 61 additions & 9 deletions drivers/net/hyperv/netvsc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 hv_netvsc_packet *packet;
int ret;
unsigned int i, num_pages;
unsigned int i, num_pages, npg_data;

/* Add 1 for skb->data and additional one for RNDIS */
num_pages = skb_shinfo(skb)->nr_frags + 1 + 1;
/* Add multipage for skb->data and additional one for RNDIS */
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. */
packet = kzalloc(sizeof(struct hv_netvsc_packet) +
Expand All @@ -174,21 +176,36 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
packet->page_buf_cnt = num_pages;

/* Initialize it from the skb */
packet->total_data_buflen = skb->len;
packet->total_data_buflen = skb->len;

/* Start filling in the page buffers starting after RNDIS buffer. */
packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
packet->page_buf[1].offset
= (unsigned long)skb->data & (PAGE_SIZE - 1);
packet->page_buf[1].len = skb_headlen(skb);
if (npg_data == 1)
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 */
for (i = 0; i < skb_shinfo(skb)->nr_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+2].offset = f->page_offset;
packet->page_buf[i+2].len = skb_frag_size(f);
packet->page_buf[i+npg_data+1].pfn =
page_to_pfn(skb_frag_page(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 */
Expand Down Expand Up @@ -300,6 +317,39 @@ static void netvsc_get_drvinfo(struct net_device *net,
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 = {
.get_drvinfo = netvsc_get_drvinfo,
.get_link = ethtool_op_get_link,
Expand All @@ -310,7 +360,7 @@ static const struct net_device_ops device_ops = {
.ndo_stop = netvsc_close,
.ndo_start_xmit = netvsc_start_xmit,
.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_set_mac_address = eth_mac_addr,
};
Expand Down Expand Up @@ -403,6 +453,8 @@ static int netvsc_remove(struct hv_device *dev)
return 0;
}

net_device->start_remove = true;

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

Expand Down
2 changes: 1 addition & 1 deletion include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#include <linux/mod_devicetable.h>


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

#pragma pack(push, 1)
Expand Down

0 comments on commit 4d447c9

Please sign in to comment.