diff options
author | Haiyang Zhang <haiyangz@microsoft.com> | 2014-04-21 17:54:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-04-23 14:48:39 -0400 |
commit | 4baab26129e0540746744232022110dbe9e011e7 (patch) | |
tree | 191b15d289faf1ba4cdaf6005156cbe87a920901 /drivers/net/hyperv | |
parent | fd0d192be6e814495aec91f357b5801afc3b6262 (diff) |
hyperv: Remove recv_pkt_list and lock
Removed recv_pkt_list and lock, and updated related code, so that
the locking overhead is reduced especially when multiple channels
are in use.
The recv_pkt_list isn't actually necessary because the packets are
processed sequentially in each channel. It has been replaced by a
local variable, and the related lock for this list is also removed.
The is_data_pkt field is not used in receive path, so its assignment
is cleaned up.
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 | 33 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 174 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 2 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 2 |
4 files changed, 13 insertions, 198 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 57eb3f906d64..a1af0f7711e2 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
@@ -119,27 +119,14 @@ struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */ | |||
119 | }; | 119 | }; |
120 | 120 | ||
121 | /* Fwd declaration */ | 121 | /* Fwd declaration */ |
122 | struct hv_netvsc_packet; | ||
123 | struct ndis_tcp_ip_checksum_info; | 122 | struct ndis_tcp_ip_checksum_info; |
124 | 123 | ||
125 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ | ||
126 | struct xferpage_packet { | ||
127 | struct list_head list_ent; | ||
128 | u32 status; | ||
129 | |||
130 | /* # of netvsc packets this xfer packet contains */ | ||
131 | u32 count; | ||
132 | |||
133 | struct vmbus_channel *channel; | ||
134 | }; | ||
135 | |||
136 | /* | 124 | /* |
137 | * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame | 125 | * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame |
138 | * within the RNDIS | 126 | * within the RNDIS |
139 | */ | 127 | */ |
140 | struct hv_netvsc_packet { | 128 | struct hv_netvsc_packet { |
141 | /* Bookkeeping stuff */ | 129 | /* Bookkeeping stuff */ |
142 | struct list_head list_ent; | ||
143 | u32 status; | 130 | u32 status; |
144 | 131 | ||
145 | struct hv_device *device; | 132 | struct hv_device *device; |
@@ -149,19 +136,8 @@ struct hv_netvsc_packet { | |||
149 | u16 q_idx; | 136 | u16 q_idx; |
150 | struct vmbus_channel *channel; | 137 | struct vmbus_channel *channel; |
151 | 138 | ||
152 | /* | ||
153 | * Valid only for receives when we break a xfer page packet | ||
154 | * into multiple netvsc packets | ||
155 | */ | ||
156 | struct xferpage_packet *xfer_page_pkt; | ||
157 | |||
158 | union { | 139 | union { |
159 | struct { | 140 | struct { |
160 | u64 recv_completion_tid; | ||
161 | void *recv_completion_ctx; | ||
162 | void (*recv_completion)(void *context); | ||
163 | } recv; | ||
164 | struct { | ||
165 | u64 send_completion_tid; | 141 | u64 send_completion_tid; |
166 | void *send_completion_ctx; | 142 | void *send_completion_ctx; |
167 | void (*send_completion)(void *context); | 143 | void (*send_completion)(void *context); |
@@ -613,9 +589,6 @@ struct nvsp_message { | |||
613 | 589 | ||
614 | #define NETVSC_RECEIVE_BUFFER_ID 0xcafe | 590 | #define NETVSC_RECEIVE_BUFFER_ID 0xcafe |
615 | 591 | ||
616 | /* Preallocated receive packets */ | ||
617 | #define NETVSC_RECEIVE_PACKETLIST_COUNT 256 | ||
618 | |||
619 | #define NETVSC_PACKET_SIZE 2048 | 592 | #define NETVSC_PACKET_SIZE 2048 |
620 | 593 | ||
621 | #define VRSS_SEND_TAB_SIZE 16 | 594 | #define VRSS_SEND_TAB_SIZE 16 |
@@ -630,12 +603,6 @@ struct netvsc_device { | |||
630 | wait_queue_head_t wait_drain; | 603 | wait_queue_head_t wait_drain; |
631 | bool start_remove; | 604 | bool start_remove; |
632 | bool destroy; | 605 | bool destroy; |
633 | /* | ||
634 | * List of free preallocated hv_netvsc_packet to represent receive | ||
635 | * packet | ||
636 | */ | ||
637 | struct list_head recv_pkt_list; | ||
638 | spinlock_t recv_pkt_list_lock; | ||
639 | 606 | ||
640 | /* Receive buffer allocated by us but manages by NetVSP */ | 607 | /* Receive buffer allocated by us but manages by NetVSP */ |
641 | void *recv_buf; | 608 | void *recv_buf; |
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index e7e77f12bc38..b10334773b32 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c | |||
@@ -387,7 +387,6 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device) | |||
387 | int netvsc_device_remove(struct hv_device *device) | 387 | int netvsc_device_remove(struct hv_device *device) |
388 | { | 388 | { |
389 | struct netvsc_device *net_device; | 389 | struct netvsc_device *net_device; |
390 | struct hv_netvsc_packet *netvsc_packet, *pos; | ||
391 | unsigned long flags; | 390 | unsigned long flags; |
392 | 391 | ||
393 | net_device = hv_get_drvdata(device); | 392 | net_device = hv_get_drvdata(device); |
@@ -416,12 +415,6 @@ int netvsc_device_remove(struct hv_device *device) | |||
416 | vmbus_close(device->channel); | 415 | vmbus_close(device->channel); |
417 | 416 | ||
418 | /* Release all resources */ | 417 | /* Release all resources */ |
419 | list_for_each_entry_safe(netvsc_packet, pos, | ||
420 | &net_device->recv_pkt_list, list_ent) { | ||
421 | list_del(&netvsc_packet->list_ent); | ||
422 | kfree(netvsc_packet); | ||
423 | } | ||
424 | |||
425 | if (net_device->sub_cb_buf) | 418 | if (net_device->sub_cb_buf) |
426 | vfree(net_device->sub_cb_buf); | 419 | vfree(net_device->sub_cb_buf); |
427 | 420 | ||
@@ -641,62 +634,6 @@ retry_send_cmplt: | |||
641 | } | 634 | } |
642 | } | 635 | } |
643 | 636 | ||
644 | /* Send a receive completion packet to RNDIS device (ie NetVsp) */ | ||
645 | static void netvsc_receive_completion(void *context) | ||
646 | { | ||
647 | struct hv_netvsc_packet *packet = context; | ||
648 | struct hv_device *device = packet->device; | ||
649 | struct vmbus_channel *channel; | ||
650 | struct netvsc_device *net_device; | ||
651 | u64 transaction_id = 0; | ||
652 | bool fsend_receive_comp = false; | ||
653 | unsigned long flags; | ||
654 | struct net_device *ndev; | ||
655 | u32 status = NVSP_STAT_NONE; | ||
656 | |||
657 | /* | ||
658 | * Even though it seems logical to do a GetOutboundNetDevice() here to | ||
659 | * send out receive completion, we are using GetInboundNetDevice() | ||
660 | * since we may have disable outbound traffic already. | ||
661 | */ | ||
662 | net_device = get_inbound_net_device(device); | ||
663 | if (!net_device) | ||
664 | return; | ||
665 | ndev = net_device->ndev; | ||
666 | |||
667 | /* Overloading use of the lock. */ | ||
668 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
669 | |||
670 | if (packet->status != NVSP_STAT_SUCCESS) | ||
671 | packet->xfer_page_pkt->status = NVSP_STAT_FAIL; | ||
672 | |||
673 | packet->xfer_page_pkt->count--; | ||
674 | |||
675 | /* | ||
676 | * Last one in the line that represent 1 xfer page packet. | ||
677 | * Return the xfer page packet itself to the freelist | ||
678 | */ | ||
679 | if (packet->xfer_page_pkt->count == 0) { | ||
680 | fsend_receive_comp = true; | ||
681 | channel = packet->xfer_page_pkt->channel; | ||
682 | transaction_id = packet->completion.recv.recv_completion_tid; | ||
683 | status = packet->xfer_page_pkt->status; | ||
684 | list_add_tail(&packet->xfer_page_pkt->list_ent, | ||
685 | &net_device->recv_pkt_list); | ||
686 | |||
687 | } | ||
688 | |||
689 | /* Put the packet back */ | ||
690 | list_add_tail(&packet->list_ent, &net_device->recv_pkt_list); | ||
691 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); | ||
692 | |||
693 | /* Send a receive completion for the xfer page packet */ | ||
694 | if (fsend_receive_comp) | ||
695 | netvsc_send_recv_completion(device, channel, net_device, | ||
696 | transaction_id, status); | ||
697 | |||
698 | } | ||
699 | |||
700 | static void netvsc_receive(struct netvsc_device *net_device, | 637 | static void netvsc_receive(struct netvsc_device *net_device, |
701 | struct vmbus_channel *channel, | 638 | struct vmbus_channel *channel, |
702 | struct hv_device *device, | 639 | struct hv_device *device, |
@@ -704,16 +641,13 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
704 | { | 641 | { |
705 | struct vmtransfer_page_packet_header *vmxferpage_packet; | 642 | struct vmtransfer_page_packet_header *vmxferpage_packet; |
706 | struct nvsp_message *nvsp_packet; | 643 | struct nvsp_message *nvsp_packet; |
707 | struct hv_netvsc_packet *netvsc_packet = NULL; | 644 | struct hv_netvsc_packet nv_pkt; |
708 | /* struct netvsc_driver *netvscDriver; */ | 645 | struct hv_netvsc_packet *netvsc_packet = &nv_pkt; |
709 | struct xferpage_packet *xferpage_packet = NULL; | 646 | u32 status = NVSP_STAT_SUCCESS; |
710 | int i; | 647 | int i; |
711 | int count = 0; | 648 | int count = 0; |
712 | unsigned long flags; | ||
713 | struct net_device *ndev; | 649 | struct net_device *ndev; |
714 | 650 | ||
715 | LIST_HEAD(listHead); | ||
716 | |||
717 | ndev = net_device->ndev; | 651 | ndev = net_device->ndev; |
718 | 652 | ||
719 | /* | 653 | /* |
@@ -746,78 +680,14 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
746 | return; | 680 | return; |
747 | } | 681 | } |
748 | 682 | ||
749 | /* | 683 | count = vmxferpage_packet->range_cnt; |
750 | * Grab free packets (range count + 1) to represent this xfer | 684 | netvsc_packet->device = device; |
751 | * page packet. +1 to represent the xfer page packet itself. | 685 | netvsc_packet->channel = channel; |
752 | * We grab it here so that we know exactly how many we can | ||
753 | * fulfil | ||
754 | */ | ||
755 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
756 | while (!list_empty(&net_device->recv_pkt_list)) { | ||
757 | list_move_tail(net_device->recv_pkt_list.next, &listHead); | ||
758 | if (++count == vmxferpage_packet->range_cnt + 1) | ||
759 | break; | ||
760 | } | ||
761 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, flags); | ||
762 | |||
763 | /* | ||
764 | * We need at least 2 netvsc pkts (1 to represent the xfer | ||
765 | * page and at least 1 for the range) i.e. we can handled | ||
766 | * some of the xfer page packet ranges... | ||
767 | */ | ||
768 | if (count < 2) { | ||
769 | netdev_err(ndev, "Got only %d netvsc pkt...needed " | ||
770 | "%d pkts. Dropping this xfer page packet completely!\n", | ||
771 | count, vmxferpage_packet->range_cnt + 1); | ||
772 | |||
773 | /* Return it to the freelist */ | ||
774 | spin_lock_irqsave(&net_device->recv_pkt_list_lock, flags); | ||
775 | for (i = count; i != 0; i--) { | ||
776 | list_move_tail(listHead.next, | ||
777 | &net_device->recv_pkt_list); | ||
778 | } | ||
779 | spin_unlock_irqrestore(&net_device->recv_pkt_list_lock, | ||
780 | flags); | ||
781 | |||
782 | netvsc_send_recv_completion(device, channel, net_device, | ||
783 | vmxferpage_packet->d.trans_id, | ||
784 | NVSP_STAT_FAIL); | ||
785 | |||
786 | return; | ||
787 | } | ||
788 | |||
789 | /* Remove the 1st packet to represent the xfer page packet itself */ | ||
790 | xferpage_packet = (struct xferpage_packet *)listHead.next; | ||
791 | list_del(&xferpage_packet->list_ent); | ||
792 | xferpage_packet->status = NVSP_STAT_SUCCESS; | ||
793 | xferpage_packet->channel = channel; | ||
794 | |||
795 | /* This is how much we can satisfy */ | ||
796 | xferpage_packet->count = count - 1; | ||
797 | |||
798 | if (xferpage_packet->count != vmxferpage_packet->range_cnt) { | ||
799 | netdev_err(ndev, "Needed %d netvsc pkts to satisfy " | ||
800 | "this xfer page...got %d\n", | ||
801 | vmxferpage_packet->range_cnt, xferpage_packet->count); | ||
802 | } | ||
803 | 686 | ||
804 | /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ | 687 | /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ |
805 | for (i = 0; i < (count - 1); i++) { | 688 | for (i = 0; i < count; i++) { |
806 | netvsc_packet = (struct hv_netvsc_packet *)listHead.next; | ||
807 | list_del(&netvsc_packet->list_ent); | ||
808 | |||
809 | /* Initialize the netvsc packet */ | 689 | /* Initialize the netvsc packet */ |
810 | netvsc_packet->status = NVSP_STAT_SUCCESS; | 690 | netvsc_packet->status = NVSP_STAT_SUCCESS; |
811 | netvsc_packet->xfer_page_pkt = xferpage_packet; | ||
812 | netvsc_packet->completion.recv.recv_completion = | ||
813 | netvsc_receive_completion; | ||
814 | netvsc_packet->completion.recv.recv_completion_ctx = | ||
815 | netvsc_packet; | ||
816 | netvsc_packet->device = device; | ||
817 | /* Save this so that we can send it back */ | ||
818 | netvsc_packet->completion.recv.recv_completion_tid = | ||
819 | vmxferpage_packet->d.trans_id; | ||
820 | |||
821 | netvsc_packet->data = (void *)((unsigned long)net_device-> | 691 | netvsc_packet->data = (void *)((unsigned long)net_device-> |
822 | recv_buf + vmxferpage_packet->ranges[i].byte_offset); | 692 | recv_buf + vmxferpage_packet->ranges[i].byte_offset); |
823 | netvsc_packet->total_data_buflen = | 693 | netvsc_packet->total_data_buflen = |
@@ -826,10 +696,12 @@ static void netvsc_receive(struct netvsc_device *net_device, | |||
826 | /* Pass it to the upper layer */ | 696 | /* Pass it to the upper layer */ |
827 | rndis_filter_receive(device, netvsc_packet); | 697 | rndis_filter_receive(device, netvsc_packet); |
828 | 698 | ||
829 | netvsc_receive_completion(netvsc_packet-> | 699 | if (netvsc_packet->status != NVSP_STAT_SUCCESS) |
830 | completion.recv.recv_completion_ctx); | 700 | status = NVSP_STAT_FAIL; |
831 | } | 701 | } |
832 | 702 | ||
703 | netvsc_send_recv_completion(device, channel, net_device, | ||
704 | vmxferpage_packet->d.trans_id, status); | ||
833 | } | 705 | } |
834 | 706 | ||
835 | 707 | ||
@@ -956,11 +828,9 @@ void netvsc_channel_cb(void *context) | |||
956 | int netvsc_device_add(struct hv_device *device, void *additional_info) | 828 | int netvsc_device_add(struct hv_device *device, void *additional_info) |
957 | { | 829 | { |
958 | int ret = 0; | 830 | int ret = 0; |
959 | int i; | ||
960 | int ring_size = | 831 | int ring_size = |
961 | ((struct netvsc_device_info *)additional_info)->ring_size; | 832 | ((struct netvsc_device_info *)additional_info)->ring_size; |
962 | struct netvsc_device *net_device; | 833 | struct netvsc_device *net_device; |
963 | struct hv_netvsc_packet *packet, *pos; | ||
964 | struct net_device *ndev; | 834 | struct net_device *ndev; |
965 | 835 | ||
966 | net_device = alloc_net_device(device); | 836 | net_device = alloc_net_device(device); |
@@ -981,18 +851,6 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) | |||
981 | ndev = net_device->ndev; | 851 | ndev = net_device->ndev; |
982 | 852 | ||
983 | /* Initialize the NetVSC channel extension */ | 853 | /* Initialize the NetVSC channel extension */ |
984 | spin_lock_init(&net_device->recv_pkt_list_lock); | ||
985 | |||
986 | INIT_LIST_HEAD(&net_device->recv_pkt_list); | ||
987 | |||
988 | for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) { | ||
989 | packet = kzalloc(sizeof(struct hv_netvsc_packet), GFP_KERNEL); | ||
990 | if (!packet) | ||
991 | break; | ||
992 | |||
993 | list_add_tail(&packet->list_ent, | ||
994 | &net_device->recv_pkt_list); | ||
995 | } | ||
996 | init_completion(&net_device->channel_init_wait); | 854 | init_completion(&net_device->channel_init_wait); |
997 | 855 | ||
998 | set_per_channel_state(device->channel, net_device->cb_buffer); | 856 | set_per_channel_state(device->channel, net_device->cb_buffer); |
@@ -1028,16 +886,8 @@ close: | |||
1028 | 886 | ||
1029 | cleanup: | 887 | cleanup: |
1030 | 888 | ||
1031 | if (net_device) { | 889 | if (net_device) |
1032 | list_for_each_entry_safe(packet, pos, | ||
1033 | &net_device->recv_pkt_list, | ||
1034 | list_ent) { | ||
1035 | list_del(&packet->list_ent); | ||
1036 | kfree(packet); | ||
1037 | } | ||
1038 | |||
1039 | kfree(net_device); | 890 | kfree(net_device); |
1040 | } | ||
1041 | 891 | ||
1042 | return ret; | 892 | return ret; |
1043 | } | 893 | } |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 093cf3fc46b8..8f6d53a2ed95 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
@@ -638,7 +638,7 @@ int netvsc_recv_callback(struct hv_device *device_obj, | |||
638 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), | 638 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
639 | packet->vlan_tci); | 639 | packet->vlan_tci); |
640 | 640 | ||
641 | skb_record_rx_queue(skb, packet->xfer_page_pkt->channel-> | 641 | skb_record_rx_queue(skb, packet->channel-> |
642 | offermsg.offer.sub_channel_index % | 642 | offermsg.offer.sub_channel_index % |
643 | net->real_num_rx_queues); | 643 | net->real_num_rx_queues); |
644 | 644 | ||
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index d92cfbe43410..48f5a0fbd674 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
@@ -401,8 +401,6 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
401 | pkt->total_data_buflen = rndis_pkt->data_len; | 401 | pkt->total_data_buflen = rndis_pkt->data_len; |
402 | pkt->data = (void *)((unsigned long)pkt->data + data_offset); | 402 | pkt->data = (void *)((unsigned long)pkt->data + data_offset); |
403 | 403 | ||
404 | pkt->is_data_pkt = true; | ||
405 | |||
406 | vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); | 404 | vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); |
407 | if (vlan) { | 405 | if (vlan) { |
408 | pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | | 406 | pkt->vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid | |