aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/hyperv/netvsc_drv.c
diff options
context:
space:
mode:
authorVitaly Kuznetsov <vkuznets@redhat.com>2015-04-08 11:54:06 -0400
committerDavid S. Miller <davem@davemloft.net>2015-04-08 12:27:25 -0400
commite88f7e078e47d4261a22e6f20a574620cbfc7a4b (patch)
treef305fdc53296701b966d07376edda69a3af7ec27 /drivers/net/hyperv/netvsc_drv.c
parent981a1bd85a959bb3b44e07c212ebc61c62ad7cf9 (diff)
hv_netvsc: try linearizing big SKBs before dropping them
In netvsc_start_xmit() we can handle packets which are scattered around not more than MAX_PAGE_BUFFER_COUNT-2 pages. It is, however, easy to create a packet which is not big in size but occupies more pages (e.g. if it uses frags on compound pages boundaries). When we drop such packet it cases sender to try resending it but in most cases it will try resending the same packet which will also get dropped, this will cause the particular connection to stick. To solve the issue we can try linearizing skb. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/hyperv/netvsc_drv.c')
-rw-r--r--drivers/net/hyperv/netvsc_drv.c25
1 files changed, 20 insertions, 5 deletions
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 9e4230d81ac0..448716787e73 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -377,27 +377,42 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
377 struct rndis_packet *rndis_pkt; 377 struct rndis_packet *rndis_pkt;
378 u32 rndis_msg_size; 378 u32 rndis_msg_size;
379 bool isvlan; 379 bool isvlan;
380 bool linear = false;
380 struct rndis_per_packet_info *ppi; 381 struct rndis_per_packet_info *ppi;
381 struct ndis_tcp_ip_checksum_info *csum_info; 382 struct ndis_tcp_ip_checksum_info *csum_info;
382 struct ndis_tcp_lso_info *lso_info; 383 struct ndis_tcp_lso_info *lso_info;
383 int hdr_offset; 384 int hdr_offset;
384 u32 net_trans_info; 385 u32 net_trans_info;
385 u32 hash; 386 u32 hash;
386 u32 skb_length = skb->len; 387 u32 skb_length;
387 u32 head_room = skb_headroom(skb); 388 u32 head_room;
388 u32 pkt_sz; 389 u32 pkt_sz;
389 struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; 390 struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
390 391
391 392
392 /* We will atmost need two pages to describe the rndis 393 /* We will atmost need two pages to describe the rndis
393 * header. We can only transmit MAX_PAGE_BUFFER_COUNT number 394 * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
394 * of pages in a single packet. 395 * of pages in a single packet. If skb is scattered around
396 * more pages we try linearizing it.
395 */ 397 */
398
399check_size:
400 skb_length = skb->len;
401 head_room = skb_headroom(skb);
396 num_data_pgs = netvsc_get_slots(skb) + 2; 402 num_data_pgs = netvsc_get_slots(skb) + 2;
397 if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { 403 if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) {
398 netdev_err(net, "Packet too big: %u\n", skb->len); 404 net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
405 num_data_pgs, skb->len);
399 ret = -EFAULT; 406 ret = -EFAULT;
400 goto drop; 407 goto drop;
408 } else if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
409 if (skb_linearize(skb)) {
410 net_alert_ratelimited("failed to linearize skb\n");
411 ret = -ENOMEM;
412 goto drop;
413 }
414 linear = true;
415 goto check_size;
401 } 416 }
402 417
403 pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE; 418 pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;