qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 14/16] virtio-mem: Support for resizable memory regions


From: David Hildenbrand
Subject: [PATCH v2 14/16] virtio-mem: Support for resizable memory regions
Date: Wed, 12 Feb 2020 14:35:59 +0100

Signed-off-by: David Hildenbrand <address@hidden>
---
 hw/virtio/virtio-mem.c | 168 ++++++++++++++++++++++++++---------------
 1 file changed, 109 insertions(+), 59 deletions(-)

diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
index 093b6eb0bb..d28b501778 100644
--- a/hw/virtio/virtio-mem.c
+++ b/hw/virtio/virtio-mem.c
@@ -237,30 +237,78 @@ static void virtio_mem_unplug_request(VirtIOMEM *vm, 
VirtQueueElement *elem,
     virtio_mem_send_response_simple(vm, elem, type);
 }
 
+/*
+ * Try to resize the usable region to hold at least the requested size.
+ */
+static void virtio_mem_resize_usable_region(VirtIOMEM *vm,
+                                            uint64_t requested_size,
+                                            Error **errp)
+{
+    /*
+     * If possible, we size the usable region a little bit bigger than the
+     * requested size, so the guest has more flexibility.
+     */
+    uint64_t newsize = MIN(memory_region_max_size(&vm->memdev->mr),
+                           requested_size + VIRTIO_MEM_USABLE_EXTENT);
+    Error *err = NULL;
+
+    /*
+     * Size it as small as possible (0 is not valid).
+     */
+    if (!requested_size) {
+        newsize = vm->block_size;
+    }
+
+    if (newsize == vm->usable_region_size) {
+        return;
+    }
+
+    /* resize the memory region, if supported */
+    if (memory_region_is_resizable(&vm->memdev->mr)) {
+        memory_region_ram_resize(&vm->memdev->mr, newsize, &err);
+    }
+    if (!err) {
+        vm->usable_region_size = newsize;
+        fprintf(stderr, "New usable_region_size: %" PRIx64 "\n",
+                vm->usable_region_size);
+    }
+    error_propagate(errp, err);
+}
+
 /*
  * Unplug all memory and shrink the usable region.
  */
-static void virtio_mem_unplug_all(VirtIOMEM *vm)
+static int virtio_mem_unplug_all(VirtIOMEM *vm)
 {
+    Error *err = NULL;
+
+    if (virtio_mem_busy()) {
+        return -EBUSY;
+    }
+
+    virtio_mem_resize_usable_region(vm, vm->requested_size, &err);
+    if (err) {
+        /* It's unlikely that shrinking fails. */
+        warn_report_err(err);
+        return -ENOMEM;
+    }
     if (vm->size) {
-        virtio_mem_set_block_state(vm, vm->addr,
-                                   memory_region_size(&vm->memdev->mr), false);
+        ram_block_discard_range(vm->memdev->mr.ram_block, 0,
+                                memory_region_size(&vm->memdev->mr));
+        bitmap_clear(vm->bitmap, 0, vm->bitmap_size);
         vm->size = 0;
     }
-    vm->usable_region_size = MIN(memory_region_size(&vm->memdev->mr),
-                                 vm->requested_size + 
VIRTIO_MEM_USABLE_EXTENT);
+    return 0;
 }
 
 static void virtio_mem_unplug_all_request(VirtIOMEM *vm, VirtQueueElement 
*elem)
 {
 
-    if (virtio_mem_busy()) {
+    if (virtio_mem_unplug_all(vm)) {
         virtio_mem_send_response_simple(vm, elem,  VIRTIO_MEM_RESP_BUSY);
-        return;
+    } else {
+        virtio_mem_send_response_simple(vm, elem,  VIRTIO_MEM_RESP_ACK);
     }
-
-    virtio_mem_unplug_all(vm);
-    virtio_mem_send_response_simple(vm, elem,  VIRTIO_MEM_RESP_ACK);
 }
 
 static void virtio_mem_state_request(VirtIOMEM *vm, VirtQueueElement *elem,
@@ -344,7 +392,7 @@ static void virtio_mem_get_config(VirtIODevice *vdev, 
uint8_t *config_data)
     config->requested_size = cpu_to_le64(vm->requested_size);
     config->plugged_size = cpu_to_le64(vm->size);
     config->addr = cpu_to_le64(vm->addr);
-    config->region_size = cpu_to_le64(memory_region_size(&vm->memdev->mr));
+    config->region_size = cpu_to_le64(memory_region_max_size(&vm->memdev->mr));
     config->usable_region_size = cpu_to_le64(vm->usable_region_size);
 }
 
@@ -370,10 +418,6 @@ static void virtio_mem_system_reset(void *opaque)
      * region size. This is, however, not possible in all scenarios. Then,
      * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL).
      */
-    if (virtio_mem_busy()) {
-        return;
-    }
-
     virtio_mem_unplug_all(vm);
 }
 
@@ -410,32 +454,32 @@ static void virtio_mem_device_realize(DeviceState *dev, 
Error **errp)
     int nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
     VirtIOMEM *vm = VIRTIO_MEM(dev);
-    Error *local_err = NULL;
+    Error *err = NULL;
     uint64_t page_size;
 
     /* verify the memdev */
     host_memory_backend_validate(vm->memdev, VIRTIO_MEM_MEMDEV_PROP,
-                                 false, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
+                                 true, &err);
+    if (err) {
+        error_propagate(errp, err);
         return;
     }
 
     /* verify the node */
     if ((nb_numa_nodes && vm->node >= nb_numa_nodes) ||
         (!nb_numa_nodes && vm->node)) {
-        error_setg(&local_err, "Property '%s' has value '%" PRIu32
+        error_setg(errp, "Property '%s' has value '%" PRIu32
                    "', which exceeds the number of numa nodes: %d",
                    VIRTIO_MEM_NODE_PROP, vm->node,
                    nb_numa_nodes ? nb_numa_nodes : 1);
-        goto out;
+        return;
     }
 
     /* mmap/madvise changes have to be reflected in guest physical memory */
     if (kvm_enabled() && !kvm_has_sync_mmu()) {
-        error_set(&local_err, ERROR_CLASS_KVM_MISSING_CAP,
+        error_set(errp, ERROR_CLASS_KVM_MISSING_CAP,
                   "Using KVM without synchronous MMU, virtio-mem unavailable");
-        goto out;
+        return;
     }
 
     /*
@@ -443,8 +487,14 @@ static void virtio_mem_device_realize(DeviceState *dev, 
Error **errp)
      * to temporarily unlock and relock at the right places to make it work.
      */
     if (enable_mlock) {
-        error_setg(&local_err, "Memory is locked, virtio-mem unavailable");
-        goto out;
+        error_setg(errp, "Memory is locked, virtio-mem unavailable");
+        return;
+    }
+
+    if (virtio_mem_busy()) {
+        error_setg(errp, "virtio-mem devices cannot be created while 
migrating,"
+                   " while dumping, or when certain vfio devices are used.");
+        return;
     }
 
     g_assert(memory_region_is_ram(&vm->memdev->mr));
@@ -458,37 +508,37 @@ static void virtio_mem_device_realize(DeviceState *dev, 
Error **errp)
      */
     page_size = qemu_ram_pagesize(vm->memdev->mr.ram_block);
     if (page_size != getpagesize()) {
-        error_setg(&local_err, "'%s' page size (0x%" PRIx64 ") not supported",
+        error_setg(errp, "'%s' page size (0x%" PRIx64 ") not supported",
                    VIRTIO_MEM_MEMDEV_PROP, page_size);
-        goto out;
+        return;
     }
 
     /* now that memdev and block_size is fixed, verify the properties */
     if (vm->block_size < page_size) {
-        error_setg(&local_err, "'%s' has to be at least the page size (0x%"
+        error_setg(errp, "'%s' has to be at least the page size (0x%"
                    PRIx64 ")", VIRTIO_MEM_BLOCK_SIZE_PROP, page_size);
-        goto out;
+        return;
     } else if (!QEMU_IS_ALIGNED(vm->requested_size, vm->block_size)) {
         error_setg(errp, "'%s' has to be multiples of '%s' (0x%" PRIx32
                    ")", VIRTIO_MEM_REQUESTED_SIZE_PROP,
                    VIRTIO_MEM_BLOCK_SIZE_PROP, vm->block_size);
-    } else if (!QEMU_IS_ALIGNED(memory_region_size(&vm->memdev->mr),
+        return;
+    } else if (!QEMU_IS_ALIGNED(memory_region_max_size(&vm->memdev->mr),
                                 vm->block_size)) {
-        error_setg(&local_err, "'%s' size has to be multiples of '%s' (0x%"
+        error_setg(errp, "'%s' size has to be multiples of '%s' (0x%"
                    PRIx32 ")", VIRTIO_MEM_MEMDEV_PROP,
                    VIRTIO_MEM_BLOCK_SIZE_PROP, vm->block_size);
-        goto out;
+        return;
     }
 
-    /*
-     * If possible, we size the usable region a little bit bigger than the
-     * requested size, so the guest has more flexibility.
-     */
-    vm->usable_region_size = MIN(memory_region_size(&vm->memdev->mr),
-                                 vm->requested_size + 
VIRTIO_MEM_USABLE_EXTENT);
+    virtio_mem_resize_usable_region(vm, vm->requested_size, &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
 
     /* allocate the bitmap for tracking the state of a block */
-    vm->bitmap_size = memory_region_size(&vm->memdev->mr) / vm->block_size;
+    vm->bitmap_size = memory_region_max_size(&vm->memdev->mr) / vm->block_size;
     vm->bitmap = bitmap_new(vm->bitmap_size);
 
     /* all memory is unplugged initially */
@@ -505,8 +555,6 @@ static void virtio_mem_device_realize(DeviceState *dev, 
Error **errp)
     vm->postcopy_notifier.notify = virtio_mem_postcopy_notifier;
     postcopy_add_notifier(&vm->postcopy_notifier);
     qemu_register_reset(virtio_mem_system_reset, vm);
-out:
-    error_propagate(errp, local_err);
 }
 
 static void virtio_mem_device_unrealize(DeviceState *dev, Error **errp)
@@ -603,7 +651,7 @@ static void virtio_mem_fill_device_info(const VirtIOMEM 
*vmem,
     vi->node = vmem->node;
     vi->requested_size = vmem->requested_size;
     vi->size = vmem->size;
-    vi->max_size = memory_region_size(&vmem->memdev->mr);
+    vi->max_size = memory_region_max_size(&vmem->memdev->mr);
     vi->block_size = vmem->block_size;
     vi->memdev = object_get_canonical_path(OBJECT(vmem->memdev));
 }
@@ -651,14 +699,6 @@ static void virtio_mem_set_requested_size(Object *obj, 
Visitor *v,
         return;
     }
 
-    /* Growing the usable region might later not be possible, disallow it. */
-    if (virtio_mem_busy() && value > vm->requested_size) {
-        error_setg(errp, "'%s' cannot be increased while migrating,"
-                   " while dumping, or when certain vfio devices are used.",
-                   name);
-        return;
-    }
-
     /*
      * The block size and memory backend are not fixed until the device was
      * realized. realize() will verify these properties then.
@@ -669,22 +709,32 @@ static void virtio_mem_set_requested_size(Object *obj, 
Visitor *v,
                        ")", name, VIRTIO_MEM_BLOCK_SIZE_PROP,
                        vm->block_size);
             return;
-        } else if (value > memory_region_size(&vm->memdev->mr)) {
+        } else if (value > memory_region_max_size(&vm->memdev->mr)) {
             error_setg(errp, "'%s' cannot exceed the memory backend size"
                        "(0x%" PRIx64 ")", name,
-                       memory_region_size(&vm->memdev->mr));
+                       memory_region_max_size(&vm->memdev->mr));
             return;
         }
 
         if (value != vm->requested_size) {
-            uint64_t tmp_size;
-
+            if (virtio_mem_busy()) {
+                error_setg(errp, "'%s' cannot be changed while migrating,"
+                           " while dumping, or when certain vfio devices are 
used.",
+                           name);
+                return;
+            }
+
+            /* We are only allowed to grow the region */
+            if (value > vm->requested_size) {
+                Error *err = NULL;
+
+                virtio_mem_resize_usable_region(vm, value, &err);
+                if (err) {
+                    error_propagate(errp, err);
+                    return;
+                }
+            }
             vm->requested_size = value;
-
-            /* Grow the usable region if required */
-            tmp_size = MIN(memory_region_size(&vm->memdev->mr),
-                           vm->requested_size + VIRTIO_MEM_USABLE_EXTENT);
-            vm->usable_region_size = MAX(vm->usable_region_size, tmp_size);
         }
         /*
          * Trigger a config update so the guest gets notified. We trigger
-- 
2.24.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]