[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v18 QEMU 09/18] vfio: Add save state functions to SaveVMHandlers
From: |
Kirti Wankhede |
Subject: |
[PATCH v18 QEMU 09/18] vfio: Add save state functions to SaveVMHandlers |
Date: |
Tue, 5 May 2020 04:14:44 +0530 |
Added .save_live_pending, .save_live_iterate and .save_live_complete_precopy
functions. These functions handles pre-copy and stop-and-copy phase.
In _SAVING|_RUNNING device state or pre-copy phase:
- read pending_bytes. If pending_bytes > 0, go through below steps.
- read data_offset - indicates kernel driver to write data to staging
buffer.
- read data_size - amount of data in bytes written by vendor driver in
migration region.
- read data_size bytes of data from data_offset in the migration region.
- Write data packet to file stream as below:
{VFIO_MIG_FLAG_DEV_DATA_STATE, data_size, actual data,
VFIO_MIG_FLAG_END_OF_STATE }
In _SAVING device state or stop-and-copy phase
a. read config space of device and save to migration file stream. This
doesn't need to be from vendor driver. Any other special config state
from driver can be saved as data in following iteration.
b. read pending_bytes. If pending_bytes > 0, go through below steps.
c. read data_offset - indicates kernel driver to write data to staging
buffer.
d. read data_size - amount of data in bytes written by vendor driver in
migration region.
e. read data_size bytes of data from data_offset in the migration region.
f. Write data packet as below:
{VFIO_MIG_FLAG_DEV_DATA_STATE, data_size, actual data}
g. iterate through steps b to f while (pending_bytes > 0)
h. Write {VFIO_MIG_FLAG_END_OF_STATE}
When data region is mapped, its user's responsibility to read data from
data_offset of data_size before moving to next steps.
Signed-off-by: Kirti Wankhede <address@hidden>
Reviewed-by: Neo Jia <address@hidden>
---
hw/vfio/migration.c | 245 +++++++++++++++++++++++++++++++++++++++++-
hw/vfio/trace-events | 6 ++
include/hw/vfio/vfio-common.h | 1 +
3 files changed, 251 insertions(+), 1 deletion(-)
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index efadc04c9fe7..97fbb0c2b301 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -139,6 +139,137 @@ static int vfio_migration_set_state(VFIODevice *vbasedev,
uint32_t mask,
return 0;
}
+static void *find_data_region(VFIORegion *region,
+ uint64_t data_offset,
+ uint64_t data_size)
+{
+ void *ptr = NULL;
+ int i;
+
+ for (i = 0; i < region->nr_mmaps; i++) {
+ if ((data_offset >= region->mmaps[i].offset) &&
+ (data_offset < region->mmaps[i].offset + region->mmaps[i].size) &&
+ (data_size <= region->mmaps[i].size)) {
+ ptr = region->mmaps[i].mmap + (data_offset -
+ region->mmaps[i].offset);
+ break;
+ }
+ }
+ return ptr;
+}
+
+static int vfio_save_buffer(QEMUFile *f, VFIODevice *vbasedev)
+{
+ VFIOMigration *migration = vbasedev->migration;
+ VFIORegion *region = &migration->region;
+ uint64_t data_offset = 0, data_size = 0;
+ int ret;
+
+ ret = pread(vbasedev->fd, &data_offset, sizeof(data_offset),
+ region->fd_offset + offsetof(struct vfio_device_migration_info,
+ data_offset));
+ if (ret != sizeof(data_offset)) {
+ error_report("%s: Failed to get migration buffer data offset %d",
+ vbasedev->name, ret);
+ return -EINVAL;
+ }
+
+ ret = pread(vbasedev->fd, &data_size, sizeof(data_size),
+ region->fd_offset + offsetof(struct vfio_device_migration_info,
+ data_size));
+ if (ret != sizeof(data_size)) {
+ error_report("%s: Failed to get migration buffer data size %d",
+ vbasedev->name, ret);
+ return -EINVAL;
+ }
+
+ if (data_size > 0) {
+ void *buf = NULL;
+ bool buffer_mmaped;
+
+ if (region->mmaps) {
+ buf = find_data_region(region, data_offset, data_size);
+ }
+
+ buffer_mmaped = (buf != NULL);
+
+ if (!buffer_mmaped) {
+ buf = g_try_malloc(data_size);
+ if (!buf) {
+ error_report("%s: Error allocating buffer ", __func__);
+ return -ENOMEM;
+ }
+
+ ret = pread(vbasedev->fd, buf, data_size,
+ region->fd_offset + data_offset);
+ if (ret != data_size) {
+ error_report("%s: Failed to get migration data %d",
+ vbasedev->name, ret);
+ g_free(buf);
+ return -EINVAL;
+ }
+ }
+
+ qemu_put_be64(f, data_size);
+ qemu_put_buffer(f, buf, data_size);
+
+ if (!buffer_mmaped) {
+ g_free(buf);
+ }
+ } else {
+ qemu_put_be64(f, data_size);
+ }
+
+ trace_vfio_save_buffer(vbasedev->name, data_offset, data_size,
+ migration->pending_bytes);
+
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ return ret;
+ }
+
+ return data_size;
+}
+
+static int vfio_update_pending(VFIODevice *vbasedev)
+{
+ VFIOMigration *migration = vbasedev->migration;
+ VFIORegion *region = &migration->region;
+ uint64_t pending_bytes = 0;
+ int ret;
+
+ ret = pread(vbasedev->fd, &pending_bytes, sizeof(pending_bytes),
+ region->fd_offset + offsetof(struct vfio_device_migration_info,
+ pending_bytes));
+ if ((ret < 0) || (ret != sizeof(pending_bytes))) {
+ error_report("%s: Failed to get pending bytes %d",
+ vbasedev->name, ret);
+ migration->pending_bytes = 0;
+ return (ret < 0) ? ret : -EINVAL;
+ }
+
+ migration->pending_bytes = pending_bytes;
+ trace_vfio_update_pending(vbasedev->name, pending_bytes);
+ return 0;
+}
+
+static int vfio_save_device_config_state(QEMUFile *f, void *opaque)
+{
+ VFIODevice *vbasedev = opaque;
+
+ qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_STATE);
+
+ if (vbasedev->ops && vbasedev->ops->vfio_save_config) {
+ vbasedev->ops->vfio_save_config(vbasedev, f);
+ }
+
+ qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
+
+ trace_vfio_save_device_config_state(vbasedev->name);
+
+ return qemu_file_get_error(f);
+}
+
/* ---------------------------------------------------------------------- */
static int vfio_save_setup(QEMUFile *f, void *opaque)
@@ -157,7 +288,7 @@ static int vfio_save_setup(QEMUFile *f, void *opaque)
qemu_mutex_unlock_iothread();
if (ret) {
error_report("%s: Failed to mmap VFIO migration region %d: %s",
- vbasedev->name, migration->region.index,
+ vbasedev->name, migration->region.nr,
strerror(-ret));
return ret;
}
@@ -196,9 +327,121 @@ static void vfio_save_cleanup(void *opaque)
trace_vfio_save_cleanup(vbasedev->name);
}
+static void vfio_save_pending(QEMUFile *f, void *opaque,
+ uint64_t threshold_size,
+ uint64_t *res_precopy_only,
+ uint64_t *res_compatible,
+ uint64_t *res_postcopy_only)
+{
+ VFIODevice *vbasedev = opaque;
+ VFIOMigration *migration = vbasedev->migration;
+ int ret;
+
+ ret = vfio_update_pending(vbasedev);
+ if (ret) {
+ return;
+ }
+
+ *res_precopy_only += migration->pending_bytes;
+
+ trace_vfio_save_pending(vbasedev->name, *res_precopy_only,
+ *res_postcopy_only, *res_compatible);
+}
+
+static int vfio_save_iterate(QEMUFile *f, void *opaque)
+{
+ VFIODevice *vbasedev = opaque;
+ int ret, data_size;
+
+ qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE);
+
+ data_size = vfio_save_buffer(f, vbasedev);
+
+ if (data_size < 0) {
+ error_report("%s: vfio_save_buffer failed %s", vbasedev->name,
+ strerror(errno));
+ return data_size;
+ }
+
+ qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
+
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ return ret;
+ }
+
+ trace_vfio_save_iterate(vbasedev->name, data_size);
+ if (data_size == 0) {
+ /* indicates data finished, goto complete phase */
+ return 1;
+ }
+
+ return 0;
+}
+
+static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
+{
+ VFIODevice *vbasedev = opaque;
+ VFIOMigration *migration = vbasedev->migration;
+ int ret;
+
+ ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_RUNNING,
+ VFIO_DEVICE_STATE_SAVING);
+ if (ret) {
+ error_report("%s: Failed to set state STOP and SAVING",
+ vbasedev->name);
+ return ret;
+ }
+
+ ret = vfio_save_device_config_state(f, opaque);
+ if (ret) {
+ return ret;
+ }
+
+ ret = vfio_update_pending(vbasedev);
+ if (ret) {
+ return ret;
+ }
+
+ while (migration->pending_bytes > 0) {
+ qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE);
+ ret = vfio_save_buffer(f, vbasedev);
+ if (ret < 0) {
+ error_report("%s: Failed to save buffer", vbasedev->name);
+ return ret;
+ } else if (ret == 0) {
+ break;
+ }
+
+ ret = vfio_update_pending(vbasedev);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE);
+
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ return ret;
+ }
+
+ ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_SAVING, 0);
+ if (ret) {
+ error_report("%s: Failed to set state STOPPED", vbasedev->name);
+ return ret;
+ }
+
+ trace_vfio_save_complete_precopy(vbasedev->name);
+ return ret;
+}
+
static SaveVMHandlers savevm_vfio_handlers = {
.save_setup = vfio_save_setup,
.save_cleanup = vfio_save_cleanup,
+ .save_live_pending = vfio_save_pending,
+ .save_live_iterate = vfio_save_iterate,
+ .save_live_complete_precopy = vfio_save_complete_precopy,
};
/* ---------------------------------------------------------------------- */
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index 86c18def016e..9a1c5e17d97f 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -151,3 +151,9 @@ vfio_vmstate_change(const char *name, int running, const
char *reason, uint32_t
vfio_migration_state_notifier(const char *name, const char *state) " (%s)
state %s"
vfio_save_setup(const char *name) " (%s)"
vfio_save_cleanup(const char *name) " (%s)"
+vfio_save_buffer(const char *name, uint64_t data_offset, uint64_t data_size,
uint64_t pending) " (%s) Offset 0x%"PRIx64" size 0x%"PRIx64" pending 0x%"PRIx64
+vfio_update_pending(const char *name, uint64_t pending) " (%s) pending
0x%"PRIx64
+vfio_save_device_config_state(const char *name) " (%s)"
+vfio_save_pending(const char *name, uint64_t precopy, uint64_t postcopy,
uint64_t compatible) " (%s) precopy 0x%"PRIx64" postcopy 0x%"PRIx64" compatible
0x%"PRIx64
+vfio_save_iterate(const char *name, int data_size) " (%s) data_size %d"
+vfio_save_complete_precopy(const char *name) " (%s)"
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 28f55f66d019..c78033e4149d 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -60,6 +60,7 @@ typedef struct VFIORegion {
typedef struct VFIOMigration {
VFIORegion region;
+ uint64_t pending_bytes;
} VFIOMigration;
typedef struct VFIOAddressSpace {
--
2.7.0
- [PATCH v18 QEMU 04/18] vfio: Add save and load functions for VFIO PCI devices, (continued)
- [PATCH v18 QEMU 04/18] vfio: Add save and load functions for VFIO PCI devices, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 05/18] vfio: Add migration region initialization and finalize function, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 06/18] vfio: Add VM state change handler to know state of VM, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 07/18] vfio: Add migration state change notifier, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 08/18] vfio: Register SaveVMHandlers for VFIO device, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 10/18] vfio: Add load state functions to SaveVMHandlers, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 11/18] iommu: add callback to get address limit IOMMU supports, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 09/18] vfio: Add save state functions to SaveVMHandlers,
Kirti Wankhede <=
- [PATCH v18 QEMU 12/18] memory: Set DIRTY_MEMORY_MIGRATION when IOMMU is enabled, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 13/18] vfio: Add function to start and stop dirty pages tracking, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 14/18] vfio: Add vfio_listener_log_sync to mark dirty pages, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 15/18] vfio: Get migration capability flags for container, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 16/18] vfio: Add ioctl to get dirty pages bitmap during dma unmap., Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 17/18] vfio: Make vfio-pci device migration capable, Kirti Wankhede, 2020/05/04
- [PATCH v18 QEMU 18/18] qapi: Add VFIO devices migration stats in Migration stats, Kirti Wankhede, 2020/05/04