diff options
author | Mark McLoughlin <markmc@redhat.com> | 2008-05-27 07:06:26 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-07-24 22:06:00 -0400 |
commit | 9953ca6cb757fb317bb7cdd2fcbf9b88312e241b (patch) | |
tree | b76b369252712111cf29cc1f248b126ce39e6b3a /drivers/net | |
parent | b5684b83b1e1579bbbc80e703e990c0cccf5892c (diff) |
virtio: fix virtio_net xmit of freed skb bug
On Mon, 2008-05-26 at 17:42 +1000, Rusty Russell wrote:
> If we fail to transmit a packet, we assume the queue is full and put
> the skb into last_xmit_skb. However, if more space frees up before we
> xmit it, we loop, and the result can be transmitting the same skb twice.
>
> Fix is simple: set skb to NULL if we've used it in some way, and check
> before sending.
...
> diff -r 564237b31993 drivers/net/virtio_net.c
> --- a/drivers/net/virtio_net.c Mon May 19 12:22:00 2008 +1000
> +++ b/drivers/net/virtio_net.c Mon May 19 12:24:58 2008 +1000
> @@ -287,21 +287,25 @@ again:
> free_old_xmit_skbs(vi);
>
> /* If we has a buffer left over from last time, send it now. */
> - if (vi->last_xmit_skb) {
> + if (unlikely(vi->last_xmit_skb)) {
> if (xmit_skb(vi, vi->last_xmit_skb) != 0) {
> /* Drop this skb: we only queue one. */
> vi->dev->stats.tx_dropped++;
> kfree_skb(skb);
> + skb = NULL;
> goto stop_queue;
> }
> vi->last_xmit_skb = NULL;
With this, may drop an skb and then later in the function discover that
we could have sent it after all. Poor wee skb :)
How about the incremental patch below?
Cheers,
Mark.
Subject: [PATCH] virtio_net: Delay dropping tx skbs
Currently we drop the skb in start_xmit() if we have a
queued buffer and fail to transmit it.
However, if we delay dropping it until we've stopped the
queue and enabled the tx notification callback, then there
is a chance space might become available for it.
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/virtio_net.c | 20 |
1 files changed, 10 insertions, 10 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index c28d7cb2035b..06d5c43bb207 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
@@ -335,16 +335,11 @@ again: | |||
335 | free_old_xmit_skbs(vi); | 335 | free_old_xmit_skbs(vi); |
336 | 336 | ||
337 | /* If we has a buffer left over from last time, send it now. */ | 337 | /* If we has a buffer left over from last time, send it now. */ |
338 | if (unlikely(vi->last_xmit_skb)) { | 338 | if (unlikely(vi->last_xmit_skb) && |
339 | if (xmit_skb(vi, vi->last_xmit_skb) != 0) { | 339 | xmit_skb(vi, vi->last_xmit_skb) != 0) |
340 | /* Drop this skb: we only queue one. */ | 340 | goto stop_queue; |
341 | vi->dev->stats.tx_dropped++; | 341 | |
342 | kfree_skb(skb); | 342 | vi->last_xmit_skb = NULL; |
343 | skb = NULL; | ||
344 | goto stop_queue; | ||
345 | } | ||
346 | vi->last_xmit_skb = NULL; | ||
347 | } | ||
348 | 343 | ||
349 | /* Put new one in send queue and do transmit */ | 344 | /* Put new one in send queue and do transmit */ |
350 | if (likely(skb)) { | 345 | if (likely(skb)) { |
@@ -370,6 +365,11 @@ stop_queue: | |||
370 | netif_start_queue(dev); | 365 | netif_start_queue(dev); |
371 | goto again; | 366 | goto again; |
372 | } | 367 | } |
368 | if (skb) { | ||
369 | /* Drop this skb: we only queue one. */ | ||
370 | vi->dev->stats.tx_dropped++; | ||
371 | kfree_skb(skb); | ||
372 | } | ||
373 | goto done; | 373 | goto done; |
374 | } | 374 | } |
375 | 375 | ||