From nobody Sun Feb 8 05:47:49 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1693557161; cv=none; d=zohomail.com; s=zohoarc; b=IuDUXz9PLBSir3K9EISg/X6LX7eT63wtrIWMtox+HIRl+248l5dA9QYL0HqyGkuMWIpxGE7rHVuMbhdOzwQ6TBy234SfNz4OIA+oq5ChQ0oXW6Y3mN8uOqMKcW0UEei4bY27jDmHvdqN8Du5f6u8jENGAAGJYlTSUUPyXBxIU2M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1693557161; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=wIByhVDs4RDn2uRWUSGLFYxD/v4KvkVays32P3F1xM8=; b=ShvaMjkmzJqq/tC3/FoRjXMmJ3xJnpbpZ1OlCLPzPp2cwM+6V1AYb7tRdHBX0I9MQ7dSgPn0INnprV8RJF0lWRtOJ183GRO7cb3xpU7+45BGIIqRJCRuXGoPOa95Z/kuU7w/7Q1MsiSDZ3pXEUsTmBQDtyXEXDwwb3zMF3UguiM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1693557161448722.9576494008519; Fri, 1 Sep 2023 01:32:41 -0700 (PDT) Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-426-oIpUe5-tPaWH825wiUouJg-1; Fri, 01 Sep 2023 04:32:37 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7CDEF29DD99F; Fri, 1 Sep 2023 08:32:35 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 674DE40C2063; Fri, 1 Sep 2023 08:32:35 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 42CFE194729B; Fri, 1 Sep 2023 08:32:30 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id BF4BB19472AC for ; Fri, 1 Sep 2023 08:32:25 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id AE4DFC15BBA; Fri, 1 Sep 2023 08:32:25 +0000 (UTC) Received: from localhost.localdomain (unknown [10.45.225.41]) by smtp.corp.redhat.com (Postfix) with ESMTP id 515E9C15BAE for ; Fri, 1 Sep 2023 08:32:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1693557160; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=wIByhVDs4RDn2uRWUSGLFYxD/v4KvkVays32P3F1xM8=; b=aQWoqw0Tpnz3DK5XPG0pp89qEwXjVPVGMfozjPrHuK0L7EAEfItCmWNv6ZsW6P0LeN0E6G YQ12h4/2F4tdvqJu2drrgAboiWY5Mi/B6gNKHWB1le0T+ZcUF1mL/dYET+rleKbH0fkZuf GsazWh+My1KTG5M5xbZalTxtD0RuRFY= X-MC-Unique: oIpUe5-tPaWH825wiUouJg-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Pavel Hrdina To: libvir-list@redhat.com Subject: [libvirt PATCH v2 4/6] qemu_snapshot: correctly load the saved memory state file Date: Fri, 1 Sep 2023 10:32:15 +0200 Message-ID: <461af59e848069a18cd6ae0fcd9206ec83c405bd.1693556950.git.phrdina@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.8 X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libvir-list-bounces@redhat.com Sender: "libvir-list" X-Scanned-By: MIMEDefang 3.1 on 10.11.54.1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1693557161965100007 Content-Type: text/plain; charset="utf-8"; x-default="true" Original code assumed that the memory state file is only migration stream but it has additional metadata stored by libvirt. To correctly load the memory state file we need to reuse code that is used when restoring domain from saved image. This duplicates some necessary parts of qemuSaveImageStartVM() because the external snapshot memory state is done by qemuSaveImageCreate(). Signed-off-by: Pavel Hrdina Reviewed-by: Peter Krempa --- src/qemu/qemu_snapshot.c | 115 +++++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index ff85d56572..538da6570a 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -2004,6 +2004,22 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, } =20 =20 +typedef struct _qemuSnapshotRevertMemoryData { + int fd; + char *path; + virQEMUSaveData *data; +} qemuSnapshotRevertMemoryData; + + +static void +qemuSnapshotClearRevertMemoryData(qemuSnapshotRevertMemoryData *memdata) +{ + VIR_FORCE_CLOSE(memdata->fd); + virQEMUSaveDataFree(memdata->data); +} +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(qemuSnapshotRevertMemoryData, qemuSnapsho= tClearRevertMemoryData); + + /** * qemuSnapshotRevertExternalPrepare: * @vm: domain object @@ -2011,15 +2027,13 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, * @snap: snapshot object we are reverting to * @config: live domain definition * @inactiveConfig: offline domain definition - * memsnapFD: pointer to store memory state file FD or NULL - * memsnapPath: pointer to store memory state file path or NULL + * @memdata: struct with data to load memory state * * Prepare new temporary snapshot definition @tmpsnapdef that will * be used while creating new overlay files after reverting to snapshot * @snap. In case we are reverting to snapshot with memory state it will - * open it and pass FD via @memsnapFD and path to the file via - * @memsnapPath, caller is responsible for freeing both @memsnapFD and - * memsnapPath. + * open it and store necessary data in @memdata. Caller is responsible + * to clear the data by using qemuSnapshotClearRevertMemoryData(). * * Returns 0 in success, -1 on error. */ @@ -2029,8 +2043,7 @@ qemuSnapshotRevertExternalPrepare(virDomainObj *vm, virDomainMomentObj *snap, virDomainDef *config, virDomainDef *inactiveConfig, - int *memsnapFD, - char **memsnapPath) + qemuSnapshotRevertMemoryData *memdata) { size_t i; bool active =3D virDomainObjIsActive(vm); @@ -2065,12 +2078,20 @@ qemuSnapshotRevertExternalPrepare(virDomainObj *vm, return -1; } =20 - if (memsnapFD && memsnapPath && snapdef->memorysnapshotfile) { + if (memdata && snapdef->memorysnapshotfile) { virQEMUDriver *driver =3D ((qemuDomainObjPrivate *) vm->privateDat= a)->driver; - g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driv= er); + g_autoptr(virDomainDef) savedef =3D NULL; =20 - *memsnapPath =3D snapdef->memorysnapshotfile; - *memsnapFD =3D qemuDomainOpenFile(cfg, NULL, *memsnapPath, O_RDONL= Y, NULL); + memdata->path =3D snapdef->memorysnapshotfile; + memdata->fd =3D qemuSaveImageOpen(driver, NULL, memdata->path, + &savedef, &memdata->data, + false, NULL, false, false); + + if (memdata->fd < 0) + return -1; + + if (!virDomainDefCheckABIStability(savedef, domdef, driver->xmlop= t)) + return -1; } =20 return 0; @@ -2254,13 +2275,16 @@ qemuSnapshotRevertActive(virDomainObj *vm, virObjectEvent *event =3D NULL; virObjectEvent *event2 =3D NULL; virDomainMomentObj *loadSnap =3D NULL; - VIR_AUTOCLOSE memsnapFD =3D -1; - char *memsnapPath =3D NULL; int detail; bool defined =3D false; qemuDomainSaveCookie *cookie =3D (qemuDomainSaveCookie *) snapdef->coo= kie; int rc; g_autoptr(virDomainSnapshotDef) tmpsnapdef =3D NULL; + g_auto(qemuSnapshotRevertMemoryData) memdata =3D { -1, NULL, NULL }; + g_autoptr(virCommand) cmd =3D NULL; + g_autofree char *errbuf =3D NULL; + VIR_AUTOCLOSE intermediatefd =3D -1; + int memrc =3D 0; =20 start_flags |=3D VIR_QEMU_PROCESS_START_PAUSED; =20 @@ -2284,7 +2308,7 @@ qemuSnapshotRevertActive(virDomainObj *vm, =20 if (qemuSnapshotRevertExternalPrepare(vm, tmpsnapdef, snap, *config, *inactiveConfig, - &memsnapFD, &memsnapPath) < = 0) { + &memdata) < 0) { return -1; } } else { @@ -2298,6 +2322,30 @@ qemuSnapshotRevertActive(virDomainObj *vm, =20 virDomainObjAssignDef(vm, config, true, NULL); =20 + if (virDomainSnapshotIsExternal(snap) && memdata.data) { + virQEMUSaveHeader *header =3D &memdata.data->header; + + if (header && (header->version =3D=3D 2) && + (header->compressed !=3D QEMU_SAVE_FORMAT_RAW)) { + if (!(cmd =3D qemuSaveImageGetCompressionCommand(header->compr= essed))) + return -1; + + intermediatefd =3D memdata.fd; + memdata.fd =3D -1; + + virCommandSetInputFD(cmd, intermediatefd); + virCommandSetOutputFD(cmd, &memdata.fd); + virCommandSetErrorBuffer(cmd, &errbuf); + virCommandDoAsyncIO(cmd); + + if (virCommandRunAsync(cmd, NULL) < 0) { + memdata.fd =3D intermediatefd; + intermediatefd =3D -1; + return -1; + } + } + } + /* No cookie means libvirt which saved the domain was too old to * mess up the CPU definitions. */ @@ -2307,17 +2355,48 @@ qemuSnapshotRevertActive(virDomainObj *vm, =20 rc =3D qemuProcessStart(snapshot->domain->conn, driver, vm, cookie ? cookie->cpu : NULL, - VIR_ASYNC_JOB_SNAPSHOT, NULL, memsnapFD, - memsnapPath, loadSnap, + VIR_ASYNC_JOB_SNAPSHOT, "stdio", + memdata.fd, memdata.path, loadSnap, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); + + if (virDomainSnapshotIsExternal(snap) && memdata.data) { + if (intermediatefd !=3D -1) { + virErrorPtr orig_err =3D NULL; + + if (rc < 0) { + /* if there was an error setting up qemu, the intermediate + * process will wait forever to write to stdout, so we + * must manually kill it and ignore any error related to + * the process + */ + virErrorPreserveLast(&orig_err); + VIR_FORCE_CLOSE(intermediatefd); + VIR_FORCE_CLOSE(memdata.fd); + } + + memrc =3D virCommandWait(cmd, NULL); + VIR_DEBUG("Decompression binary stderr: %s", NULLSTR(errbuf)); + virErrorRestore(&orig_err); + } + if (VIR_CLOSE(memdata.fd) < 0) { + virReportSystemError(errno, _("cannot close file: %1$s"), memd= ata.path); + memrc =3D -1; + } + + /* qemuProcessStart doesn't unset the qemu error reporting infrast= ructure + * in case of migration (which is used in this case) so we need to= reset it + * so that the handle to virtlogd is not held open unnecessarily */ + qemuMonitorSetDomainLog(qemuDomainGetMonitor(vm), NULL, NULL, NULL= ); + } + virDomainAuditStart(vm, "from-snapshot", rc >=3D 0); detail =3D VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT; event =3D virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, detail); virObjectEventStateQueue(driver->domainEventState, event); - if (rc < 0) + if (rc < 0 || memrc < 0) return -1; =20 =20 @@ -2428,7 +2507,7 @@ qemuSnapshotRevertInactive(virDomainObj *vm, =20 if (qemuSnapshotRevertExternalPrepare(vm, tmpsnapdef, snap, NULL, *inactiveConfig, - NULL, NULL) < 0) { + NULL) < 0) { return -1; } =20 --=20 2.41.0