diff options
| author | Haiyang Zhang <haiyangz@microsoft.com> | 2014-04-21 13:20:28 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-04-21 13:32:29 -0400 |
| commit | 5b54dac856cb5bd6f33f4159012773e4a33704f7 (patch) | |
| tree | b3aa8725ecf1605616632c305d76a3877969d685 /drivers/net/hyperv | |
| parent | 86fd14ad1e8c4b8f5e9a7a27b26bdade91dd4bd0 (diff) | |
hyperv: Add support for virtual Receive Side Scaling (vRSS)
This feature allows multiple channels to be used by each virtual NIC.
It is available on Hyper-V host 2012 R2.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv')
| -rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 110 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc.c | 136 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 103 | ||||
| -rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 189 |
4 files changed, 504 insertions, 34 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index d18f711d0b0c..57eb3f906d64 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
| @@ -28,6 +28,96 @@ | |||
| 28 | #include <linux/hyperv.h> | 28 | #include <linux/hyperv.h> |
| 29 | #include <linux/rndis.h> | 29 | #include <linux/rndis.h> |
| 30 | 30 | ||
| 31 | /* RSS related */ | ||
| 32 | #define OID_GEN_RECEIVE_SCALE_CAPABILITIES 0x00010203 /* query only */ | ||
| 33 | #define OID_GEN_RECEIVE_SCALE_PARAMETERS 0x00010204 /* query and set */ | ||
| 34 | |||
| 35 | #define NDIS_OBJECT_TYPE_RSS_CAPABILITIES 0x88 | ||
| 36 | #define NDIS_OBJECT_TYPE_RSS_PARAMETERS 0x89 | ||
| 37 | |||
| 38 | #define NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2 2 | ||
| 39 | #define NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2 2 | ||
| 40 | |||
| 41 | struct ndis_obj_header { | ||
| 42 | u8 type; | ||
| 43 | u8 rev; | ||
| 44 | u16 size; | ||
| 45 | } __packed; | ||
| 46 | |||
| 47 | /* ndis_recv_scale_cap/cap_flag */ | ||
| 48 | #define NDIS_RSS_CAPS_MESSAGE_SIGNALED_INTERRUPTS 0x01000000 | ||
| 49 | #define NDIS_RSS_CAPS_CLASSIFICATION_AT_ISR 0x02000000 | ||
| 50 | #define NDIS_RSS_CAPS_CLASSIFICATION_AT_DPC 0x04000000 | ||
| 51 | #define NDIS_RSS_CAPS_USING_MSI_X 0x08000000 | ||
| 52 | #define NDIS_RSS_CAPS_RSS_AVAILABLE_ON_PORTS 0x10000000 | ||
| 53 | #define NDIS_RSS_CAPS_SUPPORTS_MSI_X 0x20000000 | ||
| 54 | #define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV4 0x00000100 | ||
| 55 | #define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6 0x00000200 | ||
| 56 | #define NDIS_RSS_CAPS_HASH_TYPE_TCP_IPV6_EX 0x00000400 | ||
| 57 | |||
| 58 | struct ndis_recv_scale_cap { /* NDIS_RECEIVE_SCALE_CAPABILITIES */ | ||
| 59 | struct ndis_obj_header hdr; | ||
| 60 | u32 cap_flag; | ||
| 61 | u32 num_int_msg; | ||
| 62 | u32 num_recv_que; | ||
| 63 | u16 num_indirect_tabent; | ||
| 64 | } __packed; | ||
| 65 | |||
| 66 | |||
| 67 | /* ndis_recv_scale_param flags */ | ||
| 68 | #define NDIS_RSS_PARAM_FLAG_BASE_CPU_UNCHANGED 0x0001 | ||
| 69 | #define NDIS_RSS_PARAM_FLAG_HASH_INFO_UNCHANGED 0x0002 | ||
| 70 | #define NDIS_RSS_PARAM_FLAG_ITABLE_UNCHANGED 0x0004 | ||
| 71 | #define NDIS_RSS_PARAM_FLAG_HASH_KEY_UNCHANGED 0x0008 | ||
| 72 | #define NDIS_RSS_PARAM_FLAG_DISABLE_RSS 0x0010 | ||
| 73 | |||
| 74 | /* Hash info bits */ | ||
| 75 | #define NDIS_HASH_FUNC_TOEPLITZ 0x00000001 | ||
| 76 | #define NDIS_HASH_IPV4 0x00000100 | ||
| 77 | #define NDIS_HASH_TCP_IPV4 0x00000200 | ||
| 78 | #define NDIS_HASH_IPV6 0x00000400 | ||
| 79 | #define NDIS_HASH_IPV6_EX 0x00000800 | ||
| 80 | #define NDIS_HASH_TCP_IPV6 0x00001000 | ||
| 81 | #define NDIS_HASH_TCP_IPV6_EX 0x00002000 | ||
| 82 | |||
| 83 | #define NDIS_RSS_INDIRECTION_TABLE_MAX_SIZE_REVISION_2 (128 * 4) | ||
| 84 | #define NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2 40 | ||
| 85 | |||
| 86 | #define ITAB_NUM 128 | ||
| 87 | #define HASH_KEYLEN NDIS_RSS_HASH_SECRET_KEY_MAX_SIZE_REVISION_2 | ||
| 88 | extern u8 netvsc_hash_key[]; | ||
| 89 | |||
| 90 | struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */ | ||
| 91 | struct ndis_obj_header hdr; | ||
| 92 | |||
| 93 | /* Qualifies the rest of the information */ | ||
| 94 | u16 flag; | ||
| 95 | |||
| 96 | /* The base CPU number to do receive processing. not used */ | ||
| 97 | u16 base_cpu_number; | ||
| 98 | |||
| 99 | /* This describes the hash function and type being enabled */ | ||
| 100 | u32 hashinfo; | ||
| 101 | |||
| 102 | /* The size of indirection table array */ | ||
| 103 | u16 indirect_tabsize; | ||
| 104 | |||
| 105 | /* The offset of the indirection table from the beginning of this | ||
| 106 | * structure | ||
| 107 | */ | ||
| 108 | u32 indirect_taboffset; | ||
| 109 | |||
| 110 | /* The size of the hash secret key */ | ||
| 111 | u16 hashkey_size; | ||
| 112 | |||
| 113 | /* The offset of the secret key from the beginning of this structure */ | ||
| 114 | u32 kashkey_offset; | ||
| 115 | |||
| 116 | u32 processor_masks_offset; | ||
| 117 | u32 num_processor_masks; | ||
| 118 | u32 processor_masks_entry_size; | ||
| 119 | }; | ||
| 120 | |||
| 31 | /* Fwd declaration */ | 121 | /* Fwd declaration */ |
| 32 | struct hv_netvsc_packet; | 122 | struct hv_netvsc_packet; |
| 33 | struct ndis_tcp_ip_checksum_info; | 123 | struct ndis_tcp_ip_checksum_info; |
| @@ -39,6 +129,8 @@ struct xferpage_packet { | |||
| 39 | 129 | ||
| 40 | /* # of netvsc packets this xfer packet contains */ | 130 | /* # of netvsc packets this xfer packet contains */ |
| 41 | u32 count; | 131 | u32 count; |
| 132 | |||
| 133 | struct vmbus_channel *channel; | ||
| 42 | }; | 134 | }; |
| 43 | 135 | ||
| 44 | /* | 136 | /* |
| @@ -54,6 +146,9 @@ struct hv_netvsc_packet { | |||
| 54 | bool is_data_pkt; | 146 | bool is_data_pkt; |
| 55 | u16 vlan_tci; | 147 | u16 vlan_tci; |
| 56 | 148 | ||
| 149 | u16 q_idx; | ||
| 150 | struct vmbus_channel *channel; | ||
| 151 | |||
| 57 | /* | 152 | /* |
| 58 | * Valid only for receives when we break a xfer page packet | 153 | * Valid only for receives when we break a xfer page packet |
| 59 | * into multiple netvsc packets | 154 | * into multiple netvsc packets |
| @@ -120,6 +215,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, | |||
| 120 | int netvsc_recv_callback(struct hv_device *device_obj, | 215 | int netvsc_recv_callback(struct hv_device *device_obj, |
| 121 | struct hv_netvsc_packet *packet, | 216 | struct hv_netvsc_packet *packet, |
| 122 | struct ndis_tcp_ip_checksum_info *csum_info); | 217 | struct ndis_tcp_ip_checksum_info *csum_info); |
| 218 | void netvsc_channel_cb(void *context); | ||
| 123 | int rndis_filter_open(struct hv_device *dev); | 219 | int rndis_filter_open(struct hv_device *dev); |
| 124 | int rndis_filter_close(struct hv_device *dev); | 220 | int rndis_filter_close(struct hv_device *dev); |
| 125 | int rndis_filter_device_add(struct hv_device *dev, | 221 | int rndis_filter_device_add(struct hv_device *dev, |
| @@ -522,6 +618,8 @@ struct nvsp_message { | |||
| 522 | 618 | ||
| 523 | #define NETVSC_PACKET_SIZE 2048 | 619 | #define NETVSC_PACKET_SIZE 2048 |
| 524 | 620 | ||
| 621 | #define VRSS_SEND_TAB_SIZE 16 | ||
| 622 | |||
| 525 | /* Per netvsc channel-specific */ | 623 | /* Per netvsc channel-specific */ |
| 526 | struct netvsc_device { | 624 | struct netvsc_device { |
| 527 | struct hv_device *dev; | 625 | struct hv_device *dev; |
| @@ -555,10 +653,20 @@ struct netvsc_device { | |||
| 555 | 653 | ||
| 556 | struct net_device *ndev; | 654 | struct net_device *ndev; |
| 557 | 655 | ||
| 656 | struct vmbus_channel *chn_table[NR_CPUS]; | ||
| 657 | u32 send_table[VRSS_SEND_TAB_SIZE]; | ||
| 658 | u32 num_chn; | ||
| 659 | atomic_t queue_sends[NR_CPUS]; | ||
| 660 | |||
| 558 | /* Holds rndis device info */ | 661 | /* Holds rndis device info */ |
| 559 | void *extension; | 662 | void *extension; |
| 560 | /* The recive buffer for this device */ | 663 | |
| 664 | int ring_size; | ||
| 665 | |||
| 666 | /* The primary channel callback buffer */ | ||
| 561 | unsigned char cb_buffer[NETVSC_PACKET_SIZE]; | 667 | unsigned char cb_buffer[NETVSC_PACKET_SIZE]; |
| 668 | /* The sub channel callback buffer */ | ||
| 669 | unsigned char *sub_cb_buf; | ||
| 562 | }; | 670 | }; |
| 563 | 671 | ||
| 564 | /* NdisInitialize message */ | 672 | /* NdisInitialize message */ |
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index f7629ecefa84..e7e77f12bc38 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c | |||
| @@ -422,6 +422,9 @@ int netvsc_device_remove(struct hv_device *device) | |||
| 422 | kfree(netvsc_packet); | 422 | kfree(netvsc_packet); |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | if (net_device->sub_cb_buf) | ||
| 426 | vfree(net_device->sub_cb_buf); | ||
| 427 | |||
| 425 | kfree(net_device); | 428 | kfree(net_device); |
| 426 | return 0; | 429 | return 0; |
| 427 | } | 430 | } |
| @@ -461,7 +464,9 @@ static void netvsc_send_completion(struct netvsc_device *net_device, | |||
| 461 | (nvsp_packet->hdr.msg_type == | 464 | (nvsp_packet->hdr.msg_type == |
| 462 | NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || | 465 | NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE) || |
| 463 | (nvsp_packet->hdr.msg_type == | 466 | (nvsp_packet->hdr.msg_type == |
| 464 | NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE)) { | 467 | NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE) || |
| 468 | (nvsp_packet->hdr.msg_type == | ||
| 469 | NVSP_MSG5_TYPE_SUBCHANNEL)) { | ||
| 465 | /* Copy the response back */ | 470 | /* Copy the response back */ |
| 466 | memcpy(&net_device->channel_init_pkt, nvsp_packet, | 471 | memcpy(&net_device->channel_init_pkt, nvsp_packet, |
| 467 | sizeof(struct nvsp_message)); | 472 | sizeof(struct nvsp_message)); |
| @@ -469,28 +474,37 @@ static void netvsc_send_completion(struct netvsc_device *net_device, | |||
| 469 | } else if (nvsp_packet->hdr.msg_type == | 474 | } else if (nvsp_packet->hdr.msg_type == |
| 470 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { | 475 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE) { |
| 471 | int num_outstanding_sends; | 476 | int num_outstanding_sends; |
| 477 | u16 q_idx = 0; | ||
| 478 | struct vmbus_channel *channel = device->channel; | ||
| 479 | int queue_sends; | ||
| 472 | 480 | ||
| 473 | /* Get the send context */ | 481 | /* Get the send context */ |
| 474 | nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) | 482 | nvsc_packet = (struct hv_netvsc_packet *)(unsigned long) |
| 475 | packet->trans_id; | 483 | packet->trans_id; |
| 476 | 484 | ||
| 477 | /* Notify the layer above us */ | 485 | /* Notify the layer above us */ |
| 478 | if (nvsc_packet) | 486 | if (nvsc_packet) { |
| 487 | q_idx = nvsc_packet->q_idx; | ||
| 488 | channel = nvsc_packet->channel; | ||
| 479 | nvsc_packet->completion.send.send_completion( | 489 | nvsc_packet->completion.send.send_completion( |
| 480 | nvsc_packet->completion.send. | 490 | nvsc_packet->completion.send. |
| 481 | send_completion_ctx); | 491 | send_completion_ctx); |
| 492 | } | ||
| 482 | 493 | ||
| 483 | num_outstanding_sends = | 494 | num_outstanding_sends = |
| 484 | atomic_dec_return(&net_device->num_outstanding_sends); | 495 | atomic_dec_return(&net_device->num_outstanding_sends); |
| 496 | queue_sends = atomic_dec_return(&net_device-> | ||
| 497 | queue_sends[q_idx]); | ||
| 485 | 498 | ||
| 486 | if (net_device->destroy && num_outstanding_sends == 0) | 499 | if (net_device->destroy && num_outstanding_sends == 0) |
| 487 | wake_up(&net_device->wait_drain); | 500 | wake_up(&net_device->wait_drain); |
| 488 | 501 | ||
| 489 | if (netif_queue_stopped(ndev) && !net_device->start_remove && | 502 | if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && |
| 490 | (hv_ringbuf_avail_percent(&device->channel->outbound) | 503 | !net_device->start_remove && |
| 491 | > RING_AVAIL_PERCENT_HIWATER || | 504 | (hv_ringbuf_avail_percent(&channel->outbound) > |
| 492 | num_outstanding_sends < 1)) | 505 | RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) |
| 493 | netif_wake_queue(ndev); | 506 | netif_tx_wake_queue(netdev_get_tx_queue( |
| 507 | ndev, q_idx)); | ||
| 494 | } else { | 508 | } else { |
| 495 | netdev_err(ndev, "Unknown send completion packet type- " | 509 | netdev_err(ndev, "Unknown send completion packet type- " |
| 496 | "%d received!!\n", nvsp_packet->hdr.msg_type); | 510 | "%d received!!\n", nvsp_packet->hdr.msg_type); |
| @@ -505,6 +519,7 @@ int netvsc_send(struct hv_device *device, | |||
| 505 | int ret = 0; | 519 | int ret = 0; |
| 506 | struct nvsp_message sendMessage; | 520 | struct nvsp_message sendMessage; |
| 507 | struct net_device *ndev; | 521 | struct net_device *ndev; |
| 522 | struct vmbus_channel *out_channel = NULL; | ||
| 508 | u64 req_id; | 523 | u64 req_id; |
| 509 | 524 | ||
| 510 | net_device = get_outbound_net_device(device); | 525 | net_device = get_outbound_net_device(device); |
| @@ -531,15 +546,20 @@ int netvsc_send(struct hv_device *device, | |||
| 531 | else | 546 | else |
| 532 | req_id = 0; | 547 | req_id = 0; |
| 533 | 548 | ||
| 549 | out_channel = net_device->chn_table[packet->q_idx]; | ||
| 550 | if (out_channel == NULL) | ||
| 551 | out_channel = device->channel; | ||
| 552 | packet->channel = out_channel; | ||
| 553 | |||
| 534 | if (packet->page_buf_cnt) { | 554 | if (packet->page_buf_cnt) { |
| 535 | ret = vmbus_sendpacket_pagebuffer(device->channel, | 555 | ret = vmbus_sendpacket_pagebuffer(out_channel, |
| 536 | packet->page_buf, | 556 | packet->page_buf, |
| 537 | packet->page_buf_cnt, | 557 | packet->page_buf_cnt, |
| 538 | &sendMessage, | 558 | &sendMessage, |
| 539 | sizeof(struct nvsp_message), | 559 | sizeof(struct nvsp_message), |
| 540 | req_id); | 560 | req_id); |
| 541 | } else { | 561 | } else { |
| 542 | ret = vmbus_sendpacket(device->channel, &sendMessage, | 562 | ret = vmbus_sendpacket(out_channel, &sendMessage, |
| 543 | sizeof(struct nvsp_message), | 563 | sizeof(struct nvsp_message), |
| 544 | req_id, | 564 | req_id, |
| 545 | VM_PKT_DATA_INBAND, | 565 | VM_PKT_DATA_INBAND, |
| @@ -548,17 +568,24 @@ int netvsc_send(struct hv_device *device, | |||
| 548 | 568 | ||
| 549 | if (ret == 0) { | 569 | if (ret == 0) { |
| 550 | atomic_inc(&net_device->num_outstanding_sends); | 570 | atomic_inc(&net_device->num_outstanding_sends); |
| 551 | if (hv_ringbuf_avail_percent(&device->channel->outbound) < | 571 | atomic_inc(&net_device->queue_sends[packet->q_idx]); |
| 572 | |||
| 573 | if (hv_ringbuf_avail_percent(&out_channel->outbound) < | ||
| 552 | RING_AVAIL_PERCENT_LOWATER) { | 574 | RING_AVAIL_PERCENT_LOWATER) { |
| 553 | netif_stop_queue(ndev); | 575 | netif_tx_stop_queue(netdev_get_tx_queue( |
| 576 | ndev, packet->q_idx)); | ||
| 577 | |||
| 554 | if (atomic_read(&net_device-> | 578 | if (atomic_read(&net_device-> |
| 555 | num_outstanding_sends) < 1) | 579 | queue_sends[packet->q_idx]) < 1) |
| 556 | netif_wake_queue(ndev); | 580 | netif_tx_wake_queue(netdev_get_tx_queue( |
| 581 | ndev, packet->q_idx)); | ||
| 557 | } | 582 | } |
| 558 | } else if (ret == -EAGAIN) { | 583 | } else if (ret == -EAGAIN) { |
| 559 | netif_stop_queue(ndev); | 584 | netif_tx_stop_queue(netdev_get_tx_queue( |
| 560 | if (atomic_read(&net_device->num_outstanding_sends) < 1) { | 585 | ndev, packet->q_idx)); |
| 561 | netif_wake_queue(ndev); | 586 | if (atomic_read(&net_device->queue_sends[packet->q_idx]) < 1) { |
| 587 | netif_tx_wake_queue(netdev_get_tx_queue( | ||
| 588 | ndev, packet->q_idx)); | ||
| 562 | ret = -ENOSPC; | 589 | ret = -ENOSPC; |
| 563 | } | 590 | } |
| 564 | } else { | 591 | } else { |
| @@ -570,6 +597,7 @@ int netvsc_send(struct hv_device *device, | |||
| 570 | } | 597 | } |
| 571 | 598 | ||
| 572 | static void netvsc_send_recv_completion(struct hv_device *device, | 599 | static void netvsc_send_recv_completion(struct hv_device *device, |
| 600 | struct vmbus_channel *channel, | ||
| 573 | struct netvsc_device *net_device, | 601 | struct netvsc_device *net_device, |
| 574 | u64 transaction_id, u32 status) | 602 | u64 transaction_id, u32 status) |
| 575 | { | 603 | { |
| @@ -587,7 +615,7 @@ static void netvsc_send_recv_completion(struct hv_device *device, | |||
| 587 | 615 | ||
| 588 | retry_send_cmplt: | 616 | retry_send_cmplt: |
| 589 | /* Send the completion */ | 617 | /* Send the completion */ |
| 590 | ret = vmbus_sendpacket(device->channel, &recvcompMessage, | 618 | ret = vmbus_sendpacket(channel, &recvcompMessage, |
| 591 | sizeof(struct nvsp_message), transaction_id, | 619 | sizeof(struct nvsp_message), transaction_id, |
| 592 | VM_PKT_COMP, 0); | 620 | VM_PKT_COMP, 0); |
| 593 | if (ret == 0) { | 621 | if (ret == 0) { |
| @@ -618,6 +646,7 @@ static void netvsc_receive_completion(void *context) | |||
| 618 | { | 646 | { |
| 619 | struct hv_netvsc_packet *packet = context; | 647 | struct hv_netvsc_packet *packet = context; |
| 620 | struct hv_device *device = packet->device; | 648 | struct hv_device *device = packet->device; |
| 649 | struct vmbus_channel *channel; | ||
| 621 | struct netvsc_device *net_device; | 650 | struct netvsc_device *net_device; |
| 622 | u64 transaction_id = 0; | 651 | u64 transaction_id = 0; |
| 623 | bool fsend_receive_comp = false; | 652 | bool fsend_receive_comp = false; |
| @@ -649,6 +678,7 @@ static void netvsc_receive_completion(void *context) | |||
| 649 | */ | 678 | */ |
| 650 | if (packet->xfer_page_pkt->count == 0) { | 679 | if (packet->xfer_page_pkt->count == 0) { |
| 651 | fsend_receive_comp = true; | 680 | fsend_receive_comp = true; |
| 681 | channel = packet->xfer_page_pkt->channel; | ||
| 652 | transaction_id = packet->completion.recv.recv_completion_tid; | 682 | transaction_id = packet->completion.recv.recv_completion_tid; |
| 653 | status = packet->xfer_page_pkt->status; | 683 | status = packet->xfer_page_pkt->status; |
| 654 | list_add_tail(&packet->xfer_page_pkt->list_ent, | 684 | list_add_tail(&packet->xfer_page_pkt->list_ent, |
| @@ -662,12 +692,13 @@ static void netvsc_receive_completion(void *context) | |||
| 662 | 692 | ||
| 663 | /* Send a receive completion for the xfer page packet */ | 693 | /* Send a receive completion for the xfer page packet */ |
| 664 | if (fsend_receive_comp) | 694 | if (fsend_receive_comp) |
| 665 | netvsc_send_recv_completion(device, net_device, transaction_id, | 695 | netvsc_send_recv_completion(device, channel, net_device, |
| 666 | status); | 696 | transaction_id, status); |
| 667 | 697 | ||
| 668 | } | 698 | } |
| 669 | 699 | ||
| 670 | static void netvsc_receive(struct netvsc_device *net_device, | 700 | static void netvsc_receive(struct netvsc_device *net_device, |
| 701 | struct vmbus_channel *channel, | ||
| 671 | struct hv_device *device, | 702 | struct hv_device *device, |
| 672 | struct vmpacket_descriptor *packet) | 703 | struct vmpacket_descriptor *packet) |
| 673 | { | 704 | { |
| @@ -748,7 +779,7 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
| 748 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, | 779 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, |
| 749 | flags); | 780 | flags); |
| 750 | 781 | ||
| 751 | netvsc_send_recv_completion(device, net_device, | 782 | netvsc_send_recv_completion(device, channel, net_device, |
| 752 | vmxferpage_packet->d.trans_id, | 783 | vmxferpage_packet->d.trans_id, |
| 753 | NVSP_STAT_FAIL); | 784 | NVSP_STAT_FAIL); |
| 754 | 785 | ||
| @@ -759,6 +790,7 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
| 759 | xferpage_packet = (struct xferpage_packet *)listHead.next; | 790 | xferpage_packet = (struct xferpage_packet *)listHead.next; |
| 760 | list_del(&xferpage_packet->list_ent); | 791 | list_del(&xferpage_packet->list_ent); |
| 761 | xferpage_packet->status = NVSP_STAT_SUCCESS; | 792 | xferpage_packet->status = NVSP_STAT_SUCCESS; |
| 793 | xferpage_packet->channel = channel; | ||
| 762 | 794 | ||
| 763 | /* This is how much we can satisfy */ | 795 | /* This is how much we can satisfy */ |
| 764 | xferpage_packet->count = count - 1; | 796 | xferpage_packet->count = count - 1; |
| @@ -800,10 +832,45 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
| 800 | 832 | ||
| 801 | } | 833 | } |
| 802 | 834 | ||
| 803 | static void netvsc_channel_cb(void *context) | 835 | |
| 836 | static void netvsc_send_table(struct hv_device *hdev, | ||
| 837 | struct vmpacket_descriptor *vmpkt) | ||
| 838 | { | ||
| 839 | struct netvsc_device *nvscdev; | ||
| 840 | struct net_device *ndev; | ||
| 841 | struct nvsp_message *nvmsg; | ||
| 842 | int i; | ||
| 843 | u32 count, *tab; | ||
| 844 | |||
| 845 | nvscdev = get_outbound_net_device(hdev); | ||
| 846 | if (!nvscdev) | ||
| 847 | return; | ||
| 848 | ndev = nvscdev->ndev; | ||
| 849 | |||
| 850 | nvmsg = (struct nvsp_message *)((unsigned long)vmpkt + | ||
| 851 | (vmpkt->offset8 << 3)); | ||
| 852 | |||
| 853 | if (nvmsg->hdr.msg_type != NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE) | ||
| 854 | return; | ||
| 855 | |||
| 856 | count = nvmsg->msg.v5_msg.send_table.count; | ||
| 857 | if (count != VRSS_SEND_TAB_SIZE) { | ||
| 858 | netdev_err(ndev, "Received wrong send-table size:%u\n", count); | ||
| 859 | return; | ||
| 860 | } | ||
| 861 | |||
| 862 | tab = (u32 *)((unsigned long)&nvmsg->msg.v5_msg.send_table + | ||
| 863 | nvmsg->msg.v5_msg.send_table.offset); | ||
| 864 | |||
| 865 | for (i = 0; i < count; i++) | ||
| 866 | nvscdev->send_table[i] = tab[i]; | ||
| 867 | } | ||
| 868 | |||
| 869 | void netvsc_channel_cb(void *context) | ||
| 804 | { | 870 | { |
| 805 | int ret; | 871 | int ret; |
| 806 | struct hv_device *device = context; | 872 | struct vmbus_channel *channel = (struct vmbus_channel *)context; |
| 873 | struct hv_device *device; | ||
| 807 | struct netvsc_device *net_device; | 874 | struct netvsc_device *net_device; |
| 808 | u32 bytes_recvd; | 875 | u32 bytes_recvd; |
| 809 | u64 request_id; | 876 | u64 request_id; |
| @@ -812,14 +879,19 @@ static void netvsc_channel_cb(void *context) | |||
| 812 | int bufferlen = NETVSC_PACKET_SIZE; | 879 | int bufferlen = NETVSC_PACKET_SIZE; |
| 813 | struct net_device *ndev; | 880 | struct net_device *ndev; |
| 814 | 881 | ||
| 882 | if (channel->primary_channel != NULL) | ||
| 883 | device = channel->primary_channel->device_obj; | ||
| 884 | else | ||
| 885 | device = channel->device_obj; | ||
| 886 | |||
| 815 | net_device = get_inbound_net_device(device); | 887 | net_device = get_inbound_net_device(device); |
| 816 | if (!net_device) | 888 | if (!net_device) |
| 817 | return; | 889 | return; |
| 818 | ndev = net_device->ndev; | 890 | ndev = net_device->ndev; |
| 819 | buffer = net_device->cb_buffer; | 891 | buffer = get_per_channel_state(channel); |
| 820 | 892 | ||
| 821 | do { | 893 | do { |
| 822 | ret = vmbus_recvpacket_raw(device->channel, buffer, bufferlen, | 894 | ret = vmbus_recvpacket_raw(channel, buffer, bufferlen, |
| 823 | &bytes_recvd, &request_id); | 895 | &bytes_recvd, &request_id); |
| 824 | if (ret == 0) { | 896 | if (ret == 0) { |
| 825 | if (bytes_recvd > 0) { | 897 | if (bytes_recvd > 0) { |
| @@ -831,8 +903,12 @@ static void netvsc_channel_cb(void *context) | |||
| 831 | break; | 903 | break; |
| 832 | 904 | ||
| 833 | case VM_PKT_DATA_USING_XFER_PAGES: | 905 | case VM_PKT_DATA_USING_XFER_PAGES: |
| 834 | netvsc_receive(net_device, | 906 | netvsc_receive(net_device, channel, |
| 835 | device, desc); | 907 | device, desc); |
| 908 | break; | ||
| 909 | |||
| 910 | case VM_PKT_DATA_INBAND: | ||
| 911 | netvsc_send_table(device, desc); | ||
| 836 | break; | 912 | break; |
| 837 | 913 | ||
| 838 | default: | 914 | default: |
| @@ -893,6 +969,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) | |||
| 893 | goto cleanup; | 969 | goto cleanup; |
| 894 | } | 970 | } |
| 895 | 971 | ||
| 972 | net_device->ring_size = ring_size; | ||
| 973 | |||
| 896 | /* | 974 | /* |
| 897 | * Coming into this function, struct net_device * is | 975 | * Coming into this function, struct net_device * is |
| 898 | * registered as the driver private data. | 976 | * registered as the driver private data. |
| @@ -917,10 +995,12 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) | |||
| 917 | } | 995 | } |
| 918 | init_completion(&net_device->channel_init_wait); | 996 | init_completion(&net_device->channel_init_wait); |
| 919 | 997 | ||
| 998 | set_per_channel_state(device->channel, net_device->cb_buffer); | ||
| 999 | |||
| 920 | /* Open the channel */ | 1000 | /* Open the channel */ |
| 921 | ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, | 1001 | ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, |
| 922 | ring_size * PAGE_SIZE, NULL, 0, | 1002 | ring_size * PAGE_SIZE, NULL, 0, |
| 923 | netvsc_channel_cb, device); | 1003 | netvsc_channel_cb, device->channel); |
| 924 | 1004 | ||
| 925 | if (ret != 0) { | 1005 | if (ret != 0) { |
| 926 | netdev_err(ndev, "unable to open channel: %d\n", ret); | 1006 | netdev_err(ndev, "unable to open channel: %d\n", ret); |
| @@ -930,6 +1010,8 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) | |||
| 930 | /* Channel is opened */ | 1010 | /* Channel is opened */ |
| 931 | pr_info("hv_netvsc channel opened successfully\n"); | 1011 | pr_info("hv_netvsc channel opened successfully\n"); |
| 932 | 1012 | ||
| 1013 | net_device->chn_table[0] = device->channel; | ||
| 1014 | |||
| 933 | /* Connect with the NetVsp */ | 1015 | /* Connect with the NetVsp */ |
| 934 | ret = netvsc_connect_vsp(device); | 1016 | ret = netvsc_connect_vsp(device); |
| 935 | if (ret != 0) { | 1017 | if (ret != 0) { |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 31e55fba7cad..093cf3fc46b8 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
| @@ -101,7 +101,7 @@ static int netvsc_open(struct net_device *net) | |||
| 101 | return ret; | 101 | return ret; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | netif_start_queue(net); | 104 | netif_tx_start_all_queues(net); |
| 105 | 105 | ||
| 106 | nvdev = hv_get_drvdata(device_obj); | 106 | nvdev = hv_get_drvdata(device_obj); |
| 107 | rdev = nvdev->extension; | 107 | rdev = nvdev->extension; |
| @@ -149,6 +149,88 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, | |||
| 149 | return ppi; | 149 | return ppi; |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | union sub_key { | ||
| 153 | u64 k; | ||
| 154 | struct { | ||
| 155 | u8 pad[3]; | ||
| 156 | u8 kb; | ||
| 157 | u32 ka; | ||
| 158 | }; | ||
| 159 | }; | ||
| 160 | |||
| 161 | /* Toeplitz hash function | ||
| 162 | * data: network byte order | ||
| 163 | * return: host byte order | ||
| 164 | */ | ||
| 165 | static u32 comp_hash(u8 *key, int klen, u8 *data, int dlen) | ||
| 166 | { | ||
| 167 | union sub_key subk; | ||
| 168 | int k_next = 4; | ||
| 169 | u8 dt; | ||
| 170 | int i, j; | ||
| 171 | u32 ret = 0; | ||
| 172 | |||
| 173 | subk.k = 0; | ||
| 174 | subk.ka = ntohl(*(u32 *)key); | ||
| 175 | |||
| 176 | for (i = 0; i < dlen; i++) { | ||
| 177 | subk.kb = key[k_next]; | ||
| 178 | k_next = (k_next + 1) % klen; | ||
| 179 | dt = data[i]; | ||
| 180 | for (j = 0; j < 8; j++) { | ||
| 181 | if (dt & 0x80) | ||
| 182 | ret ^= subk.ka; | ||
| 183 | dt <<= 1; | ||
| 184 | subk.k <<= 1; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | return ret; | ||
| 189 | } | ||
| 190 | |||
| 191 | static bool netvsc_set_hash(u32 *hash, struct sk_buff *skb) | ||
| 192 | { | ||
| 193 | struct iphdr *iphdr; | ||
| 194 | int data_len; | ||
| 195 | bool ret = false; | ||
| 196 | |||
| 197 | if (eth_hdr(skb)->h_proto != htons(ETH_P_IP)) | ||
| 198 | return false; | ||
| 199 | |||
| 200 | iphdr = ip_hdr(skb); | ||
| 201 | |||
| 202 | if (iphdr->version == 4) { | ||
| 203 | if (iphdr->protocol == IPPROTO_TCP) | ||
| 204 | data_len = 12; | ||
| 205 | else | ||
| 206 | data_len = 8; | ||
| 207 | *hash = comp_hash(netvsc_hash_key, HASH_KEYLEN, | ||
| 208 | (u8 *)&iphdr->saddr, data_len); | ||
| 209 | ret = true; | ||
| 210 | } | ||
| 211 | |||
| 212 | return ret; | ||
| 213 | } | ||
| 214 | |||
| 215 | static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, | ||
| 216 | void *accel_priv, select_queue_fallback_t fallback) | ||
| 217 | { | ||
| 218 | struct net_device_context *net_device_ctx = netdev_priv(ndev); | ||
| 219 | struct hv_device *hdev = net_device_ctx->device_ctx; | ||
| 220 | struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev); | ||
| 221 | u32 hash; | ||
| 222 | u16 q_idx = 0; | ||
| 223 | |||
| 224 | if (nvsc_dev == NULL || ndev->real_num_tx_queues <= 1) | ||
| 225 | return 0; | ||
| 226 | |||
| 227 | if (netvsc_set_hash(&hash, skb)) | ||
| 228 | q_idx = nvsc_dev->send_table[hash % VRSS_SEND_TAB_SIZE] % | ||
| 229 | ndev->real_num_tx_queues; | ||
| 230 | |||
| 231 | return q_idx; | ||
| 232 | } | ||
| 233 | |||
| 152 | static void netvsc_xmit_completion(void *context) | 234 | static void netvsc_xmit_completion(void *context) |
| 153 | { | 235 | { |
| 154 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; | 236 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; |
| @@ -333,6 +415,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
| 333 | 415 | ||
| 334 | packet->vlan_tci = skb->vlan_tci; | 416 | packet->vlan_tci = skb->vlan_tci; |
| 335 | 417 | ||
| 418 | packet->q_idx = skb_get_queue_mapping(skb); | ||
| 419 | |||
| 336 | packet->is_data_pkt = true; | 420 | packet->is_data_pkt = true; |
| 337 | packet->total_data_buflen = skb->len; | 421 | packet->total_data_buflen = skb->len; |
| 338 | 422 | ||
| @@ -554,6 +638,10 @@ int netvsc_recv_callback(struct hv_device *device_obj, | |||
| 554 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), | 638 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
| 555 | packet->vlan_tci); | 639 | packet->vlan_tci); |
| 556 | 640 | ||
| 641 | skb_record_rx_queue(skb, packet->xfer_page_pkt->channel-> | ||
| 642 | offermsg.offer.sub_channel_index % | ||
| 643 | net->real_num_rx_queues); | ||
| 644 | |||
| 557 | net->stats.rx_packets++; | 645 | net->stats.rx_packets++; |
| 558 | net->stats.rx_bytes += packet->total_data_buflen; | 646 | net->stats.rx_bytes += packet->total_data_buflen; |
| 559 | 647 | ||
| @@ -602,7 +690,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) | |||
| 602 | hv_set_drvdata(hdev, ndev); | 690 | hv_set_drvdata(hdev, ndev); |
| 603 | device_info.ring_size = ring_size; | 691 | device_info.ring_size = ring_size; |
| 604 | rndis_filter_device_add(hdev, &device_info); | 692 | rndis_filter_device_add(hdev, &device_info); |
| 605 | netif_wake_queue(ndev); | 693 | netif_tx_wake_all_queues(ndev); |
| 606 | 694 | ||
| 607 | return 0; | 695 | return 0; |
| 608 | } | 696 | } |
| @@ -648,6 +736,7 @@ static const struct net_device_ops device_ops = { | |||
| 648 | .ndo_change_mtu = netvsc_change_mtu, | 736 | .ndo_change_mtu = netvsc_change_mtu, |
| 649 | .ndo_validate_addr = eth_validate_addr, | 737 | .ndo_validate_addr = eth_validate_addr, |
| 650 | .ndo_set_mac_address = netvsc_set_mac_addr, | 738 | .ndo_set_mac_address = netvsc_set_mac_addr, |
| 739 | .ndo_select_queue = netvsc_select_queue, | ||
| 651 | }; | 740 | }; |
| 652 | 741 | ||
| 653 | /* | 742 | /* |
| @@ -694,9 +783,11 @@ static int netvsc_probe(struct hv_device *dev, | |||
| 694 | struct net_device *net = NULL; | 783 | struct net_device *net = NULL; |
| 695 | struct net_device_context *net_device_ctx; | 784 | struct net_device_context *net_device_ctx; |
| 696 | struct netvsc_device_info device_info; | 785 | struct netvsc_device_info device_info; |
| 786 | struct netvsc_device *nvdev; | ||
| 697 | int ret; | 787 | int ret; |
| 698 | 788 | ||
| 699 | net = alloc_etherdev(sizeof(struct net_device_context)); | 789 | net = alloc_etherdev_mq(sizeof(struct net_device_context), |
| 790 | num_online_cpus()); | ||
| 700 | if (!net) | 791 | if (!net) |
| 701 | return -ENOMEM; | 792 | return -ENOMEM; |
| 702 | 793 | ||
| @@ -729,6 +820,12 @@ static int netvsc_probe(struct hv_device *dev, | |||
| 729 | } | 820 | } |
| 730 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); | 821 | memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); |
| 731 | 822 | ||
| 823 | nvdev = hv_get_drvdata(dev); | ||
| 824 | netif_set_real_num_tx_queues(net, nvdev->num_chn); | ||
| 825 | netif_set_real_num_rx_queues(net, nvdev->num_chn); | ||
| 826 | dev_info(&dev->device, "real num tx,rx queues:%u, %u\n", | ||
| 827 | net->real_num_tx_queues, net->real_num_rx_queues); | ||
| 828 | |||
| 732 | ret = register_netdev(net); | 829 | ret = register_netdev(net); |
| 733 | if (ret != 0) { | 830 | if (ret != 0) { |
| 734 | pr_err("Unable to register netdev.\n"); | 831 | pr_err("Unable to register netdev.\n"); |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 143a98caf618..d92cfbe43410 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | #include "hyperv_net.h" | 31 | #include "hyperv_net.h" |
| 32 | 32 | ||
| 33 | 33 | ||
| 34 | #define RNDIS_EXT_LEN 100 | 34 | #define RNDIS_EXT_LEN PAGE_SIZE |
| 35 | struct rndis_request { | 35 | struct rndis_request { |
| 36 | struct list_head list_ent; | 36 | struct list_head list_ent; |
| 37 | struct completion wait_event; | 37 | struct completion wait_event; |
| @@ -94,6 +94,8 @@ static struct rndis_request *get_rndis_request(struct rndis_device *dev, | |||
| 94 | rndis_msg->ndis_msg_type = msg_type; | 94 | rndis_msg->ndis_msg_type = msg_type; |
| 95 | rndis_msg->msg_len = msg_len; | 95 | rndis_msg->msg_len = msg_len; |
| 96 | 96 | ||
| 97 | request->pkt.q_idx = 0; | ||
| 98 | |||
| 97 | /* | 99 | /* |
| 98 | * Set the request id. This field is always after the rndis header for | 100 | * Set the request id. This field is always after the rndis header for |
| 99 | * request/response packet types so we just used the SetRequest as a | 101 | * request/response packet types so we just used the SetRequest as a |
| @@ -509,6 +511,19 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, | |||
| 509 | query->info_buflen = 0; | 511 | query->info_buflen = 0; |
| 510 | query->dev_vc_handle = 0; | 512 | query->dev_vc_handle = 0; |
| 511 | 513 | ||
| 514 | if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) { | ||
| 515 | struct ndis_recv_scale_cap *cap; | ||
| 516 | |||
| 517 | request->request_msg.msg_len += | ||
| 518 | sizeof(struct ndis_recv_scale_cap); | ||
| 519 | query->info_buflen = sizeof(struct ndis_recv_scale_cap); | ||
| 520 | cap = (struct ndis_recv_scale_cap *)((unsigned long)query + | ||
| 521 | query->info_buf_offset); | ||
| 522 | cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES; | ||
| 523 | cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2; | ||
| 524 | cap->hdr.size = sizeof(struct ndis_recv_scale_cap); | ||
| 525 | } | ||
| 526 | |||
| 512 | ret = rndis_filter_send_request(dev, request); | 527 | ret = rndis_filter_send_request(dev, request); |
| 513 | if (ret != 0) | 528 | if (ret != 0) |
| 514 | goto cleanup; | 529 | goto cleanup; |
| @@ -695,6 +710,89 @@ cleanup: | |||
| 695 | return ret; | 710 | return ret; |
| 696 | } | 711 | } |
| 697 | 712 | ||
| 713 | u8 netvsc_hash_key[HASH_KEYLEN] = { | ||
| 714 | 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, | ||
| 715 | 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, | ||
| 716 | 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, | ||
| 717 | 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, | ||
| 718 | 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa | ||
| 719 | }; | ||
| 720 | |||
| 721 | int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) | ||
| 722 | { | ||
| 723 | struct net_device *ndev = rdev->net_dev->ndev; | ||
| 724 | struct rndis_request *request; | ||
| 725 | struct rndis_set_request *set; | ||
| 726 | struct rndis_set_complete *set_complete; | ||
| 727 | u32 extlen = sizeof(struct ndis_recv_scale_param) + | ||
| 728 | 4*ITAB_NUM + HASH_KEYLEN; | ||
| 729 | struct ndis_recv_scale_param *rssp; | ||
| 730 | u32 *itab; | ||
| 731 | u8 *keyp; | ||
| 732 | int i, t, ret; | ||
| 733 | |||
| 734 | request = get_rndis_request( | ||
| 735 | rdev, RNDIS_MSG_SET, | ||
| 736 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); | ||
| 737 | if (!request) | ||
| 738 | return -ENOMEM; | ||
| 739 | |||
| 740 | set = &request->request_msg.msg.set_req; | ||
| 741 | set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS; | ||
| 742 | set->info_buflen = extlen; | ||
| 743 | set->info_buf_offset = sizeof(struct rndis_set_request); | ||
| 744 | set->dev_vc_handle = 0; | ||
| 745 | |||
| 746 | rssp = (struct ndis_recv_scale_param *)(set + 1); | ||
| 747 | rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS; | ||
| 748 | rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2; | ||
| 749 | rssp->hdr.size = sizeof(struct ndis_recv_scale_param); | ||
| 750 | rssp->flag = 0; | ||
| 751 | rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 | | ||
| 752 | NDIS_HASH_TCP_IPV4; | ||
| 753 | rssp->indirect_tabsize = 4*ITAB_NUM; | ||
| 754 | rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param); | ||
| 755 | rssp->hashkey_size = HASH_KEYLEN; | ||
| 756 | rssp->kashkey_offset = rssp->indirect_taboffset + | ||
| 757 | rssp->indirect_tabsize; | ||
| 758 | |||
| 759 | /* Set indirection table entries */ | ||
| 760 | itab = (u32 *)(rssp + 1); | ||
| 761 | for (i = 0; i < ITAB_NUM; i++) | ||
| 762 | itab[i] = i % num_queue; | ||
| 763 | |||
| 764 | /* Set hask key values */ | ||
| 765 | keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset); | ||
| 766 | for (i = 0; i < HASH_KEYLEN; i++) | ||
| 767 | keyp[i] = netvsc_hash_key[i]; | ||
| 768 | |||
| 769 | |||
| 770 | ret = rndis_filter_send_request(rdev, request); | ||
| 771 | if (ret != 0) | ||
| 772 | goto cleanup; | ||
| 773 | |||
| 774 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
| 775 | if (t == 0) { | ||
| 776 | netdev_err(ndev, "timeout before we got a set response...\n"); | ||
| 777 | /* can't put_rndis_request, since we may still receive a | ||
| 778 | * send-completion. | ||
| 779 | */ | ||
| 780 | return -ETIMEDOUT; | ||
| 781 | } else { | ||
| 782 | set_complete = &request->response_msg.msg.set_complete; | ||
| 783 | if (set_complete->status != RNDIS_STATUS_SUCCESS) { | ||
| 784 | netdev_err(ndev, "Fail to set RSS parameters:0x%x\n", | ||
| 785 | set_complete->status); | ||
| 786 | ret = -EINVAL; | ||
| 787 | } | ||
| 788 | } | ||
| 789 | |||
| 790 | cleanup: | ||
| 791 | put_rndis_request(rdev, request); | ||
| 792 | return ret; | ||
| 793 | } | ||
| 794 | |||
| 795 | |||
| 698 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) | 796 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) |
| 699 | { | 797 | { |
| 700 | u32 size = sizeof(u32); | 798 | u32 size = sizeof(u32); |
| @@ -886,6 +984,28 @@ static int rndis_filter_close_device(struct rndis_device *dev) | |||
| 886 | return ret; | 984 | return ret; |
| 887 | } | 985 | } |
| 888 | 986 | ||
| 987 | static void netvsc_sc_open(struct vmbus_channel *new_sc) | ||
| 988 | { | ||
| 989 | struct netvsc_device *nvscdev; | ||
| 990 | u16 chn_index = new_sc->offermsg.offer.sub_channel_index; | ||
| 991 | int ret; | ||
| 992 | |||
| 993 | nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj); | ||
| 994 | |||
| 995 | if (chn_index >= nvscdev->num_chn) | ||
| 996 | return; | ||
| 997 | |||
| 998 | set_per_channel_state(new_sc, nvscdev->sub_cb_buf + (chn_index - 1) * | ||
| 999 | NETVSC_PACKET_SIZE); | ||
| 1000 | |||
| 1001 | ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, | ||
| 1002 | nvscdev->ring_size * PAGE_SIZE, NULL, 0, | ||
| 1003 | netvsc_channel_cb, new_sc); | ||
| 1004 | |||
| 1005 | if (ret == 0) | ||
| 1006 | nvscdev->chn_table[chn_index] = new_sc; | ||
| 1007 | } | ||
| 1008 | |||
| 889 | int rndis_filter_device_add(struct hv_device *dev, | 1009 | int rndis_filter_device_add(struct hv_device *dev, |
| 890 | void *additional_info) | 1010 | void *additional_info) |
| 891 | { | 1011 | { |
| @@ -894,6 +1014,10 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 894 | struct rndis_device *rndis_device; | 1014 | struct rndis_device *rndis_device; |
| 895 | struct netvsc_device_info *device_info = additional_info; | 1015 | struct netvsc_device_info *device_info = additional_info; |
| 896 | struct ndis_offload_params offloads; | 1016 | struct ndis_offload_params offloads; |
| 1017 | struct nvsp_message *init_packet; | ||
| 1018 | int t; | ||
| 1019 | struct ndis_recv_scale_cap rsscap; | ||
| 1020 | u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); | ||
| 897 | 1021 | ||
| 898 | rndis_device = get_rndis_device(); | 1022 | rndis_device = get_rndis_device(); |
| 899 | if (!rndis_device) | 1023 | if (!rndis_device) |
| @@ -913,6 +1037,7 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 913 | 1037 | ||
| 914 | /* Initialize the rndis device */ | 1038 | /* Initialize the rndis device */ |
| 915 | net_device = hv_get_drvdata(dev); | 1039 | net_device = hv_get_drvdata(dev); |
| 1040 | net_device->num_chn = 1; | ||
| 916 | 1041 | ||
| 917 | net_device->extension = rndis_device; | 1042 | net_device->extension = rndis_device; |
| 918 | rndis_device->net_dev = net_device; | 1043 | rndis_device->net_dev = net_device; |
| @@ -952,7 +1077,6 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 952 | if (ret) | 1077 | if (ret) |
| 953 | goto err_dev_remv; | 1078 | goto err_dev_remv; |
| 954 | 1079 | ||
| 955 | |||
| 956 | rndis_filter_query_device_link_status(rndis_device); | 1080 | rndis_filter_query_device_link_status(rndis_device); |
| 957 | 1081 | ||
| 958 | device_info->link_state = rndis_device->link_state; | 1082 | device_info->link_state = rndis_device->link_state; |
| @@ -961,7 +1085,66 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
| 961 | rndis_device->hw_mac_adr, | 1085 | rndis_device->hw_mac_adr, |
| 962 | device_info->link_state ? "down" : "up"); | 1086 | device_info->link_state ? "down" : "up"); |
| 963 | 1087 | ||
| 964 | return ret; | 1088 | if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5) |
| 1089 | return 0; | ||
| 1090 | |||
| 1091 | /* vRSS setup */ | ||
| 1092 | memset(&rsscap, 0, rsscap_size); | ||
| 1093 | ret = rndis_filter_query_device(rndis_device, | ||
| 1094 | OID_GEN_RECEIVE_SCALE_CAPABILITIES, | ||
| 1095 | &rsscap, &rsscap_size); | ||
| 1096 | if (ret || rsscap.num_recv_que < 2) | ||
| 1097 | goto out; | ||
| 1098 | |||
| 1099 | net_device->num_chn = (num_online_cpus() < rsscap.num_recv_que) ? | ||
| 1100 | num_online_cpus() : rsscap.num_recv_que; | ||
| 1101 | if (net_device->num_chn == 1) | ||
| 1102 | goto out; | ||
| 1103 | |||
| 1104 | net_device->sub_cb_buf = vzalloc((net_device->num_chn - 1) * | ||
| 1105 | NETVSC_PACKET_SIZE); | ||
| 1106 | if (!net_device->sub_cb_buf) { | ||
| 1107 | net_device->num_chn = 1; | ||
| 1108 | dev_info(&dev->device, "No memory for subchannels.\n"); | ||
| 1109 | goto out; | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); | ||
| 1113 | |||
| 1114 | init_packet = &net_device->channel_init_pkt; | ||
| 1115 | memset(init_packet, 0, sizeof(struct nvsp_message)); | ||
| 1116 | init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL; | ||
| 1117 | init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE; | ||
| 1118 | init_packet->msg.v5_msg.subchn_req.num_subchannels = | ||
| 1119 | net_device->num_chn - 1; | ||
| 1120 | ret = vmbus_sendpacket(dev->channel, init_packet, | ||
| 1121 | sizeof(struct nvsp_message), | ||
| 1122 | (unsigned long)init_packet, | ||
| 1123 | VM_PKT_DATA_INBAND, | ||
| 1124 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | ||
| 1125 | if (ret) | ||
| 1126 | goto out; | ||
| 1127 | t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); | ||
| 1128 | if (t == 0) { | ||
| 1129 | ret = -ETIMEDOUT; | ||
| 1130 | goto out; | ||
| 1131 | } | ||
| 1132 | if (init_packet->msg.v5_msg.subchn_comp.status != | ||
| 1133 | NVSP_STAT_SUCCESS) { | ||
| 1134 | ret = -ENODEV; | ||
| 1135 | goto out; | ||
| 1136 | } | ||
| 1137 | net_device->num_chn = 1 + | ||
| 1138 | init_packet->msg.v5_msg.subchn_comp.num_subchannels; | ||
| 1139 | |||
| 1140 | vmbus_are_subchannels_present(dev->channel); | ||
| 1141 | |||
| 1142 | ret = rndis_filter_set_rss_param(rndis_device, net_device->num_chn); | ||
| 1143 | |||
| 1144 | out: | ||
| 1145 | if (ret) | ||
| 1146 | net_device->num_chn = 1; | ||
| 1147 | return 0; /* return 0 because primary channel can be used alone */ | ||
| 965 | 1148 | ||
| 966 | err_dev_remv: | 1149 | err_dev_remv: |
| 967 | rndis_filter_device_remove(dev); | 1150 | rndis_filter_device_remove(dev); |
