diff options
author | David S. Miller <davem@davemloft.net> | 2014-03-10 15:52:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-03-10 15:52:17 -0400 |
commit | e3ca64948b50d05304a33ad8c4332e2a748e9401 (patch) | |
tree | 5420a2dc396c09173940c73d761f0aea0fd6bed5 | |
parent | 3ee2f8ce1ab8f235bda164295fa0cf39ec1c2400 (diff) | |
parent | 77bf5487946254798ed7f265877939c703189f1e (diff) |
Merge branch 'hyperv-next'
K. Y. Srinivasan says:
====================
Drivers: net: hyperv: Enable various offloads
This patch set enables both checksum as well as segmentation offload.
As part of this effort I have enabled scatter gather I/O a well.
In version 2 of these patches, I addressed comments from David Miller and
Dan Carpenter.
In this version I have addressed the latest comments from David Miller.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 145 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 333 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 150 |
3 files changed, 504 insertions, 124 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 39fc230f5c20..7d06b4959383 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | /* Fwd declaration */ | 31 | /* Fwd declaration */ |
32 | struct hv_netvsc_packet; | 32 | struct hv_netvsc_packet; |
33 | struct ndis_tcp_ip_checksum_info; | ||
33 | 34 | ||
34 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ | 35 | /* Represent the xfer page packet which contains 1 or more netvsc packet */ |
35 | struct xferpage_packet { | 36 | struct xferpage_packet { |
@@ -73,7 +74,7 @@ struct hv_netvsc_packet { | |||
73 | } completion; | 74 | } completion; |
74 | 75 | ||
75 | /* This points to the memory after page_buf */ | 76 | /* This points to the memory after page_buf */ |
76 | void *extension; | 77 | struct rndis_message *rndis_msg; |
77 | 78 | ||
78 | u32 total_data_buflen; | 79 | u32 total_data_buflen; |
79 | /* Points to the send/receive buffer where the ethernet frame is */ | 80 | /* Points to the send/receive buffer where the ethernet frame is */ |
@@ -117,7 +118,8 @@ int netvsc_send(struct hv_device *device, | |||
117 | void netvsc_linkstatus_callback(struct hv_device *device_obj, | 118 | void netvsc_linkstatus_callback(struct hv_device *device_obj, |
118 | unsigned int status); | 119 | unsigned int status); |
119 | int netvsc_recv_callback(struct hv_device *device_obj, | 120 | int netvsc_recv_callback(struct hv_device *device_obj, |
120 | struct hv_netvsc_packet *packet); | 121 | struct hv_netvsc_packet *packet, |
122 | struct ndis_tcp_ip_checksum_info *csum_info); | ||
121 | int rndis_filter_open(struct hv_device *dev); | 123 | int rndis_filter_open(struct hv_device *dev); |
122 | int rndis_filter_close(struct hv_device *dev); | 124 | int rndis_filter_close(struct hv_device *dev); |
123 | int rndis_filter_device_add(struct hv_device *dev, | 125 | int rndis_filter_device_add(struct hv_device *dev, |
@@ -126,11 +128,6 @@ void rndis_filter_device_remove(struct hv_device *dev); | |||
126 | int rndis_filter_receive(struct hv_device *dev, | 128 | int rndis_filter_receive(struct hv_device *dev, |
127 | struct hv_netvsc_packet *pkt); | 129 | struct hv_netvsc_packet *pkt); |
128 | 130 | ||
129 | |||
130 | |||
131 | int rndis_filter_send(struct hv_device *dev, | ||
132 | struct hv_netvsc_packet *pkt); | ||
133 | |||
134 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); | 131 | int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); |
135 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); | 132 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); |
136 | 133 | ||
@@ -726,9 +723,133 @@ struct ndis_pkt_8021q_info { | |||
726 | }; | 723 | }; |
727 | }; | 724 | }; |
728 | 725 | ||
726 | struct ndis_oject_header { | ||
727 | u8 type; | ||
728 | u8 revision; | ||
729 | u16 size; | ||
730 | }; | ||
731 | |||
732 | #define NDIS_OBJECT_TYPE_DEFAULT 0x80 | ||
733 | #define NDIS_OFFLOAD_PARAMETERS_REVISION_3 3 | ||
734 | #define NDIS_OFFLOAD_PARAMETERS_NO_CHANGE 0 | ||
735 | #define NDIS_OFFLOAD_PARAMETERS_LSOV2_DISABLED 1 | ||
736 | #define NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED 2 | ||
737 | #define NDIS_OFFLOAD_PARAMETERS_LSOV1_ENABLED 2 | ||
738 | #define NDIS_OFFLOAD_PARAMETERS_RSC_DISABLED 1 | ||
739 | #define NDIS_OFFLOAD_PARAMETERS_RSC_ENABLED 2 | ||
740 | #define NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED 1 | ||
741 | #define NDIS_OFFLOAD_PARAMETERS_TX_ENABLED_RX_DISABLED 2 | ||
742 | #define NDIS_OFFLOAD_PARAMETERS_RX_ENABLED_TX_DISABLED 3 | ||
743 | #define NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED 4 | ||
744 | |||
745 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE 1 | ||
746 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4 0 | ||
747 | #define NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6 1 | ||
748 | |||
749 | /* | ||
750 | * New offload OIDs for NDIS 6 | ||
751 | */ | ||
752 | #define OID_TCP_OFFLOAD_CURRENT_CONFIG 0xFC01020B /* query only */ | ||
753 | #define OID_TCP_OFFLOAD_PARAMETERS 0xFC01020C /* set only */ | ||
754 | #define OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020D/* query only */ | ||
755 | #define OID_TCP_CONNECTION_OFFLOAD_CURRENT_CONFIG 0xFC01020E /* query only */ | ||
756 | #define OID_TCP_CONNECTION_OFFLOAD_HARDWARE_CAPABILITIES 0xFC01020F /* query */ | ||
757 | #define OID_OFFLOAD_ENCAPSULATION 0x0101010A /* set/query */ | ||
758 | |||
759 | struct ndis_offload_params { | ||
760 | struct ndis_oject_header header; | ||
761 | u8 ip_v4_csum; | ||
762 | u8 tcp_ip_v4_csum; | ||
763 | u8 udp_ip_v4_csum; | ||
764 | u8 tcp_ip_v6_csum; | ||
765 | u8 udp_ip_v6_csum; | ||
766 | u8 lso_v1; | ||
767 | u8 ip_sec_v1; | ||
768 | u8 lso_v2_ipv4; | ||
769 | u8 lso_v2_ipv6; | ||
770 | u8 tcp_connection_ip_v4; | ||
771 | u8 tcp_connection_ip_v6; | ||
772 | u32 flags; | ||
773 | u8 ip_sec_v2; | ||
774 | u8 ip_sec_v2_ip_v4; | ||
775 | struct { | ||
776 | u8 rsc_ip_v4; | ||
777 | u8 rsc_ip_v6; | ||
778 | }; | ||
779 | struct { | ||
780 | u8 encapsulated_packet_task_offload; | ||
781 | u8 encapsulation_types; | ||
782 | }; | ||
783 | }; | ||
784 | |||
785 | struct ndis_tcp_ip_checksum_info { | ||
786 | union { | ||
787 | struct { | ||
788 | u32 is_ipv4:1; | ||
789 | u32 is_ipv6:1; | ||
790 | u32 tcp_checksum:1; | ||
791 | u32 udp_checksum:1; | ||
792 | u32 ip_header_checksum:1; | ||
793 | u32 reserved:11; | ||
794 | u32 tcp_header_offset:10; | ||
795 | } transmit; | ||
796 | struct { | ||
797 | u32 tcp_checksum_failed:1; | ||
798 | u32 udp_checksum_failed:1; | ||
799 | u32 ip_checksum_failed:1; | ||
800 | u32 tcp_checksum_succeeded:1; | ||
801 | u32 udp_checksum_succeeded:1; | ||
802 | u32 ip_checksum_succeeded:1; | ||
803 | u32 loopback:1; | ||
804 | u32 tcp_checksum_value_invalid:1; | ||
805 | u32 ip_checksum_value_invalid:1; | ||
806 | } receive; | ||
807 | u32 value; | ||
808 | }; | ||
809 | }; | ||
810 | |||
811 | struct ndis_tcp_lso_info { | ||
812 | union { | ||
813 | struct { | ||
814 | u32 unused:30; | ||
815 | u32 type:1; | ||
816 | u32 reserved2:1; | ||
817 | } transmit; | ||
818 | struct { | ||
819 | u32 mss:20; | ||
820 | u32 tcp_header_offset:10; | ||
821 | u32 type:1; | ||
822 | u32 reserved2:1; | ||
823 | } lso_v1_transmit; | ||
824 | struct { | ||
825 | u32 tcp_payload:30; | ||
826 | u32 type:1; | ||
827 | u32 reserved2:1; | ||
828 | } lso_v1_transmit_complete; | ||
829 | struct { | ||
830 | u32 mss:20; | ||
831 | u32 tcp_header_offset:10; | ||
832 | u32 type:1; | ||
833 | u32 ip_version:1; | ||
834 | } lso_v2_transmit; | ||
835 | struct { | ||
836 | u32 reserved:30; | ||
837 | u32 type:1; | ||
838 | u32 reserved2:1; | ||
839 | } lso_v2_transmit_complete; | ||
840 | u32 value; | ||
841 | }; | ||
842 | }; | ||
843 | |||
729 | #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | 844 | #define NDIS_VLAN_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ |
730 | sizeof(struct ndis_pkt_8021q_info)) | 845 | sizeof(struct ndis_pkt_8021q_info)) |
731 | 846 | ||
847 | #define NDIS_CSUM_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | ||
848 | sizeof(struct ndis_tcp_ip_checksum_info)) | ||
849 | |||
850 | #define NDIS_LSO_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \ | ||
851 | sizeof(struct ndis_tcp_lso_info)) | ||
852 | |||
732 | /* Format of Information buffer passed in a SetRequest for the OID */ | 853 | /* Format of Information buffer passed in a SetRequest for the OID */ |
733 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ | 854 | /* OID_GEN_RNDIS_CONFIG_PARAMETER. */ |
734 | struct rndis_config_parameter_info { | 855 | struct rndis_config_parameter_info { |
@@ -954,6 +1075,16 @@ struct rndis_message { | |||
954 | #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 | 1075 | #define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 |
955 | #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 | 1076 | #define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 |
956 | 1077 | ||
1078 | #define INFO_IPV4 2 | ||
1079 | #define INFO_IPV6 4 | ||
1080 | #define INFO_TCP 2 | ||
1081 | #define INFO_UDP 4 | ||
1082 | |||
1083 | #define TRANSPORT_INFO_NOT_IP 0 | ||
1084 | #define TRANSPORT_INFO_IPV4_TCP ((INFO_IPV4 << 16) | INFO_TCP) | ||
1085 | #define TRANSPORT_INFO_IPV4_UDP ((INFO_IPV4 << 16) | INFO_UDP) | ||
1086 | #define TRANSPORT_INFO_IPV6_TCP ((INFO_IPV6 << 16) | INFO_TCP) | ||
1087 | #define TRANSPORT_INFO_IPV6_UDP ((INFO_IPV6 << 16) | INFO_UDP) | ||
957 | 1088 | ||
958 | 1089 | ||
959 | #endif /* _HYPERV_NET_H */ | 1090 | #endif /* _HYPERV_NET_H */ |
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 9ef6be90a81c..3d069901e6d9 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c | |||
@@ -128,6 +128,27 @@ static int netvsc_close(struct net_device *net) | |||
128 | return ret; | 128 | return ret; |
129 | } | 129 | } |
130 | 130 | ||
131 | static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, | ||
132 | int pkt_type) | ||
133 | { | ||
134 | struct rndis_packet *rndis_pkt; | ||
135 | struct rndis_per_packet_info *ppi; | ||
136 | |||
137 | rndis_pkt = &msg->msg.pkt; | ||
138 | rndis_pkt->data_offset += ppi_size; | ||
139 | |||
140 | ppi = (struct rndis_per_packet_info *)((void *)rndis_pkt + | ||
141 | rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_len); | ||
142 | |||
143 | ppi->size = ppi_size; | ||
144 | ppi->type = pkt_type; | ||
145 | ppi->ppi_offset = sizeof(struct rndis_per_packet_info); | ||
146 | |||
147 | rndis_pkt->per_pkt_info_len += ppi_size; | ||
148 | |||
149 | return ppi; | ||
150 | } | ||
151 | |||
131 | static void netvsc_xmit_completion(void *context) | 152 | static void netvsc_xmit_completion(void *context) |
132 | { | 153 | { |
133 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; | 154 | struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context; |
@@ -140,21 +161,163 @@ static void netvsc_xmit_completion(void *context) | |||
140 | dev_kfree_skb_any(skb); | 161 | dev_kfree_skb_any(skb); |
141 | } | 162 | } |
142 | 163 | ||
164 | static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, | ||
165 | struct hv_page_buffer *pb) | ||
166 | { | ||
167 | int j = 0; | ||
168 | |||
169 | /* Deal with compund pages by ignoring unused part | ||
170 | * of the page. | ||
171 | */ | ||
172 | page += (offset >> PAGE_SHIFT); | ||
173 | offset &= ~PAGE_MASK; | ||
174 | |||
175 | while (len > 0) { | ||
176 | unsigned long bytes; | ||
177 | |||
178 | bytes = PAGE_SIZE - offset; | ||
179 | if (bytes > len) | ||
180 | bytes = len; | ||
181 | pb[j].pfn = page_to_pfn(page); | ||
182 | pb[j].offset = offset; | ||
183 | pb[j].len = bytes; | ||
184 | |||
185 | offset += bytes; | ||
186 | len -= bytes; | ||
187 | |||
188 | if (offset == PAGE_SIZE && len) { | ||
189 | page++; | ||
190 | offset = 0; | ||
191 | j++; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | return j + 1; | ||
196 | } | ||
197 | |||
198 | static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, | ||
199 | struct hv_page_buffer *pb) | ||
200 | { | ||
201 | u32 slots_used = 0; | ||
202 | char *data = skb->data; | ||
203 | int frags = skb_shinfo(skb)->nr_frags; | ||
204 | int i; | ||
205 | |||
206 | /* The packet is laid out thus: | ||
207 | * 1. hdr | ||
208 | * 2. skb linear data | ||
209 | * 3. skb fragment data | ||
210 | */ | ||
211 | if (hdr != NULL) | ||
212 | slots_used += fill_pg_buf(virt_to_page(hdr), | ||
213 | offset_in_page(hdr), | ||
214 | len, &pb[slots_used]); | ||
215 | |||
216 | slots_used += fill_pg_buf(virt_to_page(data), | ||
217 | offset_in_page(data), | ||
218 | skb_headlen(skb), &pb[slots_used]); | ||
219 | |||
220 | for (i = 0; i < frags; i++) { | ||
221 | skb_frag_t *frag = skb_shinfo(skb)->frags + i; | ||
222 | |||
223 | slots_used += fill_pg_buf(skb_frag_page(frag), | ||
224 | frag->page_offset, | ||
225 | skb_frag_size(frag), &pb[slots_used]); | ||
226 | } | ||
227 | return slots_used; | ||
228 | } | ||
229 | |||
230 | static int count_skb_frag_slots(struct sk_buff *skb) | ||
231 | { | ||
232 | int i, frags = skb_shinfo(skb)->nr_frags; | ||
233 | int pages = 0; | ||
234 | |||
235 | for (i = 0; i < frags; i++) { | ||
236 | skb_frag_t *frag = skb_shinfo(skb)->frags + i; | ||
237 | unsigned long size = skb_frag_size(frag); | ||
238 | unsigned long offset = frag->page_offset; | ||
239 | |||
240 | /* Skip unused frames from start of page */ | ||
241 | offset &= ~PAGE_MASK; | ||
242 | pages += PFN_UP(offset + size); | ||
243 | } | ||
244 | return pages; | ||
245 | } | ||
246 | |||
247 | static int netvsc_get_slots(struct sk_buff *skb) | ||
248 | { | ||
249 | char *data = skb->data; | ||
250 | unsigned int offset = offset_in_page(data); | ||
251 | unsigned int len = skb_headlen(skb); | ||
252 | int slots; | ||
253 | int frag_slots; | ||
254 | |||
255 | slots = DIV_ROUND_UP(offset + len, PAGE_SIZE); | ||
256 | frag_slots = count_skb_frag_slots(skb); | ||
257 | return slots + frag_slots; | ||
258 | } | ||
259 | |||
260 | static u32 get_net_transport_info(struct sk_buff *skb, u32 *trans_off) | ||
261 | { | ||
262 | u32 ret_val = TRANSPORT_INFO_NOT_IP; | ||
263 | |||
264 | if ((eth_hdr(skb)->h_proto != htons(ETH_P_IP)) && | ||
265 | (eth_hdr(skb)->h_proto != htons(ETH_P_IPV6))) { | ||
266 | goto not_ip; | ||
267 | } | ||
268 | |||
269 | *trans_off = skb_transport_offset(skb); | ||
270 | |||
271 | if ((eth_hdr(skb)->h_proto == htons(ETH_P_IP))) { | ||
272 | struct iphdr *iphdr = ip_hdr(skb); | ||
273 | |||
274 | if (iphdr->protocol == IPPROTO_TCP) | ||
275 | ret_val = TRANSPORT_INFO_IPV4_TCP; | ||
276 | else if (iphdr->protocol == IPPROTO_UDP) | ||
277 | ret_val = TRANSPORT_INFO_IPV4_UDP; | ||
278 | } else { | ||
279 | if (ipv6_hdr(skb)->nexthdr == IPPROTO_TCP) | ||
280 | ret_val = TRANSPORT_INFO_IPV6_TCP; | ||
281 | else if (ipv6_hdr(skb)->nexthdr == IPPROTO_UDP) | ||
282 | ret_val = TRANSPORT_INFO_IPV6_UDP; | ||
283 | } | ||
284 | |||
285 | not_ip: | ||
286 | return ret_val; | ||
287 | } | ||
288 | |||
143 | static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | 289 | static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) |
144 | { | 290 | { |
145 | struct net_device_context *net_device_ctx = netdev_priv(net); | 291 | struct net_device_context *net_device_ctx = netdev_priv(net); |
146 | struct hv_netvsc_packet *packet; | 292 | struct hv_netvsc_packet *packet; |
147 | int ret; | 293 | int ret; |
148 | unsigned int i, num_pages, npg_data; | 294 | unsigned int num_data_pgs; |
149 | 295 | struct rndis_message *rndis_msg; | |
150 | /* Add multipages for skb->data and additional 2 for RNDIS */ | 296 | struct rndis_packet *rndis_pkt; |
151 | npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) | 297 | u32 rndis_msg_size; |
152 | >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; | 298 | bool isvlan; |
153 | num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; | 299 | struct rndis_per_packet_info *ppi; |
300 | struct ndis_tcp_ip_checksum_info *csum_info; | ||
301 | struct ndis_tcp_lso_info *lso_info; | ||
302 | int hdr_offset; | ||
303 | u32 net_trans_info; | ||
304 | |||
305 | |||
306 | /* We will atmost need two pages to describe the rndis | ||
307 | * header. We can only transmit MAX_PAGE_BUFFER_COUNT number | ||
308 | * of pages in a single packet. | ||
309 | */ | ||
310 | num_data_pgs = netvsc_get_slots(skb) + 2; | ||
311 | if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { | ||
312 | netdev_err(net, "Packet too big: %u\n", skb->len); | ||
313 | dev_kfree_skb(skb); | ||
314 | net->stats.tx_dropped++; | ||
315 | return NETDEV_TX_OK; | ||
316 | } | ||
154 | 317 | ||
155 | /* Allocate a netvsc packet based on # of frags. */ | 318 | /* Allocate a netvsc packet based on # of frags. */ |
156 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | 319 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + |
157 | (num_pages * sizeof(struct hv_page_buffer)) + | 320 | (num_data_pgs * sizeof(struct hv_page_buffer)) + |
158 | sizeof(struct rndis_message) + | 321 | sizeof(struct rndis_message) + |
159 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); | 322 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); |
160 | if (!packet) { | 323 | if (!packet) { |
@@ -168,53 +331,111 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
168 | 331 | ||
169 | packet->vlan_tci = skb->vlan_tci; | 332 | packet->vlan_tci = skb->vlan_tci; |
170 | 333 | ||
171 | packet->extension = (void *)(unsigned long)packet + | 334 | packet->is_data_pkt = true; |
335 | packet->total_data_buflen = skb->len; | ||
336 | |||
337 | packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + | ||
172 | sizeof(struct hv_netvsc_packet) + | 338 | sizeof(struct hv_netvsc_packet) + |
173 | (num_pages * sizeof(struct hv_page_buffer)); | 339 | (num_data_pgs * sizeof(struct hv_page_buffer))); |
340 | |||
341 | /* Set the completion routine */ | ||
342 | packet->completion.send.send_completion = netvsc_xmit_completion; | ||
343 | packet->completion.send.send_completion_ctx = packet; | ||
344 | packet->completion.send.send_completion_tid = (unsigned long)skb; | ||
174 | 345 | ||
175 | /* If the rndis msg goes beyond 1 page, we will add 1 later */ | 346 | isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; |
176 | packet->page_buf_cnt = num_pages - 1; | 347 | |
348 | /* Add the rndis header */ | ||
349 | rndis_msg = packet->rndis_msg; | ||
350 | rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; | ||
351 | rndis_msg->msg_len = packet->total_data_buflen; | ||
352 | rndis_pkt = &rndis_msg->msg.pkt; | ||
353 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | ||
354 | rndis_pkt->data_len = packet->total_data_buflen; | ||
355 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
356 | |||
357 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
358 | |||
359 | if (isvlan) { | ||
360 | struct ndis_pkt_8021q_info *vlan; | ||
361 | |||
362 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
363 | ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE, | ||
364 | IEEE_8021Q_INFO); | ||
365 | vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + | ||
366 | ppi->ppi_offset); | ||
367 | vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK; | ||
368 | vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >> | ||
369 | VLAN_PRIO_SHIFT; | ||
370 | } | ||
177 | 371 | ||
178 | /* Initialize it from the skb */ | 372 | net_trans_info = get_net_transport_info(skb, &hdr_offset); |
179 | packet->total_data_buflen = skb->len; | 373 | if (net_trans_info == TRANSPORT_INFO_NOT_IP) |
374 | goto do_send; | ||
375 | |||
376 | /* | ||
377 | * Setup the sendside checksum offload only if this is not a | ||
378 | * GSO packet. | ||
379 | */ | ||
380 | if (skb_is_gso(skb)) | ||
381 | goto do_lso; | ||
382 | |||
383 | rndis_msg_size += NDIS_CSUM_PPI_SIZE; | ||
384 | ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE, | ||
385 | TCPIP_CHKSUM_PKTINFO); | ||
386 | |||
387 | csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi + | ||
388 | ppi->ppi_offset); | ||
180 | 389 | ||
181 | /* Start filling in the page buffers starting after RNDIS buffer. */ | 390 | if (net_trans_info & (INFO_IPV4 << 16)) |
182 | packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT; | 391 | csum_info->transmit.is_ipv4 = 1; |
183 | packet->page_buf[1].offset | ||
184 | = (unsigned long)skb->data & (PAGE_SIZE - 1); | ||
185 | if (npg_data == 1) | ||
186 | packet->page_buf[1].len = skb_headlen(skb); | ||
187 | else | 392 | else |
188 | packet->page_buf[1].len = PAGE_SIZE | 393 | csum_info->transmit.is_ipv6 = 1; |
189 | - packet->page_buf[1].offset; | 394 | |
190 | 395 | if (net_trans_info & INFO_TCP) { | |
191 | for (i = 2; i <= npg_data; i++) { | 396 | csum_info->transmit.tcp_checksum = 1; |
192 | packet->page_buf[i].pfn = virt_to_phys(skb->data | 397 | csum_info->transmit.tcp_header_offset = hdr_offset; |
193 | + PAGE_SIZE * (i-1)) >> PAGE_SHIFT; | 398 | } else if (net_trans_info & INFO_UDP) { |
194 | packet->page_buf[i].offset = 0; | 399 | csum_info->transmit.udp_checksum = 1; |
195 | packet->page_buf[i].len = PAGE_SIZE; | ||
196 | } | 400 | } |
197 | if (npg_data > 1) | 401 | goto do_send; |
198 | packet->page_buf[npg_data].len = (((unsigned long)skb->data | 402 | |
199 | + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1; | 403 | do_lso: |
200 | 404 | rndis_msg_size += NDIS_LSO_PPI_SIZE; | |
201 | /* Additional fragments are after SKB data */ | 405 | ppi = init_ppi_data(rndis_msg, NDIS_LSO_PPI_SIZE, |
202 | for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { | 406 | TCP_LARGESEND_PKTINFO); |
203 | const skb_frag_t *f = &skb_shinfo(skb)->frags[i]; | 407 | |
204 | 408 | lso_info = (struct ndis_tcp_lso_info *)((void *)ppi + | |
205 | packet->page_buf[i+npg_data+1].pfn = | 409 | ppi->ppi_offset); |
206 | page_to_pfn(skb_frag_page(f)); | 410 | |
207 | packet->page_buf[i+npg_data+1].offset = f->page_offset; | 411 | lso_info->lso_v2_transmit.type = NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE; |
208 | packet->page_buf[i+npg_data+1].len = skb_frag_size(f); | 412 | if (net_trans_info & (INFO_IPV4 << 16)) { |
413 | lso_info->lso_v2_transmit.ip_version = | ||
414 | NDIS_TCP_LARGE_SEND_OFFLOAD_IPV4; | ||
415 | ip_hdr(skb)->tot_len = 0; | ||
416 | ip_hdr(skb)->check = 0; | ||
417 | tcp_hdr(skb)->check = | ||
418 | ~csum_tcpudp_magic(ip_hdr(skb)->saddr, | ||
419 | ip_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); | ||
420 | } else { | ||
421 | lso_info->lso_v2_transmit.ip_version = | ||
422 | NDIS_TCP_LARGE_SEND_OFFLOAD_IPV6; | ||
423 | ipv6_hdr(skb)->payload_len = 0; | ||
424 | tcp_hdr(skb)->check = | ||
425 | ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, | ||
426 | &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); | ||
209 | } | 427 | } |
428 | lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset; | ||
429 | lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size; | ||
210 | 430 | ||
211 | /* Set the completion routine */ | 431 | do_send: |
212 | packet->completion.send.send_completion = netvsc_xmit_completion; | 432 | /* Start filling in the page buffers with the rndis hdr */ |
213 | packet->completion.send.send_completion_ctx = packet; | 433 | rndis_msg->msg_len += rndis_msg_size; |
214 | packet->completion.send.send_completion_tid = (unsigned long)skb; | 434 | packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, |
435 | skb, &packet->page_buf[0]); | ||
436 | |||
437 | ret = netvsc_send(net_device_ctx->device_ctx, packet); | ||
215 | 438 | ||
216 | ret = rndis_filter_send(net_device_ctx->device_ctx, | ||
217 | packet); | ||
218 | if (ret == 0) { | 439 | if (ret == 0) { |
219 | net->stats.tx_bytes += skb->len; | 440 | net->stats.tx_bytes += skb->len; |
220 | net->stats.tx_packets++; | 441 | net->stats.tx_packets++; |
@@ -264,7 +485,8 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, | |||
264 | * "wire" on the specified device. | 485 | * "wire" on the specified device. |
265 | */ | 486 | */ |
266 | int netvsc_recv_callback(struct hv_device *device_obj, | 487 | int netvsc_recv_callback(struct hv_device *device_obj, |
267 | struct hv_netvsc_packet *packet) | 488 | struct hv_netvsc_packet *packet, |
489 | struct ndis_tcp_ip_checksum_info *csum_info) | ||
268 | { | 490 | { |
269 | struct net_device *net; | 491 | struct net_device *net; |
270 | struct sk_buff *skb; | 492 | struct sk_buff *skb; |
@@ -291,7 +513,17 @@ int netvsc_recv_callback(struct hv_device *device_obj, | |||
291 | packet->total_data_buflen); | 513 | packet->total_data_buflen); |
292 | 514 | ||
293 | skb->protocol = eth_type_trans(skb, net); | 515 | skb->protocol = eth_type_trans(skb, net); |
294 | skb->ip_summed = CHECKSUM_NONE; | 516 | if (csum_info) { |
517 | /* We only look at the IP checksum here. | ||
518 | * Should we be dropping the packet if checksum | ||
519 | * failed? How do we deal with other checksums - TCP/UDP? | ||
520 | */ | ||
521 | if (csum_info->receive.ip_checksum_succeeded) | ||
522 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
523 | else | ||
524 | skb->ip_summed = CHECKSUM_NONE; | ||
525 | } | ||
526 | |||
295 | if (packet->vlan_tci & VLAN_TAG_PRESENT) | 527 | if (packet->vlan_tci & VLAN_TAG_PRESENT) |
296 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), | 528 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), |
297 | packet->vlan_tci); | 529 | packet->vlan_tci); |
@@ -450,9 +682,10 @@ static int netvsc_probe(struct hv_device *dev, | |||
450 | 682 | ||
451 | net->netdev_ops = &device_ops; | 683 | net->netdev_ops = &device_ops; |
452 | 684 | ||
453 | /* TODO: Add GSO and Checksum offload */ | 685 | net->hw_features = NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_IP_CSUM | |
454 | net->hw_features = 0; | 686 | NETIF_F_TSO; |
455 | net->features = NETIF_F_HW_VLAN_CTAG_TX; | 687 | net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_RXCSUM | |
688 | NETIF_F_IP_CSUM | NETIF_F_TSO; | ||
456 | 689 | ||
457 | SET_ETHTOOL_OPS(net, ðtool_ops); | 690 | SET_ETHTOOL_OPS(net, ðtool_ops); |
458 | SET_NETDEV_DEV(net, &dev->device); | 691 | SET_NETDEV_DEV(net, &dev->device); |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index f0cc8ef21e1c..54553df74cd2 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
@@ -350,6 +350,7 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
350 | struct rndis_packet *rndis_pkt; | 350 | struct rndis_packet *rndis_pkt; |
351 | u32 data_offset; | 351 | u32 data_offset; |
352 | struct ndis_pkt_8021q_info *vlan; | 352 | struct ndis_pkt_8021q_info *vlan; |
353 | struct ndis_tcp_ip_checksum_info *csum_info; | ||
353 | 354 | ||
354 | rndis_pkt = &msg->msg.pkt; | 355 | rndis_pkt = &msg->msg.pkt; |
355 | 356 | ||
@@ -388,7 +389,8 @@ static void rndis_filter_receive_data(struct rndis_device *dev, | |||
388 | pkt->vlan_tci = 0; | 389 | pkt->vlan_tci = 0; |
389 | } | 390 | } |
390 | 391 | ||
391 | netvsc_recv_callback(dev->net_dev->dev, pkt); | 392 | csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); |
393 | netvsc_recv_callback(dev->net_dev->dev, pkt, csum_info); | ||
392 | } | 394 | } |
393 | 395 | ||
394 | int rndis_filter_receive(struct hv_device *dev, | 396 | int rndis_filter_receive(struct hv_device *dev, |
@@ -607,6 +609,61 @@ cleanup: | |||
607 | return ret; | 609 | return ret; |
608 | } | 610 | } |
609 | 611 | ||
612 | int rndis_filter_set_offload_params(struct hv_device *hdev, | ||
613 | struct ndis_offload_params *req_offloads) | ||
614 | { | ||
615 | struct netvsc_device *nvdev = hv_get_drvdata(hdev); | ||
616 | struct rndis_device *rdev = nvdev->extension; | ||
617 | struct net_device *ndev = nvdev->ndev; | ||
618 | struct rndis_request *request; | ||
619 | struct rndis_set_request *set; | ||
620 | struct ndis_offload_params *offload_params; | ||
621 | struct rndis_set_complete *set_complete; | ||
622 | u32 extlen = sizeof(struct ndis_offload_params); | ||
623 | int ret, t; | ||
624 | |||
625 | request = get_rndis_request(rdev, RNDIS_MSG_SET, | ||
626 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); | ||
627 | if (!request) | ||
628 | return -ENOMEM; | ||
629 | |||
630 | set = &request->request_msg.msg.set_req; | ||
631 | set->oid = OID_TCP_OFFLOAD_PARAMETERS; | ||
632 | set->info_buflen = extlen; | ||
633 | set->info_buf_offset = sizeof(struct rndis_set_request); | ||
634 | set->dev_vc_handle = 0; | ||
635 | |||
636 | offload_params = (struct ndis_offload_params *)((ulong)set + | ||
637 | set->info_buf_offset); | ||
638 | *offload_params = *req_offloads; | ||
639 | offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT; | ||
640 | offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3; | ||
641 | offload_params->header.size = extlen; | ||
642 | |||
643 | ret = rndis_filter_send_request(rdev, request); | ||
644 | if (ret != 0) | ||
645 | goto cleanup; | ||
646 | |||
647 | t = wait_for_completion_timeout(&request->wait_event, 5*HZ); | ||
648 | if (t == 0) { | ||
649 | netdev_err(ndev, "timeout before we got aOFFLOAD set response...\n"); | ||
650 | /* can't put_rndis_request, since we may still receive a | ||
651 | * send-completion. | ||
652 | */ | ||
653 | return -EBUSY; | ||
654 | } else { | ||
655 | set_complete = &request->response_msg.msg.set_complete; | ||
656 | if (set_complete->status != RNDIS_STATUS_SUCCESS) { | ||
657 | netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", | ||
658 | set_complete->status); | ||
659 | ret = -EINVAL; | ||
660 | } | ||
661 | } | ||
662 | |||
663 | cleanup: | ||
664 | put_rndis_request(rdev, request); | ||
665 | return ret; | ||
666 | } | ||
610 | 667 | ||
611 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) | 668 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) |
612 | { | 669 | { |
@@ -807,6 +864,7 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
807 | struct netvsc_device *net_device; | 864 | struct netvsc_device *net_device; |
808 | struct rndis_device *rndis_device; | 865 | struct rndis_device *rndis_device; |
809 | struct netvsc_device_info *device_info = additional_info; | 866 | struct netvsc_device_info *device_info = additional_info; |
867 | struct ndis_offload_params offloads; | ||
810 | 868 | ||
811 | rndis_device = get_rndis_device(); | 869 | rndis_device = get_rndis_device(); |
812 | if (!rndis_device) | 870 | if (!rndis_device) |
@@ -846,6 +904,26 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
846 | 904 | ||
847 | memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); | 905 | memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN); |
848 | 906 | ||
907 | /* Turn on the offloads; the host supports all of the relevant | ||
908 | * offloads. | ||
909 | */ | ||
910 | memset(&offloads, 0, sizeof(struct ndis_offload_params)); | ||
911 | /* A value of zero means "no change"; now turn on what we | ||
912 | * want. | ||
913 | */ | ||
914 | offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
915 | offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
916 | offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
917 | offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
918 | offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED; | ||
919 | offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED; | ||
920 | |||
921 | |||
922 | ret = rndis_filter_set_offload_params(dev, &offloads); | ||
923 | if (ret) | ||
924 | goto err_dev_remv; | ||
925 | |||
926 | |||
849 | rndis_filter_query_device_link_status(rndis_device); | 927 | rndis_filter_query_device_link_status(rndis_device); |
850 | 928 | ||
851 | device_info->link_state = rndis_device->link_state; | 929 | device_info->link_state = rndis_device->link_state; |
@@ -855,6 +933,10 @@ int rndis_filter_device_add(struct hv_device *dev, | |||
855 | device_info->link_state ? "down" : "up"); | 933 | device_info->link_state ? "down" : "up"); |
856 | 934 | ||
857 | return ret; | 935 | return ret; |
936 | |||
937 | err_dev_remv: | ||
938 | rndis_filter_device_remove(dev); | ||
939 | return ret; | ||
858 | } | 940 | } |
859 | 941 | ||
860 | void rndis_filter_device_remove(struct hv_device *dev) | 942 | void rndis_filter_device_remove(struct hv_device *dev) |
@@ -891,69 +973,3 @@ int rndis_filter_close(struct hv_device *dev) | |||
891 | 973 | ||
892 | return rndis_filter_close_device(nvdev->extension); | 974 | return rndis_filter_close_device(nvdev->extension); |
893 | } | 975 | } |
894 | |||
895 | int rndis_filter_send(struct hv_device *dev, | ||
896 | struct hv_netvsc_packet *pkt) | ||
897 | { | ||
898 | struct rndis_message *rndis_msg; | ||
899 | struct rndis_packet *rndis_pkt; | ||
900 | u32 rndis_msg_size; | ||
901 | bool isvlan = pkt->vlan_tci & VLAN_TAG_PRESENT; | ||
902 | |||
903 | /* Add the rndis header */ | ||
904 | rndis_msg = (struct rndis_message *)pkt->extension; | ||
905 | |||
906 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
907 | if (isvlan) | ||
908 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
909 | |||
910 | rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; | ||
911 | rndis_msg->msg_len = pkt->total_data_buflen + | ||
912 | rndis_msg_size; | ||
913 | |||
914 | rndis_pkt = &rndis_msg->msg.pkt; | ||
915 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | ||
916 | if (isvlan) | ||
917 | rndis_pkt->data_offset += NDIS_VLAN_PPI_SIZE; | ||
918 | rndis_pkt->data_len = pkt->total_data_buflen; | ||
919 | |||
920 | if (isvlan) { | ||
921 | struct rndis_per_packet_info *ppi; | ||
922 | struct ndis_pkt_8021q_info *vlan; | ||
923 | |||
924 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
925 | rndis_pkt->per_pkt_info_len = NDIS_VLAN_PPI_SIZE; | ||
926 | |||
927 | ppi = (struct rndis_per_packet_info *)((ulong)rndis_pkt + | ||
928 | rndis_pkt->per_pkt_info_offset); | ||
929 | ppi->size = NDIS_VLAN_PPI_SIZE; | ||
930 | ppi->type = IEEE_8021Q_INFO; | ||
931 | ppi->ppi_offset = sizeof(struct rndis_per_packet_info); | ||
932 | |||
933 | vlan = (struct ndis_pkt_8021q_info *)((ulong)ppi + | ||
934 | ppi->ppi_offset); | ||
935 | vlan->vlanid = pkt->vlan_tci & VLAN_VID_MASK; | ||
936 | vlan->pri = (pkt->vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; | ||
937 | } | ||
938 | |||
939 | pkt->is_data_pkt = true; | ||
940 | pkt->page_buf[0].pfn = virt_to_phys(rndis_msg) >> PAGE_SHIFT; | ||
941 | pkt->page_buf[0].offset = | ||
942 | (unsigned long)rndis_msg & (PAGE_SIZE-1); | ||
943 | pkt->page_buf[0].len = rndis_msg_size; | ||
944 | |||
945 | /* Add one page_buf if the rndis msg goes beyond page boundary */ | ||
946 | if (pkt->page_buf[0].offset + rndis_msg_size > PAGE_SIZE) { | ||
947 | int i; | ||
948 | for (i = pkt->page_buf_cnt; i > 1; i--) | ||
949 | pkt->page_buf[i] = pkt->page_buf[i-1]; | ||
950 | pkt->page_buf_cnt++; | ||
951 | pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; | ||
952 | pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) | ||
953 | rndis_msg + pkt->page_buf[0].len)) >> PAGE_SHIFT; | ||
954 | pkt->page_buf[1].offset = 0; | ||
955 | pkt->page_buf[1].len = rndis_msg_size - pkt->page_buf[0].len; | ||
956 | } | ||
957 | |||
958 | return netvsc_send(dev, pkt); | ||
959 | } | ||