@@ -432,6 +432,10 @@ static void vhost_set_memory(MemoryListener *listener,
assert(size);
+ if (!dev->rsvd_hva.mr) {
+ dev->rsvd_hva = memory_region_find_hva_range(section->mr);
+ }
+
/* Optimize no-change case. At least cirrus_vga does this a lot at this time. */
ram = memory_region_get_ram_ptr(section->mr) + section->offset_within_region;
if (add) {
@@ -472,6 +476,42 @@ static void vhost_begin(MemoryListener *listener)
dev->mem_changed_start_addr = -1;
}
+static int vhost_set_mem_table(struct vhost_dev *dev)
+{
+ hwaddr start_addr = 0;
+ ram_addr_t size = 0;
+ struct vhost_memory *mem;
+ int r, i;
+
+ /* drop memory ranges from continuos HVA */
+ mem = g_memdup(dev->mem, offsetof(struct vhost_memory, regions) +
+ dev->mem->nregions * sizeof dev->mem->regions[0]);
+ start_addr = dev->rsvd_hva.offset_within_address_space;
+ size = int128_get64(dev->rsvd_hva.size);
+ for (i = 0; i < mem->nregions; i++) {
+ if (mem->regions[i].guest_phys_addr >= start_addr &&
+ mem->regions[i].guest_phys_addr < start_addr + size) {
+ mem->nregions--;
+ memmove(&mem->regions[i], &mem->regions[i + 1],
+ (mem->nregions - i) * sizeof mem->regions[0]);
+ }
+ }
+ /* add one continuos HVA entry if memory ranges from it is present */
+ if (dev->mem->nregions > mem->nregions) {
+ struct vhost_memory_region *reg = &mem->regions[mem->nregions];
+
+ reg->guest_phys_addr = start_addr;
+ reg->memory_size = size;
+ reg->userspace_addr =
+ (__u64)memory_region_get_ram_ptr(dev->rsvd_hva.mr);
+ mem->nregions++;
+ }
+
+ r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, mem);
+ g_free(mem);
+ return r;
+}
+
static void vhost_commit(MemoryListener *listener)
{
struct vhost_dev *dev = container_of(listener, struct vhost_dev,
@@ -500,7 +540,7 @@ static void vhost_commit(MemoryListener *listener)
}
if (!dev->log_enabled) {
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = vhost_set_mem_table(dev);
assert(r >= 0);
dev->memory_changed = false;
return;
@@ -513,7 +553,7 @@ static void vhost_commit(MemoryListener *listener)
if (dev->log_size < log_size) {
vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER);
}
- r = dev->vhost_ops->vhost_call(dev, VHOST_SET_MEM_TABLE, dev->mem);
+ r = vhost_set_mem_table(dev);
assert(r >= 0);
/* To log less, can only decrease log size after table update. */
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
@@ -956,6 +996,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
migrate_add_blocker(hdev->migration_blocker);
}
hdev->mem = g_malloc0(offsetof(struct vhost_memory, regions));
+ memset(&hdev->rsvd_hva, 0, sizeof hdev->rsvd_hva);
hdev->n_mem_sections = 0;
hdev->mem_sections = NULL;
hdev->log = NULL;
@@ -1119,7 +1160,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
if (r < 0) {
goto fail_features;
}
- r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem);
+ r = vhost_set_mem_table(hdev);
if (r < 0) {
r = -errno;
goto fail_mem;
@@ -40,6 +40,7 @@ struct vhost_dev {
struct vhost_memory *mem;
int n_mem_sections;
MemoryRegionSection *mem_sections;
+ MemoryRegionSection rsvd_hva;
struct vhost_virtqueue *vqs;
int nvqs;
/* the first virtqueue which would be used by this vhost dev */
QEMU asserts in vhost due to hitting vhost backend limit on number of supported memory regions. Describe all hotplugged memory as one continuos range to vhost with linear 1:1 HVA->GPA mapping in backend. Signed-off-by: Igor Mammedov <imammedo@redhat.com> --- hw/virtio/vhost.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- include/hw/virtio/vhost.h | 1 + 2 files changed, 45 insertions(+), 3 deletions(-)