diff options
author | KY Srinivasan <kys@microsoft.com> | 2014-03-08 22:23:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-03-10 15:51:37 -0400 |
commit | 8a00251a3682a23649cef36949c8019f8589c021 (patch) | |
tree | 1419e8e548d3d61696e6618f2aabcf33773ecf47 /drivers/net/hyperv | |
parent | 54a7357f7ac408be4ef4ca82900bd24cb6789f36 (diff) |
Drivers: net: hyperv: Cleanup the send path
In preparation for enabling offloads, cleanup the send path.
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@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 | 7 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 88 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 66 |
3 files changed, 71 insertions, 90 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 39fc230f5c20..694bf7cada90 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
@@ -73,7 +73,7 @@ struct hv_netvsc_packet { | |||
73 | } completion; | 73 | } completion; |
74 | 74 | ||
75 | /* This points to the memory after page_buf */ | 75 | /* This points to the memory after page_buf */ |
76 | void *extension; | 76 | struct rndis_message *rndis_msg; |
77 | 77 | ||
78 | u32 total_data_buflen; | 78 | u32 total_data_buflen; |
79 | /* Points to the send/receive buffer where the ethernet frame is */ | 79 | /* Points to the send/receive buffer where the ethernet frame is */ |
@@ -126,11 +126,6 @@ void rndis_filter_device_remove(struct hv_device *dev); | |||
126 | int rndis_filter_receive(struct hv_device *dev, | 126 | int rndis_filter_receive(struct hv_device *dev, |
127 | struct hv_netvsc_packet *pkt); | 127 | struct hv_netvsc_packet *pkt); |
128 | 128 | ||
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); | 129 | 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); | 130 | int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); |
136 | 131 | ||
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1f7b12f9e6fb..7010c0630d24 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; |
@@ -174,8 +195,8 @@ static u32 fill_pg_buf(struct page *page, u32 offset, u32 len, | |||
174 | return j + 1; | 195 | return j + 1; |
175 | } | 196 | } |
176 | 197 | ||
177 | static void init_page_array(void *hdr, u32 len, struct sk_buff *skb, | 198 | static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, |
178 | struct hv_page_buffer *pb) | 199 | struct hv_page_buffer *pb) |
179 | { | 200 | { |
180 | u32 slots_used = 0; | 201 | u32 slots_used = 0; |
181 | char *data = skb->data; | 202 | char *data = skb->data; |
@@ -203,6 +224,7 @@ static void init_page_array(void *hdr, u32 len, struct sk_buff *skb, | |||
203 | frag->page_offset, | 224 | frag->page_offset, |
204 | skb_frag_size(frag), &pb[slots_used]); | 225 | skb_frag_size(frag), &pb[slots_used]); |
205 | } | 226 | } |
227 | return slots_used; | ||
206 | } | 228 | } |
207 | 229 | ||
208 | static int count_skb_frag_slots(struct sk_buff *skb) | 230 | static int count_skb_frag_slots(struct sk_buff *skb) |
@@ -240,14 +262,19 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
240 | struct net_device_context *net_device_ctx = netdev_priv(net); | 262 | struct net_device_context *net_device_ctx = netdev_priv(net); |
241 | struct hv_netvsc_packet *packet; | 263 | struct hv_netvsc_packet *packet; |
242 | int ret; | 264 | int ret; |
243 | unsigned int num_data_pages; | 265 | unsigned int num_data_pgs; |
266 | struct rndis_message *rndis_msg; | ||
267 | struct rndis_packet *rndis_pkt; | ||
268 | u32 rndis_msg_size; | ||
269 | bool isvlan; | ||
270 | struct rndis_per_packet_info *ppi; | ||
244 | 271 | ||
245 | /* We will atmost need two pages to describe the rndis | 272 | /* We will atmost need two pages to describe the rndis |
246 | * header. We can only transmit MAX_PAGE_BUFFER_COUNT number | 273 | * header. We can only transmit MAX_PAGE_BUFFER_COUNT number |
247 | * of pages in a single packet. | 274 | * of pages in a single packet. |
248 | */ | 275 | */ |
249 | num_data_pages = netvsc_get_slots(skb) + 2; | 276 | num_data_pgs = netvsc_get_slots(skb) + 2; |
250 | if (num_data_pages > MAX_PAGE_BUFFER_COUNT) { | 277 | if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { |
251 | netdev_err(net, "Packet too big: %u\n", skb->len); | 278 | netdev_err(net, "Packet too big: %u\n", skb->len); |
252 | dev_kfree_skb(skb); | 279 | dev_kfree_skb(skb); |
253 | net->stats.tx_dropped++; | 280 | net->stats.tx_dropped++; |
@@ -256,7 +283,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
256 | 283 | ||
257 | /* Allocate a netvsc packet based on # of frags. */ | 284 | /* Allocate a netvsc packet based on # of frags. */ |
258 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + | 285 | packet = kzalloc(sizeof(struct hv_netvsc_packet) + |
259 | (num_data_pages * sizeof(struct hv_page_buffer)) + | 286 | (num_data_pgs * sizeof(struct hv_page_buffer)) + |
260 | sizeof(struct rndis_message) + | 287 | sizeof(struct rndis_message) + |
261 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); | 288 | NDIS_VLAN_PPI_SIZE, GFP_ATOMIC); |
262 | if (!packet) { | 289 | if (!packet) { |
@@ -270,26 +297,51 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) | |||
270 | 297 | ||
271 | packet->vlan_tci = skb->vlan_tci; | 298 | packet->vlan_tci = skb->vlan_tci; |
272 | 299 | ||
273 | packet->extension = (void *)(unsigned long)packet + | 300 | packet->is_data_pkt = true; |
274 | sizeof(struct hv_netvsc_packet) + | ||
275 | (num_data_pages * sizeof(struct hv_page_buffer)); | ||
276 | |||
277 | /* If the rndis msg goes beyond 1 page, we will add 1 later */ | ||
278 | packet->page_buf_cnt = num_data_pages - 1; | ||
279 | |||
280 | /* Initialize it from the skb */ | ||
281 | packet->total_data_buflen = skb->len; | 301 | packet->total_data_buflen = skb->len; |
282 | 302 | ||
283 | /* Start filling in the page buffers starting after RNDIS buffer. */ | 303 | packet->rndis_msg = (struct rndis_message *)((unsigned long)packet + |
284 | init_page_array(NULL, 0, skb, &packet->page_buf[1]); | 304 | sizeof(struct hv_netvsc_packet) + |
305 | (num_data_pgs * sizeof(struct hv_page_buffer))); | ||
285 | 306 | ||
286 | /* Set the completion routine */ | 307 | /* Set the completion routine */ |
287 | packet->completion.send.send_completion = netvsc_xmit_completion; | 308 | packet->completion.send.send_completion = netvsc_xmit_completion; |
288 | packet->completion.send.send_completion_ctx = packet; | 309 | packet->completion.send.send_completion_ctx = packet; |
289 | packet->completion.send.send_completion_tid = (unsigned long)skb; | 310 | packet->completion.send.send_completion_tid = (unsigned long)skb; |
290 | 311 | ||
291 | ret = rndis_filter_send(net_device_ctx->device_ctx, | 312 | isvlan = packet->vlan_tci & VLAN_TAG_PRESENT; |
292 | packet); | 313 | |
314 | /* Add the rndis header */ | ||
315 | rndis_msg = packet->rndis_msg; | ||
316 | rndis_msg->ndis_msg_type = RNDIS_MSG_PACKET; | ||
317 | rndis_msg->msg_len = packet->total_data_buflen; | ||
318 | rndis_pkt = &rndis_msg->msg.pkt; | ||
319 | rndis_pkt->data_offset = sizeof(struct rndis_packet); | ||
320 | rndis_pkt->data_len = packet->total_data_buflen; | ||
321 | rndis_pkt->per_pkt_info_offset = sizeof(struct rndis_packet); | ||
322 | |||
323 | rndis_msg_size = RNDIS_MESSAGE_SIZE(struct rndis_packet); | ||
324 | |||
325 | if (isvlan) { | ||
326 | struct ndis_pkt_8021q_info *vlan; | ||
327 | |||
328 | rndis_msg_size += NDIS_VLAN_PPI_SIZE; | ||
329 | ppi = init_ppi_data(rndis_msg, NDIS_VLAN_PPI_SIZE, | ||
330 | IEEE_8021Q_INFO); | ||
331 | vlan = (struct ndis_pkt_8021q_info *)((void *)ppi + | ||
332 | ppi->ppi_offset); | ||
333 | vlan->vlanid = packet->vlan_tci & VLAN_VID_MASK; | ||
334 | vlan->pri = (packet->vlan_tci & VLAN_PRIO_MASK) >> | ||
335 | VLAN_PRIO_SHIFT; | ||
336 | } | ||
337 | |||
338 | /* Start filling in the page buffers with the rndis hdr */ | ||
339 | rndis_msg->msg_len += rndis_msg_size; | ||
340 | packet->page_buf_cnt = init_page_array(rndis_msg, rndis_msg_size, | ||
341 | skb, &packet->page_buf[0]); | ||
342 | |||
343 | ret = netvsc_send(net_device_ctx->device_ctx, packet); | ||
344 | |||
293 | if (ret == 0) { | 345 | if (ret == 0) { |
294 | net->stats.tx_bytes += skb->len; | 346 | net->stats.tx_bytes += skb->len; |
295 | net->stats.tx_packets++; | 347 | net->stats.tx_packets++; |
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index f0cc8ef21e1c..eaa149950af7 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c | |||
@@ -891,69 +891,3 @@ int rndis_filter_close(struct hv_device *dev) | |||
891 | 891 | ||
892 | return rndis_filter_close_device(nvdev->extension); | 892 | return rndis_filter_close_device(nvdev->extension); |
893 | } | 893 | } |
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 | } | ||