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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).