aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHaiyang Zhang <haiyangz@microsoft.com>2011-12-15 16:45:17 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2012-01-04 19:13:05 -0500
commit4d447c9a6ebc0142d320f075c5bac6d202a79fd4 (patch)
tree6eb2a1a17e6de57dba5d350fc2821e2f48a8a484 /drivers
parentf157e78de5923dfb209355f3005ce1b5d64f7998 (diff)
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: Haiyang Zhang <haiyangz@microsoft.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/hyperv/hyperv_net.h8
-rw-r--r--drivers/net/hyperv/netvsc.c6
-rw-r--r--drivers/net/hyperv/netvsc_drv.c70
3 files changed, 67 insertions, 17 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 287767055125..dec5836ae075 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -456,12 +456,9 @@ struct nvsp_message {
456} __packed; 456} __packed;
457 457
458 458
459#define NETVSC_MTU 65536
459 460
460 461#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*2) /* 2MB */
461/* #define NVSC_MIN_PROTOCOL_VERSION 1 */
462/* #define NVSC_MAX_PROTOCOL_VERSION 1 */
463
464#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */
465 462
466#define NETVSC_RECEIVE_BUFFER_ID 0xcafe 463#define NETVSC_RECEIVE_BUFFER_ID 0xcafe
467 464
@@ -479,6 +476,7 @@ struct netvsc_device {
479 u32 nvsp_version; 476 u32 nvsp_version;
480 477
481 atomic_t num_outstanding_sends; 478 atomic_t num_outstanding_sends;
479 bool start_remove;
482 bool destroy; 480 bool destroy;
483 /* 481 /*
484 * List of free preallocated hv_netvsc_packet to represent receive 482 * List of free preallocated hv_netvsc_packet to represent receive
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 46828b4dd8ab..8965b45ce5a5 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -42,7 +42,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
42 if (!net_device) 42 if (!net_device)
43 return NULL; 43 return NULL;
44 44
45 45 net_device->start_remove = false;
46 net_device->destroy = false; 46 net_device->destroy = false;
47 net_device->dev = device; 47 net_device->dev = device;
48 net_device->ndev = ndev; 48 net_device->ndev = ndev;
@@ -299,7 +299,7 @@ static int negotiate_nvsp_ver(struct hv_device *device,
299 /* NVSPv2 only: Send NDIS config */ 299 /* NVSPv2 only: Send NDIS config */
300 memset(init_packet, 0, sizeof(struct nvsp_message)); 300 memset(init_packet, 0, sizeof(struct nvsp_message));
301 init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; 301 init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
302 init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN; 302 init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu;
303 303
304 ret = vmbus_sendpacket(device->channel, init_packet, 304 ret = vmbus_sendpacket(device->channel, init_packet,
305 sizeof(struct nvsp_message), 305 sizeof(struct nvsp_message),
@@ -464,7 +464,7 @@ static void netvsc_send_completion(struct hv_device *device,
464 464
465 atomic_dec(&net_device->num_outstanding_sends); 465 atomic_dec(&net_device->num_outstanding_sends);
466 466
467 if (netif_queue_stopped(ndev)) 467 if (netif_queue_stopped(ndev) && !net_device->start_remove)
468 netif_wake_queue(ndev); 468 netif_wake_queue(ndev);
469 } else { 469 } else {
470 netdev_err(ndev, "Unknown send completion packet type- " 470 netdev_err(ndev, "Unknown send completion packet type- "
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index b7cbd126f20a..462d05f05e84 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -148,10 +148,12 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
148 struct net_device_context *net_device_ctx = netdev_priv(net); 148 struct net_device_context *net_device_ctx = netdev_priv(net);
149 struct hv_netvsc_packet *packet; 149 struct hv_netvsc_packet *packet;
150 int ret; 150 int ret;
151 unsigned int i, num_pages; 151 unsigned int i, num_pages, npg_data;
152 152
153 /* Add 1 for skb->data and additional one for RNDIS */ 153 /* Add multipage for skb->data and additional one for RNDIS */
154 num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; 154 npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1)
155 >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1;
156 num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1;
155 157
156 /* Allocate a netvsc packet based on # of frags. */ 158 /* Allocate a netvsc packet based on # of frags. */
157 packet = kzalloc(sizeof(struct hv_netvsc_packet) + 159 packet = kzalloc(sizeof(struct hv_netvsc_packet) +
@@ -174,21 +176,36 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
174 packet->page_buf_cnt = num_pages; 176 packet->page_buf_cnt = num_pages;
175 177
176 /* Initialize it from the skb */ 178 /* Initialize it from the skb */
177 packet->total_data_buflen = skb->len; 179 packet->total_data_buflen = skb->len;
178 180
179 /* Start filling in the page buffers starting after RNDIS buffer. */ 181 /* Start filling in the page buffers starting after RNDIS buffer. */
180 packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; 182 packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
181 packet->page_buf[1].offset 183 packet->page_buf[1].offset
182 = (unsigned long)skb->data & (PAGE_SIZE - 1); 184 = (unsigned long)skb->data & (PAGE_SIZE - 1);
183 packet->page_buf[1].len = skb_headlen(skb); 185 if (npg_data == 1)
186 packet->page_buf[1].len = skb_headlen(skb);
187 else
188 packet->page_buf[1].len = PAGE_SIZE
189 - packet->page_buf[1].offset;
190
191 for (i = 2; i <= npg_data; i++) {
192 packet->page_buf[i].pfn = virt_to_phys(skb->data
193 + PAGE_SIZE * (i-1)) >> PAGE_SHIFT;
194 packet->page_buf[i].offset = 0;
195 packet->page_buf[i].len = PAGE_SIZE;
196 }
197 if (npg_data > 1)
198 packet->page_buf[npg_data].len = (((unsigned long)skb->data
199 + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1;
184 200
185 /* Additional fragments are after SKB data */ 201 /* Additional fragments are after SKB data */
186 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 202 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
187 const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; 203 const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
188 204
189 packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f)); 205 packet->page_buf[i+npg_data+1].pfn =
190 packet->page_buf[i+2].offset = f->page_offset; 206 page_to_pfn(skb_frag_page(f));
191 packet->page_buf[i+2].len = skb_frag_size(f); 207 packet->page_buf[i+npg_data+1].offset = f->page_offset;
208 packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
192 } 209 }
193 210
194 /* Set the completion routine */ 211 /* Set the completion routine */
@@ -300,6 +317,39 @@ static void netvsc_get_drvinfo(struct net_device *net,
300 strcpy(info->fw_version, "N/A"); 317 strcpy(info->fw_version, "N/A");
301} 318}
302 319
320static int netvsc_change_mtu(struct net_device *ndev, int mtu)
321{
322 struct net_device_context *ndevctx = netdev_priv(ndev);
323 struct hv_device *hdev = ndevctx->device_ctx;
324 struct netvsc_device *nvdev = hv_get_drvdata(hdev);
325 struct netvsc_device_info device_info;
326 int limit = ETH_DATA_LEN;
327
328 if (nvdev == NULL || nvdev->destroy)
329 return -ENODEV;
330
331 if (nvdev->nvsp_version == NVSP_PROTOCOL_VERSION_2)
332 limit = NETVSC_MTU;
333
334 if (mtu < 68 || mtu > limit)
335 return -EINVAL;
336
337 nvdev->start_remove = true;
338 cancel_delayed_work_sync(&ndevctx->dwork);
339 netif_stop_queue(ndev);
340 rndis_filter_device_remove(hdev);
341
342 ndev->mtu = mtu;
343
344 ndevctx->device_ctx = hdev;
345 hv_set_drvdata(hdev, ndev);
346 device_info.ring_size = ring_size;
347 rndis_filter_device_add(hdev, &device_info);
348 netif_wake_queue(ndev);
349
350 return 0;
351}
352
303static const struct ethtool_ops ethtool_ops = { 353static const struct ethtool_ops ethtool_ops = {
304 .get_drvinfo = netvsc_get_drvinfo, 354 .get_drvinfo = netvsc_get_drvinfo,
305 .get_link = ethtool_op_get_link, 355 .get_link = ethtool_op_get_link,
@@ -310,7 +360,7 @@ static const struct net_device_ops device_ops = {
310 .ndo_stop = netvsc_close, 360 .ndo_stop = netvsc_close,
311 .ndo_start_xmit = netvsc_start_xmit, 361 .ndo_start_xmit = netvsc_start_xmit,
312 .ndo_set_rx_mode = netvsc_set_multicast_list, 362 .ndo_set_rx_mode = netvsc_set_multicast_list,
313 .ndo_change_mtu = eth_change_mtu, 363 .ndo_change_mtu = netvsc_change_mtu,
314 .ndo_validate_addr = eth_validate_addr, 364 .ndo_validate_addr = eth_validate_addr,
315 .ndo_set_mac_address = eth_mac_addr, 365 .ndo_set_mac_address = eth_mac_addr,
316}; 366};
@@ -403,6 +453,8 @@ static int netvsc_remove(struct hv_device *dev)
403 return 0; 453 return 0;
404 } 454 }
405 455
456 net_device->start_remove = true;
457
406 ndev_ctx = netdev_priv(net); 458 ndev_ctx = netdev_priv(net);
407 cancel_delayed_work_sync(&ndev_ctx->dwork); 459 cancel_delayed_work_sync(&ndev_ctx->dwork);
408 460