From nobody Fri Dec 19 20:16:36 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1738953759; cv=none; d=zohomail.com; s=zohoarc; b=XSV25mRNOjzAXPTdHcTqSjT2yKPJYFwB+BTmpQhLB22HwHm79H1Rpx/6LDu12WDMQFx0XA/QWMFoce3ssVWdpZ0tbCGSSSWUZKY7y205KTdbZPdtEb5EsmtkccMsshTegGR4skbq+xnsAaWl5HyChixqRUtLF9Xo66++Z1e4x94= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1738953759; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id; bh=1KgXyx8sah6SbhTK4GCf9j2N/W1MIBK+z3u1PeuK55g=; b=NqtsBS9+Qh26SHzBSlcvi8++Q1QWaRk2PSFjwzYTv8P41N3ws3xrL/BeP24Xfl/8vpIjNw9D4ti/KMwHcEuTHuFqS+F39pZ8CqLYlSA+bvjFZtKdcwCVedk6VnhUQnD76lmGcOS1ZQ8HPJzylSVbw2n3J0W4DCT6VBVecLT1BF4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1738953759081260.15481500731005; Fri, 7 Feb 2025 10:42:39 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 1806C15A5; Fri, 7 Feb 2025 13:42:38 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 8CA3B1715; Fri, 7 Feb 2025 13:38:21 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id A572C1508; Fri, 7 Feb 2025 13:38:13 -0500 (EST) Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 68C00159C for ; Fri, 7 Feb 2025 13:37:56 -0500 (EST) Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-38dd14c99c3so146655f8f.3 for ; Fri, 07 Feb 2025 10:37:56 -0800 (PST) Received: from localhost (205-201-32-8.scinternet.net. [205.201.32.8]) by smtp.gmail.com with UTF8SMTPSA id 98e67ed59e1d1-2f9e1e40ab8sm5877956a91.42.2025.02.07.10.37.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 07 Feb 2025 10:37:54 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.com; s=google; t=1738953475; x=1739558275; darn=lists.libvirt.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZFivyDUpbWNs8S/MdEFSVn5Dom6/G56qWVI+FwKZmPk=; b=Psit60MaThya9CtP69CLWj7g6TZWDNLnOfxf4K5HKTC2mldX/mAVnVP61nTELlCtMO PMIOqYKNGYW4SLvkSsWo+jSjrQeDHDF6KXYHXGINNgH0RX51s7fL5LYapQI0VvmQFAUL kuOzEnu5wBF9NKDvbpmwqLznq41WwZ+M2RxmyfO1jOZIEHhglDb9+I51JO/L3ild0VBh fJs1v/E+YY+4oAPU+HrK7/aZrIG+KTmydkSt10gBGlhvOiKZk8YHmeR4GgleaW9xaXaF iCaW61pjHIhWhGRIbmpq5FR26h49RbjJmpxQnZU19pSI69PTIlZJ03nvht6EC9vC0PHM t3UQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738953475; x=1739558275; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ZFivyDUpbWNs8S/MdEFSVn5Dom6/G56qWVI+FwKZmPk=; b=UZzg+FD9sZAevUN0El/nyrZaTwMmLNI3c6aP+X9T32++239d4AVJC5rym8T7v1GIys wflN5QyQoDF+E6+xWOUVzfrwIBItqGZWpHGuXwyk27tsjy3yaomYX8egQ2PihjXQgQuw hZTgQIEYW6ScYmov2GIJoF4IYuV2oM8YhQZCTsR0lpYPg8CUGbLZeVcE5iVEY7leu/oo xJK2KxPUzfnRHByE7fRSn9LvGbDlbTsSsh7D0+rahkspDdi2TiQHTkInXzJVQGdtAdEz lmIPmDwUHNkE4h3Dk4UXwusGyV4m/H6OnsnJG+UGDspyMzcn11Sc2ScewZCVA/xOQzS1 3TMw== X-Gm-Message-State: AOJu0YypZRB18UOvabtDPeyneM5iunKNIWe0pT4GJq7//zLy+G2txYc7 xmC9sPQBXjpEe/U1GnDrJE5Nu3/3qgLJ9vD5WUO1M+6gswr/Zcf+P5ifOE8DnxSciwNWfkUvocD l X-Gm-Gg: ASbGncuzkf3a/aPkTjwAl7QDqn8z1fSKcTV3k+I36+oY/p0p2SSfANo1sZnVhyfZ4OD b2DG/zx3zS61z9KfOOJn8EtkFBGBi4WN3e6Fv1FN6nGKzarvMxLXTpvHW6Pcb8Imiiwx4ogqUZ4 4tsV7teV2oN2Rrg8jMW9VtjhmGY3gNrIBosHf1qTDxsfzzkc4s/+tMvFvQomMm9unBvIYooCQSY 7I6tiKT6jbd2Ziult1dX4j9Tm0VyxDbYyRqmhy8ICanYE+fOOFIjUZUVzHIKM6AG6KS/i8qZcgo ZpK5ACYUSXDKfRRE9tx9EGpnmUu7nyo3Qw== X-Google-Smtp-Source: AGHT+IHcmjyr4p+fJXCyaXE7cOygoUJLt3vpm5HrqmbBlBJHrgD+8s4WoK1TsuWjrsv4xZ6X8r61GQ== X-Received: by 2002:a05:6000:1a8a:b0:38c:5b9e:ab83 with SMTP id ffacd0b85a97d-38dc935f177mr3430045f8f.40.1738953475017; Fri, 07 Feb 2025 10:37:55 -0800 (PST) To: devel@lists.libvirt.org Subject: [PATCH V3 10/19] qemu: Add support for mapped-ram on save Date: Fri, 7 Feb 2025 11:27:21 -0700 Message-ID: <20250207183730.21686-11-jfehlig@suse.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250207183730.21686-1-jfehlig@suse.com> References: <20250207183730.21686-1-jfehlig@suse.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: Y3UZGFQL73KMELVB56QSS32FJUG4BRV7 X-Message-ID-Hash: Y3UZGFQL73KMELVB56QSS32FJUG4BRV7 X-MailFrom: jfehlig@suse.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: farosas@suse.de X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: From: Jim Fehlig via Devel Reply-To: Jim Fehlig X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1738953761593019100 Content-Type: text/plain; charset="utf-8" Introduce support for QEMU's new mapped-ram stream format [1]. mapped-ram can be enabled by setting the 'save_image_format' setting in qemu.conf to 'sparse'. To use mapped-ram with QEMU: - The 'mapped-ram' migration capability must be set to true - The 'multifd' migration capability must be set to true and the 'multifd-channels' migration parameter must set to 1 - QEMU must be provided an fdset containing the migration fd - The 'migrate' qmp command is invoked with a URI referencing the fdset and an offset where to start reading or writing the data stream, e.g. {"execute":"migrate", "arguments":{"detach":true,"resume":false, "uri":"file:/dev/fdset/0,offset=3D0x11921"}} The mapped-ram stream, in conjunction with direct IO and multifd support provided by subsequent patches, can significantly improve the time required to save VM memory state. The following tables compare mapped-ram with the existing, sequential save stream. In all cases, the save and restore operations are to/from a block device comprised of two NVMe disks in RAID0 configuration with xfs (~8600MiB/s). The values in the 'save time' and 'restore time' columns were scraped from the 'real' time reported by time(1). The 'Size' and 'Blocks' columns were provided by the corresponding outputs of stat(1). VM: 32G RAM, 1 vcpu, idle (shortly after boot) | save | restore | | time | time | Size | Blocks -----------------------+---------+---------+--------------+-------- legacy | 6.193s | 4.399s | 985744812 | 1925288 -----------------------+---------+---------+--------------+-------- mapped-ram | 5.109s | 1.176s | 34368554354 | 1774472 -----------------------+---------+---------+--------------+-------- legacy + direct IO | 5.725s | 4.512s | 985765251 | 1925328 -----------------------+---------+---------+--------------+-------- mapped-ram + direct IO | 4.627s | 1.490s | 34368554354 | 1774304 -----------------------+---------+---------+--------------+-------- mapped-ram + direct IO | | | | + multifd-channels=3D8 | 4.421s | 0.845s | 34368554318 | 1774312 ------------------------------------------------------------------- VM: 32G RAM, 30G dirty, 1 vcpu in tight loop dirtying memory | save | restore | | time | time | Size | Blocks -----------------------+---------+---------+--------------+--------- legacy | 25.800s | 14.332s | 33154309983 | 64754512 -----------------------+---------+---------+--------------+--------- mapped-ram | 18.742s | 15.027s | 34368559228 | 64617160 -----------------------+---------+---------+--------------+--------- legacy + direct IO | 13.115s | 18.050s | 33154310496 | 64754520 -----------------------+---------+---------+--------------+--------- mapped-ram + direct IO | 13.623s | 15.959s | 34368557392 | 64662040 -----------------------+-------- +---------+--------------+--------- mapped-ram + direct IO | | | | + multifd-channels=3D8 | 6.994s | 6.470s | 34368554980 | 64665776 -------------------------------------------------------------------- As can be seen from the tables, one caveat of mapped-ram is the logical file size of a saved image is basically equivalent to the VM memory size. Note however that mapped-ram typically uses fewer blocks on disk, hence the name 'sparse' for 'save_image_format'. Also note the mapped-ram stream is incompatible with the existing stream format, hence mapped-ram cannot be used to restore an image saved with the existing format and vice versa. [1] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/devel/migration= /mapped-ram.rst?ref_type=3Dheads Signed-off-by: Jim Fehlig --- src/qemu/qemu_driver.c | 23 +++++- src/qemu/qemu_migration.c | 149 ++++++++++++++++++++++++++------------ src/qemu/qemu_migration.h | 4 +- src/qemu/qemu_monitor.c | 34 +++++++++ src/qemu/qemu_monitor.h | 4 + src/qemu/qemu_saveimage.c | 33 +++++---- src/qemu/qemu_saveimage.h | 1 + src/qemu/qemu_snapshot.c | 14 +++- 8 files changed, 199 insertions(+), 63 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 78bfaa5b3a..f77516a4f4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2620,6 +2620,7 @@ qemuDomainSaveInternal(virQEMUDriver *driver, qemuDomainObjPrivate *priv =3D vm->privateData; virQEMUSaveData *data =3D NULL; g_autoptr(qemuDomainSaveCookie) cookie =3D NULL; + g_autoptr(qemuMigrationParams) saveParams =3D NULL; =20 if (virDomainObjBeginAsyncJob(vm, VIR_ASYNC_JOB_SAVE, VIR_DOMAIN_JOB_OPERATION_SAVE, flags) < = 0) @@ -2628,6 +2629,14 @@ qemuDomainSaveInternal(virQEMUDriver *driver, if (!qemuMigrationSrcIsAllowed(vm, false, VIR_ASYNC_JOB_SAVE, 0)) goto endjob; =20 + if (format =3D=3D QEMU_SAVE_FORMAT_SPARSE && + !qemuMigrationCapsGet(vm, QEMU_MIGRATION_CAP_MAPPED_RAM)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("save image format %1$s is not supported by this = QEMU binary"), + qemuSaveFormatTypeToString(format)); + goto endjob; + } + if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("guest unexpectedly quit")); @@ -2691,8 +2700,11 @@ qemuDomainSaveInternal(virQEMUDriver *driver, goto endjob; xml =3D NULL; =20 + if (!(saveParams =3D qemuMigrationParamsForSave(format =3D=3D QEMU_SAV= E_FORMAT_SPARSE))) + goto endjob; + ret =3D qemuSaveImageCreate(driver, vm, path, data, compressor, - flags, VIR_ASYNC_JOB_SAVE); + saveParams, flags, VIR_ASYNC_JOB_SAVE); if (ret < 0) goto endjob; =20 @@ -3126,6 +3138,8 @@ doCoreDump(virQEMUDriver *driver, memory_dump_format) < 0) goto cleanup; } else { + g_autoptr(qemuMigrationParams) dump_params =3D NULL; + if (dumpformat !=3D VIR_DOMAIN_CORE_DUMP_FORMAT_RAW) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("kdump-compressed format is only supported wi= th memory-only dump")); @@ -3135,8 +3149,11 @@ doCoreDump(virQEMUDriver *driver, if (!qemuMigrationSrcIsAllowed(vm, false, VIR_ASYNC_JOB_DUMP, 0)) goto cleanup; =20 - if (qemuMigrationSrcToFile(driver, vm, fd, compressor, - VIR_ASYNC_JOB_DUMP) < 0) + if (!(dump_params =3D qemuMigrationParamsNew())) + goto cleanup; + + if (qemuMigrationSrcToFile(driver, vm, &fd, compressor, + dump_params, dump_flags, VIR_ASYNC_JOB_= DUMP) < 0) goto cleanup; } =20 diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4efc03fc00..1e3bee3781 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -7069,46 +7069,17 @@ qemuMigrationProcessUnattended(virQEMUDriver *drive= r, } =20 =20 -/* Helper function called while vm is active. */ -int -qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm, - int fd, - virCommand *compressor, - virDomainAsyncJob asyncJob) +static int +qemuMigrationSrcToLegacyFile(virQEMUDriver *driver, + virDomainObj *vm, + int fd, + virCommand *compressor, + virDomainAsyncJob asyncJob) { qemuDomainObjPrivate *priv =3D vm->privateData; - int rc; int ret =3D -1; int pipeFD[2] =3D { -1, -1 }; - unsigned long saveMigBandwidth =3D priv->migMaxBandwidth; char *errbuf =3D NULL; - virErrorPtr orig_err =3D NULL; - g_autoptr(qemuMigrationParams) migParams =3D NULL; - - if (qemuMigrationSetDBusVMState(driver, vm) < 0) - return -1; - - /* Increase migration bandwidth to unlimited since target is a file. - * Failure to change migration speed is not fatal. */ - if (!(migParams =3D qemuMigrationParamsForSave(false))) - return -1; - - if (qemuMigrationParamsSetULL(migParams, - QEMU_MIGRATION_PARAM_MAX_BANDWIDTH, - QEMU_DOMAIN_MIG_BANDWIDTH_MAX * 1024 * 1= 024) < 0) - return -1; - - if (qemuMigrationParamsApply(vm, asyncJob, migParams, 0) < 0) - return -1; - - priv->migMaxBandwidth =3D QEMU_DOMAIN_MIG_BANDWIDTH_MAX; - - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("guest unexpectedly quit")); - /* nothing to tear down */ - return -1; - } =20 if (compressor && virPipe(pipeFD) < 0) return -1; @@ -7125,7 +7096,7 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDoma= inObj *vm, goto cleanup; =20 if (!compressor) { - rc =3D qemuMonitorMigrateToFd(priv->mon, 0, fd); + ret =3D qemuMonitorMigrateToFd(priv->mon, 0, fd); } else { virCommandSetInputFD(compressor, pipeFD[0]); virCommandSetOutputFD(compressor, &fd); @@ -7141,12 +7112,98 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDo= mainObj *vm, qemuDomainObjExitMonitor(vm); goto cleanup; } - rc =3D qemuMonitorMigrateToFd(priv->mon, 0, pipeFD[1]); + ret =3D qemuMonitorMigrateToFd(priv->mon, 0, pipeFD[1]); if (VIR_CLOSE(pipeFD[0]) < 0 || VIR_CLOSE(pipeFD[1]) < 0) VIR_WARN("failed to close intermediate pipe"); } qemuDomainObjExitMonitor(vm); + + cleanup: + VIR_FORCE_CLOSE(pipeFD[0]); + VIR_FORCE_CLOSE(pipeFD[1]); + + if (errbuf) { + VIR_DEBUG("Compression binary stderr: %s", NULLSTR(errbuf)); + VIR_FREE(errbuf); + } + + return ret; +} + + +static int +qemuMigrationSrcToSparseFile(virQEMUDriver *driver, + virDomainObj *vm, + int *fd, + unsigned int flags, + virDomainAsyncJob asyncJob) +{ + int ret; + + /* mapped-ram does not support directIO */ + if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("bypass cache unsupported by this system")); + return -1; + } + + if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, *fd)= < 0) + return -1; + + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) < 0) + return -1; + + ret =3D qemuMonitorMigrateToFdSet(vm, 0, fd); + qemuDomainObjExitMonitor(vm); + return ret; +} + + +/* Helper function called while vm is active. */ +int +qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm, + int *fd, + virCommand *compressor, + qemuMigrationParams *migParams, + unsigned int flags, + virDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + int rc; + int ret =3D -1; + unsigned long saveMigBandwidth =3D priv->migMaxBandwidth; + virErrorPtr orig_err =3D NULL; + + if (qemuMigrationSetDBusVMState(driver, vm) < 0) + return -1; + + /* Increase migration bandwidth to unlimited since target is a file. + * Failure to change migration speed is not fatal. */ + if (migParams && + qemuMigrationParamsSetULL(migParams, + QEMU_MIGRATION_PARAM_MAX_BANDWIDTH, + QEMU_DOMAIN_MIG_BANDWIDTH_MAX * 1024 * 1= 024) < 0) + return -1; + + if (qemuMigrationParamsApply(vm, asyncJob, migParams, 0) < 0) + return -1; + + priv->migMaxBandwidth =3D QEMU_DOMAIN_MIG_BANDWIDTH_MAX; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit")); + /* nothing to tear down */ + return -1; + } + + if (migParams && + qemuMigrationParamsCapEnabled(migParams, QEMU_MIGRATION_CAP_MAPPED= _RAM)) + rc =3D qemuMigrationSrcToSparseFile(driver, vm, fd, flags, asyncJo= b); + else + rc =3D qemuMigrationSrcToLegacyFile(driver, vm, *fd, compressor, a= syncJob); + if (rc < 0) goto cleanup; =20 @@ -7172,8 +7229,17 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDom= ainObj *vm, if (ret < 0 && !orig_err) virErrorPreserveLast(&orig_err); =20 - /* Restore max migration bandwidth */ + /* Remove fdset passed to qemu and restore max migration bandwidth */ if (qemuDomainObjIsActive(vm)) { + if (qemuDomainObjEnterMonitorAsync(vm, asyncJob) =3D=3D 0) { + qemuFDPass *fdPass =3D + qemuFDPassNewFromMonitor("libvirt-outgoing-migrate", priv-= >mon); + + if (fdPass) + qemuFDPassTransferMonitorRollback(fdPass, priv->mon); + qemuDomainObjExitMonitor(vm); + } + if (qemuMigrationParamsSetULL(migParams, QEMU_MIGRATION_PARAM_MAX_BANDWIDTH, saveMigBandwidth * 1024 * 1024) =3D= =3D 0) @@ -7182,13 +7248,6 @@ qemuMigrationSrcToFile(virQEMUDriver *driver, virDom= ainObj *vm, priv->migMaxBandwidth =3D saveMigBandwidth; } =20 - VIR_FORCE_CLOSE(pipeFD[0]); - VIR_FORCE_CLOSE(pipeFD[1]); - if (errbuf) { - VIR_DEBUG("Compression binary stderr: %s", NULLSTR(errbuf)); - VIR_FREE(errbuf); - } - virErrorRestore(&orig_err); =20 return ret; diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index efe1b9e88a..9fa007b949 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -238,8 +238,10 @@ qemuMigrationSrcIsAllowed(virDomainObj *vm, int qemuMigrationSrcToFile(virQEMUDriver *driver, virDomainObj *vm, - int fd, + int *fd, virCommand *compressor, + qemuMigrationParams *migParams, + unsigned int flags, virDomainAsyncJob asyncJob) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; =20 diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 830ecbad1c..e2043c0120 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2232,6 +2232,40 @@ qemuMonitorMigrateToFd(qemuMonitor *mon, } =20 =20 +int +qemuMonitorMigrateToFdSet(virDomainObj *vm, + unsigned int flags, + int *fd) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + qemuMonitor *mon =3D priv->mon; + off_t offset; + g_autoptr(qemuFDPass) fdPassMigrate =3D NULL; + g_autofree char *uri =3D NULL; + int ret; + + VIR_DEBUG("fd=3D%d flags=3D0x%x", *fd, flags); + + QEMU_CHECK_MONITOR(mon); + + if ((offset =3D lseek(*fd, 0, SEEK_CUR)) =3D=3D -1) { + virReportSystemError(errno, + "%s", _("failed to seek on file descriptor")); + return -1; + } + + fdPassMigrate =3D qemuFDPassNew("libvirt-outgoing-migrate", priv); + qemuFDPassAddFD(fdPassMigrate, fd, "-fd"); + qemuFDPassTransferMonitor(fdPassMigrate, mon); + + uri =3D g_strdup_printf("file:%s,offset=3D%#lx", + qemuFDPassGetPath(fdPassMigrate), offset); + ret =3D qemuMonitorJSONMigrate(mon, flags, uri); + + return ret; +} + + int qemuMonitorMigrateToHost(qemuMonitor *mon, unsigned int flags, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 072f452e79..6da380aa65 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -859,6 +859,10 @@ int qemuMonitorMigrateToFd(qemuMonitor *mon, unsigned int flags, int fd); =20 +int qemuMonitorMigrateToFdSet(virDomainObj *vm, + unsigned int flags, + int *fd); + int qemuMonitorMigrateToHost(qemuMonitor *mon, unsigned int flags, const char *protocol, diff --git a/src/qemu/qemu_saveimage.c b/src/qemu/qemu_saveimage.c index 59237cc46a..0ffbe03f24 100644 --- a/src/qemu/qemu_saveimage.c +++ b/src/qemu/qemu_saveimage.c @@ -430,6 +430,7 @@ qemuSaveImageCreateFd(virQEMUDriver *driver, virDomainObj *vm, const char *path, virFileWrapperFd *wrapperFd, + bool sparse, bool *needUnlink, unsigned int flags) { @@ -439,7 +440,7 @@ qemuSaveImageCreateFd(virQEMUDriver *driver, int directFlag =3D 0; unsigned int wrapperFlags =3D VIR_FILE_WRAPPER_NON_BLOCKING; =20 - if (flags & VIR_DOMAIN_SAVE_BYPASS_CACHE) { + if (!sparse && flags & VIR_DOMAIN_SAVE_BYPASS_CACHE) { wrapperFlags |=3D VIR_FILE_WRAPPER_BYPASS_CACHE; directFlag =3D virFileDirectFdFlag(); if (directFlag < 0) { @@ -459,7 +460,7 @@ qemuSaveImageCreateFd(virQEMUDriver *driver, if (qemuSecuritySetImageFDLabel(driver->securityManager, vm->def, fd) = < 0) return -1; =20 - if (!(wrapperFd =3D virFileWrapperFdNew(&fd, path, wrapperFlags))) + if (!sparse && !(wrapperFd =3D virFileWrapperFdNew(&fd, path, wrapperF= lags))) return -1; =20 ret =3D fd; @@ -478,6 +479,7 @@ qemuSaveImageCreate(virQEMUDriver *driver, const char *path, virQEMUSaveData *data, virCommand *compressor, + qemuMigrationParams *saveParams, unsigned int flags, virDomainAsyncJob asyncJob) { @@ -486,9 +488,10 @@ qemuSaveImageCreate(virQEMUDriver *driver, int ret =3D -1; int fd =3D -1; virFileWrapperFd *wrapperFd =3D NULL; + bool sparse =3D data->header.format =3D=3D QEMU_SAVE_FORMAT_SPARSE; =20 /* Obtain the file handle. */ - fd =3D qemuSaveImageCreateFd(driver, vm, path, wrapperFd, &needUnlink,= flags); + fd =3D qemuSaveImageCreateFd(driver, vm, path, wrapperFd, sparse, &nee= dUnlink, flags); =20 if (fd < 0) goto cleanup; @@ -497,7 +500,7 @@ qemuSaveImageCreate(virQEMUDriver *driver, goto cleanup; =20 /* Perform the migration */ - if (qemuMigrationSrcToFile(driver, vm, fd, compressor, asyncJob) < 0) + if (qemuMigrationSrcToFile(driver, vm, &fd, compressor, saveParams, fl= ags, asyncJob) < 0) goto cleanup; =20 /* Touch up file header to mark image complete. */ @@ -505,14 +508,18 @@ qemuSaveImageCreate(virQEMUDriver *driver, /* Reopen the file to touch up the header, since we aren't set * up to seek backwards on wrapperFd. The reopened fd will * trigger a single page of file system cache pollution, but - * that's acceptable. */ - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, _("unable to close %1$s"), path); - goto cleanup; - } + * that's acceptable. + * If using mapped-ram, the fd was passed to qemu, so no need + * to close it. */ + if (!sparse) { + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, _("unable to close %1$s"), path); + goto cleanup; + } =20 - if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) - goto cleanup; + if (qemuDomainFileWrapperFDClose(vm, wrapperFd) < 0) + goto cleanup; + } =20 if ((fd =3D qemuDomainOpenFile(cfg, vm->def, path, O_WRONLY, NULL)) < = 0 || virQEMUSaveDataFinish(data, &fd, path) < 0) @@ -569,8 +576,8 @@ qemuSaveImageGetCompressionProgram(const char *imageFor= mat, if ((ret =3D qemuSaveFormatTypeFromString(imageFormat)) < 0) goto error; =20 - if (ret =3D=3D QEMU_SAVE_FORMAT_RAW) - return QEMU_SAVE_FORMAT_RAW; + if (ret =3D=3D QEMU_SAVE_FORMAT_RAW || ret =3D=3D QEMU_SAVE_FORMAT_SPA= RSE) + return ret; =20 if (!(prog =3D virFindFileInPath(imageFormat))) goto error; diff --git a/src/qemu/qemu_saveimage.h b/src/qemu/qemu_saveimage.h index a5b55c6b10..2b3d839e5b 100644 --- a/src/qemu/qemu_saveimage.h +++ b/src/qemu/qemu_saveimage.h @@ -138,6 +138,7 @@ qemuSaveImageCreate(virQEMUDriver *driver, const char *path, virQEMUSaveData *data, virCommand *compressor, + qemuMigrationParams *saveParams, unsigned int flags, virDomainAsyncJob asyncJob); =20 diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index ed140dd41c..3088b28716 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1644,6 +1644,8 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *drive= r, =20 /* do the memory snapshot if necessary */ if (memory) { + g_autoptr(qemuMigrationParams) snap_params =3D NULL; + /* check if migration is possible */ if (!qemuMigrationSrcIsAllowed(vm, false, VIR_ASYNC_JOB_SNAPSHOT, = 0)) goto cleanup; @@ -1661,6 +1663,13 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driv= er, "snapshot", false= )) < 0) goto cleanup; =20 + if (format =3D=3D QEMU_SAVE_FORMAT_SPARSE) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Snapshots do not support image format %1$s"), + qemuSaveFormatTypeToString(format)); + goto cleanup; + } + if (!(xml =3D qemuDomainDefFormatLive(driver, priv->qemuCaps, vm->def, priv->origCPU, true, true)) || @@ -1675,8 +1684,11 @@ qemuSnapshotCreateActiveExternal(virQEMUDriver *driv= er, =20 memory_existing =3D virFileExists(snapdef->memorysnapshotfile); =20 + if (!(snap_params =3D qemuMigrationParamsNew())) + goto cleanup; + if ((ret =3D qemuSaveImageCreate(driver, vm, snapdef->memorysnapsh= otfile, - data, compressor, 0, + data, compressor, snap_params, 0, VIR_ASYNC_JOB_SNAPSHOT)) < 0) goto cleanup; =20 --=20 2.43.0