diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2008-05-26 17:42:42 +1000 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-05-30 22:07:20 -0400 |
commit | 7eb2e25112bf920bb0a4d1cca445f3d96874c25f (patch) | |
tree | cb26b7adfe4c8af39361716966049ed8dd4732d0 | |
parent | d399cf8c04c595d738d82d02ae2755b902a51571 (diff) |
virtio: fix virtio_net xmit of freed skb bug
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.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
-rw-r--r-- | drivers/net/virtio_net.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fe7cdf2a2a2..d50f4fe352b 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -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; } /* Put new one in send queue and do transmit */ - __skb_queue_head(&vi->send, skb); - if (xmit_skb(vi, skb) != 0) { - vi->last_xmit_skb = skb; - goto stop_queue; + if (likely(skb)) { + __skb_queue_head(&vi->send, skb); + if (xmit_skb(vi, skb) != 0) { + vi->last_xmit_skb = skb; + skb = NULL; + goto stop_queue; + } } done: vi->svq->vq_ops->kick(vi->svq); |