All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Hildenbrand <david@redhat.com>
To: qemu-devel@nongnu.org
Cc: Eduardo Habkost <ehabkost@redhat.com>,
	"Michael S . Tsirkin" <mst@redhat.com>,
	David Hildenbrand <david@redhat.com>,
	"Dr . David Alan Gilbert" <dgilbert@redhat.com>,
	Igor Mammedov <imammedo@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Richard Henderson <rth@twiddle.net>
Subject: [PATCH v2 14/16] virtio-mem: Support for resizable memory regions
Date: Wed, 12 Feb 2020 14:35:59 +0100	[thread overview]
Message-ID: <20200212133601.10555-15-david@redhat.com> (raw)
In-Reply-To: <20200212133601.10555-1-david@redhat.com>

Signed-off-by: David Hildenbrand <david@redhat.com>
---
 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



  parent reply	other threads:[~2020-02-12 13:47 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-12 13:35 [PATCH v2 00/16] Ram blocks with resizable anonymous allocations under POSIX David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 01/16] virtio-mem: Prototype David Hildenbrand
2020-02-12 14:15   ` Eric Blake
2020-02-12 14:20     ` David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 02/16] virtio-pci: Proxy for virtio-mem David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 03/16] hmp: Handle virtio-mem when printing memory device infos David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 04/16] numa: Handle virtio-mem in NUMA stats David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 05/16] pc: Support for virtio-mem-pci David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 06/16] exec: Provide owner when resizing memory region David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 07/16] memory: Add memory_region_max_size() and memory_region_is_resizable() David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 08/16] memory: Disallow resizing to 0 David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 09/16] memory-device: properly deal with resizable memory regions David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 10/16] hostmem: Factor out applying settings David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 11/16] hostmem: Factor out common checks into host_memory_backend_validate() David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 12/16] hostmem: Introduce "managed-size" for memory-backend-ram David Hildenbrand
2020-02-12 13:35 ` [PATCH v2 13/16] qmp/hmp: Expose "managed-size" for memory backends David Hildenbrand
2020-02-12 14:17   ` Eric Blake
2020-02-12 13:35 ` David Hildenbrand [this message]
2020-02-12 13:36 ` [PATCH v2 15/16] memory: Add region_resize() callback to memory notifier David Hildenbrand
2020-02-12 13:36 ` [PATCH v2 16/16] kvm: Implement region_resize() for atomic memory section resizes David Hildenbrand
2020-02-12 13:40 ` [PATCH v2 00/16] Ram blocks with resizable anonymous allocations under POSIX David Hildenbrand

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200212133601.10555-15-david@redhat.com \
    --to=david@redhat.com \
    --cc=dgilbert@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=imammedo@redhat.com \
    --cc=mst@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rth@twiddle.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.