diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2008-05-26 03:42:42 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-05-30 22:07:20 -0400 |
commit | 7eb2e25112bf920bb0a4d1cca445f3d96874c25f (patch) | |
tree | cb26b7adfe4c8af39361716966049ed8dd4732d0 /drivers | |
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>
Diffstat (limited to 'drivers')
-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 fe7cdf2a2a23..d50f4fe352b3 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
@@ -287,21 +287,25 @@ again: | |||
287 | free_old_xmit_skbs(vi); | 287 | free_old_xmit_skbs(vi); |
288 | 288 | ||
289 | /* If we has a buffer left over from last time, send it now. */ | 289 | /* If we has a buffer left over from last time, send it now. */ |
290 | if (vi->last_xmit_skb) { | 290 | if (unlikely(vi->last_xmit_skb)) { |
291 | if (xmit_skb(vi, vi->last_xmit_skb) != 0) { | 291 | if (xmit_skb(vi, vi->last_xmit_skb) != 0) { |
292 | /* Drop this skb: we only queue one. */ | 292 | /* Drop this skb: we only queue one. */ |
293 | vi->dev->stats.tx_dropped++; | 293 | vi->dev->stats.tx_dropped++; |
294 | kfree_skb(skb); | 294 | kfree_skb(skb); |
295 | skb = NULL; | ||
295 | goto stop_queue; | 296 | goto stop_queue; |
296 | } | 297 | } |
297 | vi->last_xmit_skb = NULL; | 298 | vi->last_xmit_skb = NULL; |
298 | } | 299 | } |
299 | 300 | ||
300 | /* Put new one in send queue and do transmit */ | 301 | /* Put new one in send queue and do transmit */ |
301 | __skb_queue_head(&vi->send, skb); | 302 | if (likely(skb)) { |
302 | if (xmit_skb(vi, skb) != 0) { | 303 | __skb_queue_head(&vi->send, skb); |
303 | vi->last_xmit_skb = skb; | 304 | if (xmit_skb(vi, skb) != 0) { |
304 | goto stop_queue; | 305 | vi->last_xmit_skb = skb; |
306 | skb = NULL; | ||
307 | goto stop_queue; | ||
308 | } | ||
305 | } | 309 | } |
306 | done: | 310 | done: |
307 | vi->svq->vq_ops->kick(vi->svq); | 311 | vi->svq->vq_ops->kick(vi->svq); |