diff options
| author | David S. Miller <davem@davemloft.net> | 2013-07-09 15:45:43 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-07-09 15:45:43 -0400 |
| commit | e1d6fbc3dedbb463fc79b48ddb05ab6b20fd088a (patch) | |
| tree | 6d63721222da5d652d2e4e65991a15007a83a520 | |
| parent | 01276ed2424eb78c95461545410923d5da154d31 (diff) | |
| parent | cbdadbbf0c790f79350a8f36029208944c5487d0 (diff) | |
virtio_net: fix race in RX VQ processing
Michael S. Tsirkin says:
====================
Jason Wang reported a race in RX VQ processing:
virtqueue_enable_cb is called outside napi lock,
violating virtio serialization rules.
The race has been there from day 1, but it got especially nasty in 3.0
when commit a5c262c5fd83ece01bd649fb08416c501d4c59d7
"virtio_ring: support event idx feature"
added more dependency on vq state.
Please review, and consider for 3.11 and stable.
Changes from v1:
- Added Jason's Tested-by tag
- minor coding style fix
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/virtio_net.c | 5 | ||||
| -rw-r--r-- | drivers/virtio/virtio_ring.c | 56 | ||||
| -rw-r--r-- | include/linux/virtio.h | 4 |
3 files changed, 51 insertions, 14 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index c9e00387d999..42d670a468f8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
| @@ -602,7 +602,7 @@ static int virtnet_poll(struct napi_struct *napi, int budget) | |||
| 602 | container_of(napi, struct receive_queue, napi); | 602 | container_of(napi, struct receive_queue, napi); |
| 603 | struct virtnet_info *vi = rq->vq->vdev->priv; | 603 | struct virtnet_info *vi = rq->vq->vdev->priv; |
| 604 | void *buf; | 604 | void *buf; |
| 605 | unsigned int len, received = 0; | 605 | unsigned int r, len, received = 0; |
| 606 | 606 | ||
| 607 | again: | 607 | again: |
| 608 | while (received < budget && | 608 | while (received < budget && |
| @@ -619,8 +619,9 @@ again: | |||
| 619 | 619 | ||
| 620 | /* Out of packets? */ | 620 | /* Out of packets? */ |
| 621 | if (received < budget) { | 621 | if (received < budget) { |
| 622 | r = virtqueue_enable_cb_prepare(rq->vq); | ||
| 622 | napi_complete(napi); | 623 | napi_complete(napi); |
| 623 | if (unlikely(!virtqueue_enable_cb(rq->vq)) && | 624 | if (unlikely(virtqueue_poll(rq->vq, r)) && |
| 624 | napi_schedule_prep(napi)) { | 625 | napi_schedule_prep(napi)) { |
| 625 | virtqueue_disable_cb(rq->vq); | 626 | virtqueue_disable_cb(rq->vq); |
| 626 | __napi_schedule(napi); | 627 | __napi_schedule(napi); |
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5217baf5528c..37d58f84dc50 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c | |||
| @@ -607,19 +607,21 @@ void virtqueue_disable_cb(struct virtqueue *_vq) | |||
| 607 | EXPORT_SYMBOL_GPL(virtqueue_disable_cb); | 607 | EXPORT_SYMBOL_GPL(virtqueue_disable_cb); |
| 608 | 608 | ||
| 609 | /** | 609 | /** |
| 610 | * virtqueue_enable_cb - restart callbacks after disable_cb. | 610 | * virtqueue_enable_cb_prepare - restart callbacks after disable_cb |
| 611 | * @vq: the struct virtqueue we're talking about. | 611 | * @vq: the struct virtqueue we're talking about. |
| 612 | * | 612 | * |
| 613 | * This re-enables callbacks; it returns "false" if there are pending | 613 | * This re-enables callbacks; it returns current queue state |
| 614 | * buffers in the queue, to detect a possible race between the driver | 614 | * in an opaque unsigned value. This value should be later tested by |
| 615 | * checking for more work, and enabling callbacks. | 615 | * virtqueue_poll, to detect a possible race between the driver checking for |
| 616 | * more work, and enabling callbacks. | ||
| 616 | * | 617 | * |
| 617 | * Caller must ensure we don't call this with other virtqueue | 618 | * Caller must ensure we don't call this with other virtqueue |
| 618 | * operations at the same time (except where noted). | 619 | * operations at the same time (except where noted). |
| 619 | */ | 620 | */ |
| 620 | bool virtqueue_enable_cb(struct virtqueue *_vq) | 621 | unsigned virtqueue_enable_cb_prepare(struct virtqueue *_vq) |
| 621 | { | 622 | { |
| 622 | struct vring_virtqueue *vq = to_vvq(_vq); | 623 | struct vring_virtqueue *vq = to_vvq(_vq); |
| 624 | u16 last_used_idx; | ||
| 623 | 625 | ||
| 624 | START_USE(vq); | 626 | START_USE(vq); |
| 625 | 627 | ||
| @@ -629,15 +631,45 @@ bool virtqueue_enable_cb(struct virtqueue *_vq) | |||
| 629 | * either clear the flags bit or point the event index at the next | 631 | * either clear the flags bit or point the event index at the next |
| 630 | * entry. Always do both to keep code simple. */ | 632 | * entry. Always do both to keep code simple. */ |
| 631 | vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; | 633 | vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; |
| 632 | vring_used_event(&vq->vring) = vq->last_used_idx; | 634 | vring_used_event(&vq->vring) = last_used_idx = vq->last_used_idx; |
| 635 | END_USE(vq); | ||
| 636 | return last_used_idx; | ||
| 637 | } | ||
| 638 | EXPORT_SYMBOL_GPL(virtqueue_enable_cb_prepare); | ||
| 639 | |||
| 640 | /** | ||
| 641 | * virtqueue_poll - query pending used buffers | ||
| 642 | * @vq: the struct virtqueue we're talking about. | ||
| 643 | * @last_used_idx: virtqueue state (from call to virtqueue_enable_cb_prepare). | ||
| 644 | * | ||
| 645 | * Returns "true" if there are pending used buffers in the queue. | ||
| 646 | * | ||
| 647 | * This does not need to be serialized. | ||
| 648 | */ | ||
| 649 | bool virtqueue_poll(struct virtqueue *_vq, unsigned last_used_idx) | ||
| 650 | { | ||
| 651 | struct vring_virtqueue *vq = to_vvq(_vq); | ||
| 652 | |||
| 633 | virtio_mb(vq->weak_barriers); | 653 | virtio_mb(vq->weak_barriers); |
| 634 | if (unlikely(more_used(vq))) { | 654 | return (u16)last_used_idx != vq->vring.used->idx; |
| 635 | END_USE(vq); | 655 | } |
| 636 | return false; | 656 | EXPORT_SYMBOL_GPL(virtqueue_poll); |
| 637 | } | ||
| 638 | 657 | ||
| 639 | END_USE(vq); | 658 | /** |
| 640 | return true; | 659 | * virtqueue_enable_cb - restart callbacks after disable_cb. |
| 660 | * @vq: the struct virtqueue we're talking about. | ||
| 661 | * | ||
| 662 | * This re-enables callbacks; it returns "false" if there are pending | ||
| 663 | * buffers in the queue, to detect a possible race between the driver | ||
| 664 | * checking for more work, and enabling callbacks. | ||
| 665 | * | ||
| 666 | * Caller must ensure we don't call this with other virtqueue | ||
| 667 | * operations at the same time (except where noted). | ||
| 668 | */ | ||
| 669 | bool virtqueue_enable_cb(struct virtqueue *_vq) | ||
| 670 | { | ||
| 671 | unsigned last_used_idx = virtqueue_enable_cb_prepare(_vq); | ||
| 672 | return !virtqueue_poll(_vq, last_used_idx); | ||
| 641 | } | 673 | } |
| 642 | EXPORT_SYMBOL_GPL(virtqueue_enable_cb); | 674 | EXPORT_SYMBOL_GPL(virtqueue_enable_cb); |
| 643 | 675 | ||
diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 9ff8645b7e0b..72398eea6e86 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h | |||
| @@ -70,6 +70,10 @@ void virtqueue_disable_cb(struct virtqueue *vq); | |||
| 70 | 70 | ||
| 71 | bool virtqueue_enable_cb(struct virtqueue *vq); | 71 | bool virtqueue_enable_cb(struct virtqueue *vq); |
| 72 | 72 | ||
| 73 | unsigned virtqueue_enable_cb_prepare(struct virtqueue *vq); | ||
| 74 | |||
| 75 | bool virtqueue_poll(struct virtqueue *vq, unsigned); | ||
| 76 | |||
| 73 | bool virtqueue_enable_cb_delayed(struct virtqueue *vq); | 77 | bool virtqueue_enable_cb_delayed(struct virtqueue *vq); |
| 74 | 78 | ||
| 75 | void *virtqueue_detach_unused_buf(struct virtqueue *vq); | 79 | void *virtqueue_detach_unused_buf(struct virtqueue *vq); |
