aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2013-12-26 08:32:47 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-15 18:28:49 -0500
commite32904ec6251df8e552074c9eb068606955d894c (patch)
tree2d04d33ec50716e3a1ffd9429071cfeecb7474c4
parentc726095ec74daabd48a9a4ed48d46304601017b4 (diff)
virtio_net: fix error handling for mergeable buffers
Eric Dumazet noticed that if we encounter an error when processing a mergeable buffer, we don't dequeue all of the buffers from this packet, the result is almost sure to be loss of networking. Fix this issue. Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Michael Dalton <mwdalton@google.com> Acked-by: Michael Dalton <mwdalton@google.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Jason Wang <jasowang@redhat.com> Cc: David S. Miller <davem@davemloft.net> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> (cherry picked from commit 8fc3b9e9a229778e5af3aa453c44f1a3857ba769) Acked-by: Jason Wang <jasowang@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/net/virtio_net.c66
1 files changed, 46 insertions, 20 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 5055eb7527c6..ad9b385e1f3d 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -294,26 +294,33 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq,
294 return skb; 294 return skb;
295} 295}
296 296
297static int receive_mergeable(struct receive_queue *rq, struct sk_buff *skb) 297static struct sk_buff *receive_mergeable(struct net_device *dev,
298 struct receive_queue *rq,
299 void *buf,
300 unsigned int len)
298{ 301{
299 struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); 302 struct skb_vnet_hdr *hdr = page_address(buf);
300 struct page *page; 303 int num_buf = hdr->mhdr.num_buffers;
301 int num_buf, i, len; 304 struct page *page = buf;
305 struct sk_buff *skb = page_to_skb(rq, page, len);
306 int i;
307
308 if (unlikely(!skb))
309 goto err_skb;
302 310
303 num_buf = hdr->mhdr.num_buffers;
304 while (--num_buf) { 311 while (--num_buf) {
305 i = skb_shinfo(skb)->nr_frags; 312 i = skb_shinfo(skb)->nr_frags;
306 if (i >= MAX_SKB_FRAGS) { 313 if (i >= MAX_SKB_FRAGS) {
307 pr_debug("%s: packet too long\n", skb->dev->name); 314 pr_debug("%s: packet too long\n", skb->dev->name);
308 skb->dev->stats.rx_length_errors++; 315 skb->dev->stats.rx_length_errors++;
309 return -EINVAL; 316 return NULL;
310 } 317 }
311 page = virtqueue_get_buf(rq->vq, &len); 318 page = virtqueue_get_buf(rq->vq, &len);
312 if (!page) { 319 if (!page) {
313 pr_debug("%s: rx error: %d buffers missing\n", 320 pr_debug("%s: rx error: %d buffers %d missing\n",
314 skb->dev->name, hdr->mhdr.num_buffers); 321 dev->name, hdr->mhdr.num_buffers, num_buf);
315 skb->dev->stats.rx_length_errors++; 322 dev->stats.rx_length_errors++;
316 return -EINVAL; 323 goto err_buf;
317 } 324 }
318 325
319 if (len > PAGE_SIZE) 326 if (len > PAGE_SIZE)
@@ -323,7 +330,25 @@ static int receive_mergeable(struct receive_queue *rq, struct sk_buff *skb)
323 330
324 --rq->num; 331 --rq->num;
325 } 332 }
326 return 0; 333 return skb;
334err_skb:
335 give_pages(rq, page);
336 while (--num_buf) {
337 buf = virtqueue_get_buf(rq->vq, &len);
338 if (unlikely(!buf)) {
339 pr_debug("%s: rx error: %d buffers missing\n",
340 dev->name, num_buf);
341 dev->stats.rx_length_errors++;
342 break;
343 }
344 page = buf;
345 give_pages(rq, page);
346 --rq->num;
347 }
348err_buf:
349 dev->stats.rx_dropped++;
350 dev_kfree_skb(skb);
351 return NULL;
327} 352}
328 353
329static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) 354static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
@@ -351,17 +376,18 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
351 skb_trim(skb, len); 376 skb_trim(skb, len);
352 } else { 377 } else {
353 page = buf; 378 page = buf;
354 skb = page_to_skb(rq, page, len); 379 if (vi->mergeable_rx_bufs) {
355 if (unlikely(!skb)) { 380 skb = receive_mergeable(dev, rq, page, len);
356 dev->stats.rx_dropped++; 381 if (unlikely(!skb))
357 give_pages(rq, page); 382 return;
358 return; 383 } else {
359 } 384 skb = page_to_skb(rq, page, len);
360 if (vi->mergeable_rx_bufs) 385 if (unlikely(!skb)) {
361 if (receive_mergeable(rq, skb)) { 386 dev->stats.rx_dropped++;
362 dev_kfree_skb(skb); 387 give_pages(rq, page);
363 return; 388 return;
364 } 389 }
390 }
365 } 391 }
366 392
367 hdr = skb_vnet_hdr(skb); 393 hdr = skb_vnet_hdr(skb);