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 | |
| 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 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); |
