From nobody Sun Feb 8 12:39:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 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=1566438212; cv=none; d=zoho.com; s=zohoarc; b=OpxORvjQA16o5Pc80x2umI4jCdodGcREfS7xWubqgIJlnIf82V8xUXY5/M6x+LUabKZ+vnsxYVPd5g6aUOZLRm47YiY00q0yZNBCiFDxgZFzyHAvdU/1zfuDuvHNJtY39wVg+7nUp5QSJwE2pjG8uLhvOSW2KEbPG3tvdlS8M0w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1566438212; h=Content-Type:Content-Transfer-Encoding:Cc: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:ARC-Authentication-Results; bh=R5/1lAMTNukvNk5IHad8UA9U6o+GFQYkbvRGQ5j4wsI=; b=NJrvxWs8lIEY0XsvRSofZTYJRsh9AxLZl1sGKqCuSIxu7BfBSHA5i3pH/5r6FdUO0wMNL24+80A4SUUOgmZk/DAHtSMo0McX4EjUtdDlhPIlFyICnHtThliCSPEASfIES9w35QZH51gM30kbT/zviZGG0jaHH5+uA+PROPoVMOo= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1566438212462122.64997810234718; Wed, 21 Aug 2019 18:43:32 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 306374E926; Thu, 22 Aug 2019 01:43:31 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 072E81B465; Thu, 22 Aug 2019 01:43:31 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id BF35C24F35; Thu, 22 Aug 2019 01:43:30 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x7M1h1WG000768 for ; Wed, 21 Aug 2019 21:43:01 -0400 Received: by smtp.corp.redhat.com (Postfix) id DC08B1001B35; Thu, 22 Aug 2019 01:43:01 +0000 (UTC) Received: from blue.redhat.com (ovpn-116-234.phx2.redhat.com [10.3.116.234]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7F7CF1001B28; Thu, 22 Aug 2019 01:43:01 +0000 (UTC) From: Eric Blake To: libvir-list@redhat.com Date: Wed, 21 Aug 2019 20:42:49 -0500 Message-Id: <20190822014249.8325-11-eblake@redhat.com> In-Reply-To: <20190822014249.8325-1-eblake@redhat.com> References: <20190822014249.8325-1-eblake@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-loop: libvir-list@redhat.com Cc: nsoffer@redhat.com, eshenitz@redhat.com, pkrempa@redhat.com Subject: [libvirt] [PATCH v10 10/10] backup: Implement qemu incremental pull backup X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.38]); Thu, 22 Aug 2019 01:43:31 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Complete wiring up incremental backup, by adding in support for creating a checkpoint at the same time as a backup (make the transaction have a few more steps) as well as exposing the dirty bitmap for a prior backup over NBD (requires creating a temporary bitmap, merging all appropriate bitmaps in, then exposing that bitmap over NBD). Signed-off-by: Eric Blake --- src/qemu/qemu_driver.c | 193 +++++++++++++++++++++++++++++++++++------ 1 file changed, 165 insertions(+), 28 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d6f7a49ab4..9dd0bdb999 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17054,6 +17054,24 @@ qemuDomainCheckpointPrepare(virQEMUDriverPtr drive= r, virCapsPtr caps, if (disk->type !=3D VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) continue; + /* We want to name temporary bitmap after disk name during + * incremental backup, which is not possible if that is a + * persistent bitmap name. We can also make life easier by + * enforcing bitmap names match checkpoint name, although this + * is not technically necessary. */ + if (STREQ(disk->name, disk->bitmap)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("checkpoint for disk %s must have distinct bi= tmap name"), + disk->name); + goto cleanup; + } + if (STRNEQ(disk->bitmap, def->parent.name)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk %s bitmap should match checkpoint name = %s"), + disk->name, def->parent.name); + goto cleanup; + } + if (vm->def->disks[i]->src->format !=3D VIR_STORAGE_FILE_QCOW2) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("checkpoint for disk %s unsupported " @@ -17594,19 +17612,44 @@ qemuDomainCheckpointDelete(virDomainCheckpointPtr= checkpoint, static int qemuDomainBackupPrepare(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainBackupDefPtr def) + virDomainBackupDefPtr def, + virDomainMomentObjPtr chk) { int ret =3D -1; size_t i; + virDomainCheckpointDefPtr chkdef; + chkdef =3D chk ? virDomainCheckpointObjGetDef(chk) : NULL; + if (chk && def->ndisks !=3D chkdef->ndisks) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("inconsistency between backup and checkpoint disk= s")); + goto cleanup; + } if (qemuBlockNodeNamesDetect(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) goto cleanup; for (i =3D 0; i < def->ndisks; i++) { virDomainBackupDiskDef *disk =3D &def->disks[i]; virStorageSourcePtr src =3D vm->def->disks[disk->idx]->src; - if (!disk->store) + /* For now, insist that atomic checkpoint affect same disks as + * those being backed up. */ + if (!disk->store) { + if (chk && + chkdef->disks[i].type !=3D VIR_DOMAIN_CHECKPOINT_TYPE_NONE= ) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("disk %s requested checkpoint without bac= kup"), + disk->name); + goto cleanup; + } continue; + } + if (chk && + chkdef->disks[i].type !=3D VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("disk %s requested backup without checkpoint"= ), + disk->name); + goto cleanup; + } if (virAsprintf(&disk->store->nodeformat, "tmp-%s", disk->name) < = 0) goto cleanup; if (!disk->store->format) @@ -17640,7 +17683,7 @@ qemuDomainBackupDiskCleanup(virQEMUDriverPtr driver= , virDomainObjPtr vm, * shortly be calling nbd-server-stop. */ } if (incremental && disk->state >=3D VIR_DOMAIN_BACKUP_DISK_STATE_BITMA= P && - qemuMonitorDeleteBitmap(priv->mon, node, disk->store->nodeformat) = < 0) { + qemuMonitorDeleteBitmap(priv->mon, node, disk->name) < 0) { VIR_WARN("Unable to remove temp bitmap for disk %s after backup", disk->name); ret =3D -1; @@ -17678,28 +17721,22 @@ qemuDomainBackupBegin(virDomainPtr domain, const = char *diskXml, bool job_started =3D false; bool nbd_running =3D false; bool push; + const char *mode; size_t i; struct timeval tv; char *suffix =3D NULL; virCommandPtr cmd =3D NULL; const char *qemuImgPath; + virDomainMomentObjPtr chk =3D NULL; + virDomainMomentObjPtr other =3D NULL; + virDomainMomentObjPtr parent =3D NULL; + virDomainMomentObjPtr current; + virJSONValuePtr arr =3D NULL; + VIR_AUTOUNREF(virDomainCheckpointDefPtr) chkdef =3D NULL; virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA, -1); /* TODO: VIR_DOMAIN_BACKUP_BEGIN_QUIESCE */ - // FIXME: Support non-null checkpointXML for incremental - what - // code can be shared with CheckpointCreateXML, then add to transaction - // to create new checkpoint at same time as starting blockdev-backup - if (checkpointXml) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("cannot create incremental backups yet")); - return -1; - } - // if (chk) VIR_STRDUP(suffix, chk->name); - gettimeofday(&tv, NULL); - if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0) - goto cleanup; - if (!(vm =3D qemuDomObjFromDomain(domain))) goto cleanup; @@ -17726,6 +17763,18 @@ qemuDomainBackupBegin(virDomainPtr domain, const c= har *diskXml, if (!(def =3D virDomainBackupDefParseString(diskXml, driver->xmlopt, 0= ))) goto cleanup; + if (checkpointXml) { + if (!(chkdef =3D virDomainCheckpointDefParseString(checkpointXml, = caps, + driver->xmlopt, + priv->qemuCaps, 0= )) || + VIR_STRDUP(suffix, chkdef->parent.name) < 0) + goto cleanup; + } else { + gettimeofday(&tv, NULL); + if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0) + goto cleanup; + } + push =3D def->type =3D=3D VIR_DOMAIN_BACKUP_TYPE_PUSH; if (!push) { if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NBD_BITMAP)) { @@ -17763,15 +17812,25 @@ qemuDomainBackupBegin(virDomainPtr domain, const = char *diskXml, goto cleanup; } } + current =3D virDomainCheckpointGetCurrent(vm->checkpoints); if (def->incremental) { if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("qemu binary lacks persistent bitmaps support= ")); goto cleanup; } - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", - _("cannot create incremental backups yet")); - goto cleanup; + for (other =3D current; other; + other =3D other->def->parent_name ? + virDomainCheckpointFindByName(vm->checkpoints, + other->def->parent_name) : = NULL) + if (STREQ(other->def->name, def->incremental)) + break; + if (!other) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("could not locate checkpoint '%s' for increme= ntal backup"), + def->incremental); + goto cleanup; + } } if (!(qemuImgPath =3D qemuFindQemuImgBinary(driver))) @@ -17787,14 +17846,38 @@ qemuDomainBackupBegin(virDomainPtr domain, const = char *diskXml, goto endjob; } + if (chkdef) { + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BITMAP_MERGE)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("qemu binary lacks persistent bitmaps support= ")); + goto endjob; + } + + if (qemuDomainCheckpointPrepare(driver, caps, vm, chkdef) < 0) + goto endjob; + if (!(chk =3D virDomainCheckpointAssignDef(vm->checkpoints, chkdef= ))) + goto endjob; + chkdef =3D NULL; + if (current) { + parent =3D current; + if (VIR_STRDUP(chk->def->parent_name, parent->def->name) < 0) + goto endjob; + if (qemuDomainCheckpointWriteMetadata(vm, parent, driver->caps, + driver->xmlopt, + cfg->checkpointDir) < 0) + goto endjob; + } + } + if (virDomainBackupAlignDisks(def, vm->def, suffix) < 0 || - qemuDomainBackupPrepare(driver, vm, def) < 0) + qemuDomainBackupPrepare(driver, vm, def, chk) < 0) goto endjob; /* actually start the checkpoint. 2x2 array of push/pull, full/incr, plus additional tweak if checkpoint requested */ qemuDomainObjEnterMonitor(driver, vm); - /* - push/pull: blockdev-add per */ + /* - push/pull: blockdev-add per + - incr: bitmap-add of tmp, bitmap-merge per */ for (i =3D 0; i < def->ndisks; i++) { virDomainBackupDiskDef *disk =3D &def->disks[i]; virJSONValuePtr file; @@ -17860,11 +17943,32 @@ qemuDomainBackupBegin(virDomainPtr domain, const = char *diskXml, goto endmon; json =3D NULL; disk->state =3D VIR_DOMAIN_BACKUP_DISK_STATE_READY; + + if (def->incremental) { + if (!(arr =3D virJSONValueNewArray())) + goto endmon; + if (qemuMonitorAddBitmap(priv->mon, node, disk->name, false) <= 0) { + virJSONValueFree(arr); + goto endmon; + } + disk->state =3D VIR_DOMAIN_BACKUP_DISK_STATE_BITMAP; + for (other =3D parent ? parent : current; other; + other =3D other->def->parent_name ? + virDomainCheckpointFindByName(vm->checkpoints, + other->def->parent_name= ) : NULL) { + if (virJSONValueArrayAppendString(arr, other->def->name) <= 0) { + virJSONValueFree(arr); + goto endmon; + } + if (STREQ(other->def->name, def->incremental)) + break; + } + if (qemuMonitorMergeBitmaps(priv->mon, node, disk->name, &arr)= < 0) + goto endmon; + } } - /* TODO: - - incr: bitmap-add of tmp, bitmap-merge per - - transaction, containing: + /* - transaction, containing: - push+full: blockdev-backup sync:full - push+incr: blockdev-backup sync:incremental bitmap:tmp - pull+full: blockdev-backup sync:none @@ -17873,24 +17977,56 @@ qemuDomainBackupBegin(virDomainPtr domain, const = char *diskXml, */ if (!(json =3D virJSONValueNewArray())) goto endmon; + if (push) + mode =3D def->incremental ? "incremental" : "full"; + else + mode =3D "none"; for (i =3D 0; i < def->ndisks; i++) { virDomainBackupDiskDef *disk =3D &def->disks[i]; - virStorageSourcePtr src =3D vm->def->disks[disk->idx]->src; + const char *node; + const char *push_bitmap =3D NULL; if (!disk->store) continue; + node =3D qemuDomainDiskNodeFormatLookup(vm, disk->name); + if (push && def->incremental) + push_bitmap =3D disk->name; if (qemuMonitorJSONTransactionAdd(json, "blockdev-backup", - "s:device", src->nodeformat, + "s:device", node, "s:target", disk->store->nodefor= mat, - "s:sync", push ? "full" : "none", + "s:sync", mode, + "S:bitmap", push_bitmap, "s:job-id", disk->name, NULL) < 0) goto endmon; + if (def->incremental && !push && + qemuMonitorJSONTransactionAdd(json, + "block-dirty-bitmap-disable", + "s:node", node, + "s:name", disk->name, + NULL) < 0) + goto endmon; } + if (chk && qemuDomainCheckpointAddActions(vm, json, parent, + virDomainCheckpointObjGetDef= (chk)) < 0) + goto endmon; if (qemuMonitorTransaction(priv->mon, &json) < 0) goto endmon; job_started =3D true; + if (chk) { + virDomainCheckpointSetCurrent(vm->checkpoints, chk); + if (qemuDomainCheckpointWriteMetadata(vm, chk, driver->caps, + driver->xmlopt, + cfg->checkpointDir) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to save metadata for checkpoint %s"), + chk->def->name); + virDomainCheckpointObjListRemove(vm->checkpoints, chk); + goto endmon; + } + virDomainCheckpointLinkParent(vm->checkpoints, chk); + } /* - pull: nbd-server-start with from user (or autogenerate s= erver) @@ -17932,7 +18068,7 @@ qemuDomainBackupBegin(virDomainPtr domain, const ch= ar *diskXml, if (job_started && qemuMonitorBlockJobCancel(priv->mon, disk->name) < 0) VIR_WARN("Unable to stop backup job %s on vm %s after fail= ure", - disk->store->nodeformat, vm->def->name); + disk->name, vm->def->name); qemuDomainBackupDiskCleanup(driver, vm, disk, push, !!def->incremental, false); } @@ -17955,6 +18091,7 @@ qemuDomainBackupBegin(virDomainPtr domain, const ch= ar *diskXml, qemuDomainObjEndJob(driver, vm); cleanup: + virJSONValueFree(arr); virCommandFree(cmd); VIR_FREE(suffix); virJSONValueFree(json); --=20 2.21.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list