aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>2018-11-02 13:56:27 +0000
committerWill Deacon <will.deacon@arm.com>2018-11-02 14:14:50 +0000
commit66ba0baea8743e6ac7142367ca58bd76b6f79c7b (patch)
tree237f00700ad3aa2d912c0fe21310fcc39540bd71
parent971ba8ccff299dd41b0e2db655a6b99f3eb2ae0b (diff)
downloadkvmtool-66ba0baea8743e6ac7142367ca58bd76b6f79c7b.tar.gz
virtio: Fix ordering of virt_queue__available()
After adding buffers to the virtio queue, the guest increments the avail index. It then reads the event index to check if it needs to notify the host. If the event index corresponds to the previous avail value, then the guest notifies the host. Otherwise it means that the host is still processing the queue and hasn't had a chance to increment the event index yet. Once it gets there, the host will see the new avail index and process the descriptors, so there is no need for a notification. This is only guaranteed to work if both threads write and read the indices in the right order. Currently a barrier is missing from virt_queue__available(), and the host may not see an up-to-date value of event index after writing avail. HOST | GUEST | | write avail = 1 | mb() | read event -> 0 write event = 0 | == prev_avail -> notify read avail -> 1 | | write event = 1 | read avail -> 1 | wait() | write avail = 2 | mb() | read event -> 0 | != prev_avail -> no notification By adding a memory barrier on the host side, we ensure that it doesn't miss any notification. Reviewed-By: Steven Price <steven.price@arm.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--include/kvm/virtio.h9
1 files changed, 8 insertions, 1 deletions
diff --git a/include/kvm/virtio.h b/include/kvm/virtio.h
index db758b12..72290fc5 100644
--- a/include/kvm/virtio.h
+++ b/include/kvm/virtio.h
@@ -124,8 +124,15 @@ static inline bool virt_queue__available(struct virt_queue *vq)
if (!vq->vring.avail)
return 0;
- if (vq->use_event_idx)
+ if (vq->use_event_idx) {
vring_avail_event(&vq->vring) = last_avail_idx;
+ /*
+ * After the driver writes a new avail index, it reads the event
+ * index to see if we need any notification. Ensure that it
+ * reads the updated index, or else we'll miss the notification.
+ */
+ mb();
+ }
return vq->vring.avail->idx != last_avail_idx;
}