summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/virtio/virtio-mem.c54
-rw-r--r--include/hw/virtio/virtio-mem.h3
2 files changed, 56 insertions, 1 deletions
diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
index 1ab9ef17fa..65850530e7 100644
--- a/hw/virtio/virtio-mem.c
+++ b/hw/virtio/virtio-mem.c
@@ -62,8 +62,14 @@ static bool virtio_mem_is_busy(void)
/*
* Postcopy cannot handle concurrent discards and we don't want to migrate
* pages on-demand with stale content when plugging new blocks.
+ *
+ * For precopy, we don't want unplugged blocks in our migration stream, and
+ * when plugging new blocks, the page content might differ between source
+ * and destination (observable by the guest when not initializing pages
+ * after plugging them) until we're running on the destination (as we didn't
+ * migrate these blocks when they were unplugged).
*/
- return migration_in_incoming_postcopy();
+ return migration_in_incoming_postcopy() || !migration_is_idle();
}
static bool virtio_mem_test_bitmap(VirtIOMEM *vmem, uint64_t start_gpa,
@@ -475,6 +481,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
host_memory_backend_set_mapped(vmem->memdev, true);
vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
qemu_register_reset(virtio_mem_system_reset, vmem);
+ precopy_add_notifier(&vmem->precopy_notifier);
}
static void virtio_mem_device_unrealize(DeviceState *dev)
@@ -482,6 +489,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIOMEM *vmem = VIRTIO_MEM(dev);
+ precopy_remove_notifier(&vmem->precopy_notifier);
qemu_unregister_reset(virtio_mem_system_reset, vmem);
vmstate_unregister_ram(&vmem->memdev->mr, DEVICE(vmem));
host_memory_backend_set_mapped(vmem->memdev, false);
@@ -757,12 +765,56 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name,
vmem->block_size = value;
}
+static void virtio_mem_precopy_exclude_unplugged(VirtIOMEM *vmem)
+{
+ void * const host = qemu_ram_get_host_addr(vmem->memdev->mr.ram_block);
+ unsigned long first_zero_bit, last_zero_bit;
+ uint64_t offset, length;
+
+ /*
+ * Find consecutive unplugged blocks and exclude them from migration.
+ *
+ * Note: Blocks cannot get (un)plugged during precopy, no locking needed.
+ */
+ first_zero_bit = find_first_zero_bit(vmem->bitmap, vmem->bitmap_size);
+ while (first_zero_bit < vmem->bitmap_size) {
+ offset = first_zero_bit * vmem->block_size;
+ last_zero_bit = find_next_bit(vmem->bitmap, vmem->bitmap_size,
+ first_zero_bit + 1) - 1;
+ length = (last_zero_bit - first_zero_bit + 1) * vmem->block_size;
+
+ qemu_guest_free_page_hint(host + offset, length);
+ first_zero_bit = find_next_zero_bit(vmem->bitmap, vmem->bitmap_size,
+ last_zero_bit + 2);
+ }
+}
+
+static int virtio_mem_precopy_notify(NotifierWithReturn *n, void *data)
+{
+ VirtIOMEM *vmem = container_of(n, VirtIOMEM, precopy_notifier);
+ PrecopyNotifyData *pnd = data;
+
+ switch (pnd->reason) {
+ case PRECOPY_NOTIFY_SETUP:
+ precopy_enable_free_page_optimization();
+ break;
+ case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC:
+ virtio_mem_precopy_exclude_unplugged(vmem);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void virtio_mem_instance_init(Object *obj)
{
VirtIOMEM *vmem = VIRTIO_MEM(obj);
vmem->block_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
notifier_list_init(&vmem->size_change_notifiers);
+ vmem->precopy_notifier.notify = virtio_mem_precopy_notify;
object_property_add(obj, VIRTIO_MEM_SIZE_PROP, "size", virtio_mem_get_size,
NULL, NULL, NULL);
diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h
index b74c77cd42..0778224964 100644
--- a/include/hw/virtio/virtio-mem.h
+++ b/include/hw/virtio/virtio-mem.h
@@ -67,6 +67,9 @@ typedef struct VirtIOMEM {
/* notifiers to notify when "size" changes */
NotifierList size_change_notifiers;
+
+ /* don't migrate unplugged memory */
+ NotifierWithReturn precopy_notifier;
} VirtIOMEM;
typedef struct VirtIOMEMClass {