diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2013-12-26 08:32:47 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-15 18:28:49 -0500 |
commit | e32904ec6251df8e552074c9eb068606955d894c (patch) | |
tree | 2d04d33ec50716e3a1ffd9429071cfeecb7474c4 | |
parent | c726095ec74daabd48a9a4ed48d46304601017b4 (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.c | 66 |
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 | ||
297 | static int receive_mergeable(struct receive_queue *rq, struct sk_buff *skb) | 297 | static 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; |
334 | err_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 | } | ||
348 | err_buf: | ||
349 | dev->stats.rx_dropped++; | ||
350 | dev_kfree_skb(skb); | ||
351 | return NULL; | ||
327 | } | 352 | } |
328 | 353 | ||
329 | static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) | 354 | static 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); |