aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/virtio/virtio.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index c727e92..2058b83 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -997,7 +997,12 @@ void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
/* Called within rcu_read_lock(). */
static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
{
- uint16_t num_heads = vring_avail_idx(vq) - idx;
+ uint16_t avail_idx, num_heads;
+
+ /* Use shadow index whenever possible. */
+ avail_idx = (vq->shadow_avail_idx != idx) ? vq->shadow_avail_idx
+ : vring_avail_idx(vq);
+ num_heads = avail_idx - idx;
/* Check it isn't doing very strange things with descriptor numbers. */
if (num_heads > vq->vring.num) {
@@ -1005,8 +1010,15 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
idx, vq->shadow_avail_idx);
return -EINVAL;
}
- /* On success, callers read a descriptor at vq->last_avail_idx.
- * Make sure descriptor read does not bypass avail index read. */
+ /*
+ * On success, callers read a descriptor at vq->last_avail_idx.
+ * Make sure descriptor read does not bypass avail index read.
+ *
+ * This is necessary even if we are using a shadow index, since
+ * the shadow index could have been initialized by calling
+ * vring_avail_idx() outside of this function, i.e., by a guest
+ * memory read not accompanied by a barrier.
+ */
if (num_heads) {
smp_rmb();
}