vhost: Always initialize cached vring data
vhost_virtqueue_start() can exit early if the descriptor ring address is 0, assuming the virtqueue isn’t ready to start. In this case, all cached vring information (size, physical address, pointer) is left as-is. This is OK at first startup, when that info is still initialized to 0, but after a reset, it will retain old (outdated) information. vhost_virtqueue_start() must make sure these values are (re-)set properly before exiting. (When using an IOMMU, these outdated values can stall the device: vhost_dev_start() deliberately produces an IOMMU miss event for each used vring. If used_phys contains an outdated value, the resulting lookup may fail, forcing the device to be stopped.) Cc: qemu-stable@nongnu.org Signed-off-by: Hanna Czenczek <hreitz@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20251208113008.153249-1-hreitz@redhat.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
This commit is contained in:
parent
8062bfd517
commit
46228925ed
1 changed files with 23 additions and 15 deletions
|
|
@ -1261,7 +1261,7 @@ int vhost_virtqueue_start(struct vhost_dev *dev,
|
||||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
|
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
|
||||||
VirtioBusState *vbus = VIRTIO_BUS(qbus);
|
VirtioBusState *vbus = VIRTIO_BUS(qbus);
|
||||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
|
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
|
||||||
hwaddr s, l, a;
|
hwaddr l;
|
||||||
int r;
|
int r;
|
||||||
int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
|
int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
|
||||||
struct vhost_vring_file file = {
|
struct vhost_vring_file file = {
|
||||||
|
|
@ -1272,8 +1272,17 @@ int vhost_virtqueue_start(struct vhost_dev *dev,
|
||||||
};
|
};
|
||||||
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
|
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
|
||||||
|
|
||||||
a = virtio_queue_get_desc_addr(vdev, idx);
|
vq->desc_size = virtio_queue_get_desc_size(vdev, idx);
|
||||||
if (a == 0) {
|
vq->desc_phys = virtio_queue_get_desc_addr(vdev, idx);
|
||||||
|
vq->desc = NULL;
|
||||||
|
vq->avail_size = virtio_queue_get_avail_size(vdev, idx);
|
||||||
|
vq->avail_phys = virtio_queue_get_avail_addr(vdev, idx);
|
||||||
|
vq->avail = NULL;
|
||||||
|
vq->used_size = virtio_queue_get_used_size(vdev, idx);
|
||||||
|
vq->used_phys = virtio_queue_get_used_addr(vdev, idx);
|
||||||
|
vq->used = NULL;
|
||||||
|
|
||||||
|
if (vq->desc_phys == 0) {
|
||||||
/* Queue might not be ready for start */
|
/* Queue might not be ready for start */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1301,24 +1310,23 @@ int vhost_virtqueue_start(struct vhost_dev *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vq->desc_size = s = l = virtio_queue_get_desc_size(vdev, idx);
|
l = vq->desc_size;
|
||||||
vq->desc_phys = a;
|
vq->desc = vhost_memory_map(dev, vq->desc_phys, &l, false);
|
||||||
vq->desc = vhost_memory_map(dev, a, &l, false);
|
if (!vq->desc || l != vq->desc_size) {
|
||||||
if (!vq->desc || l != s) {
|
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto fail_alloc_desc;
|
goto fail_alloc_desc;
|
||||||
}
|
}
|
||||||
vq->avail_size = s = l = virtio_queue_get_avail_size(vdev, idx);
|
|
||||||
vq->avail_phys = a = virtio_queue_get_avail_addr(vdev, idx);
|
l = vq->avail_size;
|
||||||
vq->avail = vhost_memory_map(dev, a, &l, false);
|
vq->avail = vhost_memory_map(dev, vq->avail_phys, &l, false);
|
||||||
if (!vq->avail || l != s) {
|
if (!vq->avail || l != vq->avail_size) {
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto fail_alloc_avail;
|
goto fail_alloc_avail;
|
||||||
}
|
}
|
||||||
vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx);
|
|
||||||
vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx);
|
l = vq->used_size;
|
||||||
vq->used = vhost_memory_map(dev, a, &l, true);
|
vq->used = vhost_memory_map(dev, vq->used_phys, &l, true);
|
||||||
if (!vq->used || l != s) {
|
if (!vq->used || l != vq->used_size) {
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto fail_alloc_used;
|
goto fail_alloc_used;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue