diff options
author | Haiyang Zhang <haiyangz@microsoft.com> | 2011-12-15 16:45:17 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-04 19:13:05 -0500 |
commit | 4d447c9a6ebc0142d320f075c5bac6d202a79fd4 (patch) | |
tree | 6eb2a1a17e6de57dba5d350fc2821e2f48a8a484 /drivers | |
parent | f157e78de5923dfb209355f3005ce1b5d64f7998 (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.h | 8 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 6 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 70 |
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 | ||
320 | static 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 | |||
303 | static const struct ethtool_ops ethtool_ops = { | 353 | static 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 | ||