Non-shared storage migration of guests which are disk I/O intensive and have fast local storage may actually never converge if the guest happens to dirty the disk faster than it can be copied. This patch introduces a new flag 'VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES' which will instruct hypervisors to synchronize local I/O writes with the writes to remote storage used for migration so that the guest can't overwhelm the migration. This comes at a cost of decreased local I/O performance for guests which behave well on average. Signed-off-by: Peter Krempa <pkrempa@xxxxxxxxxx> --- docs/manpages/virsh.rst | 6 +++++- include/libvirt/libvirt-domain.h | 10 ++++++++++ src/libvirt-domain.c | 20 ++++++++++++++++++++ tools/virsh-domain.c | 13 +++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 7c50388216..1ce3e77c9f 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -3180,7 +3180,7 @@ migrate [--postcopy-bandwidth bandwidth] [--parallel [--parallel-connections connections]] [--bandwidth bandwidth] [--tls-destination hostname] - [--disks-uri URI] + [--disks-uri URI] [--copy-storage-synchronous-writes] Migrate domain to another host. Add *--live* for live migration; <--p2p> for peer-2-peer migration; *--direct* for direct migration; or *--tunnelled* @@ -3202,6 +3202,10 @@ images on source host to the images found at the same place on the destination host. By default only non-shared non-readonly images are transferred. Use *--migrate-disks* to explicitly specify a list of disk targets to transfer via the comma separated ``disk-list`` argument. +With *--copy-storage-synchronous-writes* flag used the disk data migration will +synchronous handle guest disk writes to both the original soure and the +destination to ensure that the disk migration coverges at the price of possibly +decreased burst performance. *--change-protection* enforces that no incompatible configuration changes will be made to the domain while the migration is underway; this flag is implicitly diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index f81e96d374..d0dd11ab01 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -850,6 +850,16 @@ typedef enum { */ VIR_MIGRATE_PARALLEL = (1 << 17), + /* Force the guest writes which happen when copying disk images for + * non-shared storage migration to be synchronously written to the + * destination. This ensures the storage migration converges for VMs + * doing heavy I/O on fast local storage and slow mirror. + * + * Requires one of VIR_MIGRATE_NON_SHARED_DISK, VIR_MIGRATE_NON_SHARED_INC + * to be present as well. + */ + VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES = (1 << 18), + } virDomainMigrateFlags; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 8ee2490867..5708ff839b 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -3567,6 +3567,10 @@ virDomainMigrate(virDomainPtr domain, VIR_MIGRATE_PARALLEL, error); + VIR_REQUIRE_FLAG_GOTO(VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES, + VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC, + error); + if (flags & VIR_MIGRATE_OFFLINE) { rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATION_OFFLINE); @@ -3760,6 +3764,10 @@ virDomainMigrate2(virDomainPtr domain, VIR_MIGRATE_PARALLEL, error); + VIR_REQUIRE_FLAG_GOTO(VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES, + VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC, + error); + if (flags & VIR_MIGRATE_OFFLINE) { rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATION_OFFLINE); @@ -3966,6 +3974,14 @@ virDomainMigrate3(virDomainPtr domain, VIR_MIGRATE_NON_SHARED_INC, error); + VIR_REQUIRE_FLAG_GOTO(VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES, + VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC, + error); + + VIR_REQUIRE_FLAG_GOTO(VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES, + VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC, + error); + if (flags & VIR_MIGRATE_PEER2PEER) { virReportInvalidArg(flags, "%s", _("use virDomainMigrateToURI3 for peer-to-peer " @@ -4137,6 +4153,10 @@ int virDomainMigrateUnmanagedCheckCompat(virDomainPtr domain, VIR_MIGRATE_NON_SHARED_INC, -1); + VIR_REQUIRE_FLAG_RET(VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES, + VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC, + -1); + if (flags & VIR_MIGRATE_OFFLINE) { rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn, VIR_DRV_FEATURE_MIGRATION_OFFLINE); diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index a00b4cc243..8379f9f135 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -10543,6 +10543,10 @@ static const vshCmdOptDef opts_migrate[] = { .type = VSH_OT_BOOL, .help = N_("migration with non-shared storage with incremental copy (same base image shared between source and destination)") }, + {.name = "copy-storage-synchronous-writes", + .type = VSH_OT_BOOL, + .help = N_("force guest disk writes to be synchronously written to the destination to improve storage migration convergence") + }, {.name = "change-protection", .type = VSH_OT_BOOL, .help = N_("prevent any configuration changes to domain until migration ends") @@ -10949,6 +10953,15 @@ doMigrate(void *opaque) if (vshCommandOptBool(cmd, "copy-storage-inc")) flags |= VIR_MIGRATE_NON_SHARED_INC; + if (vshCommandOptBool(cmd, "copy-storage-synchronous-writes")) { + if (!(flags & VIR_MIGRATE_NON_SHARED_DISK) && + !(flags & VIR_MIGRATE_NON_SHARED_INC)) { + vshError(ctl, "'--copy-storage-synchronous-writes' requires one of '--copy-storage-all', 'copy-storage-inc'"); + goto out; + } + flags |= VIR_MIGRATE_NON_SHARED_SYNCHRONOUS_WRITES; + } + if (vshCommandOptBool(cmd, "change-protection")) flags |= VIR_MIGRATE_CHANGE_PROTECTION; -- 2.31.1