Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Haiyang Zhang and committed by
Greg Kroah-Hartman
4d447c9a f157e78d

+68 -18
+3 -5
drivers/net/hyperv/hyperv_net.h
··· 456 456 } __packed; 457 457 458 458 459 + #define NETVSC_MTU 65536 459 460 460 - 461 - /* #define NVSC_MIN_PROTOCOL_VERSION 1 */ 462 - /* #define NVSC_MAX_PROTOCOL_VERSION 1 */ 463 - 464 - #define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024) /* 1MB */ 461 + #define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*2) /* 2MB */ 465 462 466 463 #define NETVSC_RECEIVE_BUFFER_ID 0xcafe 467 464 ··· 476 479 u32 nvsp_version; 477 480 478 481 atomic_t num_outstanding_sends; 482 + bool start_remove; 479 483 bool destroy; 480 484 /* 481 485 * List of free preallocated hv_netvsc_packet to represent receive
+3 -3
drivers/net/hyperv/netvsc.c
··· 42 42 if (!net_device) 43 43 return NULL; 44 44 45 - 45 + net_device->start_remove = false; 46 46 net_device->destroy = false; 47 47 net_device->dev = device; 48 48 net_device->ndev = ndev; ··· 299 299 /* NVSPv2 only: Send NDIS config */ 300 300 memset(init_packet, 0, sizeof(struct nvsp_message)); 301 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 304 ret = vmbus_sendpacket(device->channel, init_packet, 305 305 sizeof(struct nvsp_message), ··· 464 464 465 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 468 netif_wake_queue(ndev); 469 469 } else { 470 470 netdev_err(ndev, "Unknown send completion packet type- "
+61 -9
drivers/net/hyperv/netvsc_drv.c
··· 148 148 struct net_device_context *net_device_ctx = netdev_priv(net); 149 149 struct hv_netvsc_packet *packet; 150 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 */ 154 - num_pages = skb_shinfo(skb)->nr_frags + 1 + 1; 153 + /* Add multipage for skb->data and additional one for RNDIS */ 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 158 /* Allocate a netvsc packet based on # of frags. */ 157 159 packet = kzalloc(sizeof(struct hv_netvsc_packet) + ··· 176 174 packet->page_buf_cnt = num_pages; 177 175 178 176 /* Initialize it from the skb */ 179 - packet->total_data_buflen = skb->len; 177 + packet->total_data_buflen = skb->len; 180 178 181 179 /* Start filling in the page buffers starting after RNDIS buffer. */ 182 180 packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; 183 181 packet->page_buf[1].offset 184 182 = (unsigned long)skb->data & (PAGE_SIZE - 1); 185 - packet->page_buf[1].len = skb_headlen(skb); 183 + if (npg_data == 1) 184 + packet->page_buf[1].len = skb_headlen(skb); 185 + else 186 + packet->page_buf[1].len = PAGE_SIZE 187 + - packet->page_buf[1].offset; 188 + 189 + for (i = 2; i <= npg_data; i++) { 190 + packet->page_buf[i].pfn = virt_to_phys(skb->data 191 + + PAGE_SIZE * (i-1)) >> PAGE_SHIFT; 192 + packet->page_buf[i].offset = 0; 193 + packet->page_buf[i].len = PAGE_SIZE; 194 + } 195 + if (npg_data > 1) 196 + packet->page_buf[npg_data].len = (((unsigned long)skb->data 197 + + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1; 186 198 187 199 /* Additional fragments are after SKB data */ 188 200 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 189 201 const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; 190 202 191 - packet->page_buf[i+2].pfn = page_to_pfn(skb_frag_page(f)); 192 - packet->page_buf[i+2].offset = f->page_offset; 193 - packet->page_buf[i+2].len = skb_frag_size(f); 203 + packet->page_buf[i+npg_data+1].pfn = 204 + page_to_pfn(skb_frag_page(f)); 205 + packet->page_buf[i+npg_data+1].offset = f->page_offset; 206 + packet->page_buf[i+npg_data+1].len = skb_frag_size(f); 194 207 } 195 208 196 209 /* Set the completion routine */ ··· 317 300 strcpy(info->fw_version, "N/A"); 318 301 } 319 302 303 + static int netvsc_change_mtu(struct net_device *ndev, int mtu) 304 + { 305 + struct net_device_context *ndevctx = netdev_priv(ndev); 306 + struct hv_device *hdev = ndevctx->device_ctx; 307 + struct netvsc_device *nvdev = hv_get_drvdata(hdev); 308 + struct netvsc_device_info device_info; 309 + int limit = ETH_DATA_LEN; 310 + 311 + if (nvdev == NULL || nvdev->destroy) 312 + return -ENODEV; 313 + 314 + if (nvdev->nvsp_version == NVSP_PROTOCOL_VERSION_2) 315 + limit = NETVSC_MTU; 316 + 317 + if (mtu < 68 || mtu > limit) 318 + return -EINVAL; 319 + 320 + nvdev->start_remove = true; 321 + cancel_delayed_work_sync(&ndevctx->dwork); 322 + netif_stop_queue(ndev); 323 + rndis_filter_device_remove(hdev); 324 + 325 + ndev->mtu = mtu; 326 + 327 + ndevctx->device_ctx = hdev; 328 + hv_set_drvdata(hdev, ndev); 329 + device_info.ring_size = ring_size; 330 + rndis_filter_device_add(hdev, &device_info); 331 + netif_wake_queue(ndev); 332 + 333 + return 0; 334 + } 335 + 320 336 static const struct ethtool_ops ethtool_ops = { 321 337 .get_drvinfo = netvsc_get_drvinfo, 322 338 .get_link = ethtool_op_get_link, ··· 360 310 .ndo_stop = netvsc_close, 361 311 .ndo_start_xmit = netvsc_start_xmit, 362 312 .ndo_set_rx_mode = netvsc_set_multicast_list, 363 - .ndo_change_mtu = eth_change_mtu, 313 + .ndo_change_mtu = netvsc_change_mtu, 364 314 .ndo_validate_addr = eth_validate_addr, 365 315 .ndo_set_mac_address = eth_mac_addr, 366 316 }; ··· 452 402 dev_err(&dev->device, "No net device to remove\n"); 453 403 return 0; 454 404 } 405 + 406 + net_device->start_remove = true; 455 407 456 408 ndev_ctx = netdev_priv(net); 457 409 cancel_delayed_work_sync(&ndev_ctx->dwork);
+1 -1
include/linux/hyperv.h
··· 35 35 #include <linux/mod_devicetable.h> 36 36 37 37 38 - #define MAX_PAGE_BUFFER_COUNT 16 38 + #define MAX_PAGE_BUFFER_COUNT 18 39 39 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ 40 40 41 41 #pragma pack(push, 1)