[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
- [PATCH v2 05/16] pc: Support for virtio-mem-pci, (continued)
- [PATCH v2 05/16] pc: Support for virtio-mem-pci, David Hildenbrand, 2020/02/12
- [PATCH v2 06/16] exec: Provide owner when resizing memory region, David Hildenbrand, 2020/02/12
- [PATCH v2 07/16] memory: Add memory_region_max_size() and memory_region_is_resizable(), David Hildenbrand, 2020/02/12
- [PATCH v2 08/16] memory: Disallow resizing to 0, David Hildenbrand, 2020/02/12
- [PATCH v2 09/16] memory-device: properly deal with resizable memory regions, David Hildenbrand, 2020/02/12
- [PATCH v2 10/16] hostmem: Factor out applying settings, David Hildenbrand, 2020/02/12
- [PATCH v2 11/16] hostmem: Factor out common checks into host_memory_backend_validate(), David Hildenbrand, 2020/02/12
- [PATCH v2 12/16] hostmem: Introduce "managed-size" for memory-backend-ram, David Hildenbrand, 2020/02/12
- [PATCH v2 13/16] qmp/hmp: Expose "managed-size" for memory backends, David Hildenbrand, 2020/02/12
- [PATCH v2 14/16] virtio-mem: Support for resizable memory regions,
David Hildenbrand <=
- [PATCH v2 15/16] memory: Add region_resize() callback to memory notifier, David Hildenbrand, 2020/02/12
- [PATCH v2 16/16] kvm: Implement region_resize() for atomic memory section resizes, David Hildenbrand, 2020/02/12
- Re: [PATCH v2 00/16] Ram blocks with resizable anonymous allocations under POSIX, David Hildenbrand, 2020/02/12