[libvirt] [PATCH v5 1/4] blockcopy: add more XML for state tracking
Peter Krempa
pkrempa at redhat.com
Tue Jul 29 11:20:28 UTC 2014
On 07/29/14 06:20, Eric Blake wrote:
> Doing a blockcopy operation across a libvirtd restart is not very
> robust at the moment. In particular, we are clearing the <mirror>
> element prior to telling qemu to finish the job; and thanks to the
> ability to request async completion, the user can easily regain
> control prior to qemu actually finishing the effort, and they should
> be able to poll the domain XML to see if the job is still going.
>
> A future patch will fix things to actually wait until qemu is done
> before modifying the XML to reflect the job completion. But since
> qemu issues identical BLOCK_JOB_COMPLETE events regardless of whether
> the job was cancelled (kept the original disk) or completed (pivoted
> to the new disk), we have to track which of the two operations were
> used to end the job. Furthermore, we'd like to avoid attempts to
> end a job where we are already waiting on an earlier request to qemu
> to end the job. Likewise, if we miss the qemu event (perhaps because
> it arrived during a libvirtd restart), we still need enough state
> recorded to be able to determine how to modify the domain XML once
> we reconnect to qemu and manually learn whether the job still exists.
>
> Although this patch doesn't actually fix the problem, it is a
> preliminary step that makes it possible to track whether a job
> has already begun steps towards completion.
>
> * src/conf/domain_conf.h (virDomainDiskMirror): New enum.
> (_virDomainDiskDef): Convert bool mirroring to new enum.
> * src/conf/domain_conf.c (virDomainDiskDefParseXML)
> (virDomainDiskDefFormat): Handle new values.
> * src/qemu/qemu_process.c (qemuProcessHandleBlockJob): Adjust
> client.
> * src/qemu/qemu_driver.c (qemuDomainBlockPivot)
> (qemuDomainBlockJobImpl): Likewise.
> * docs/schemas/domaincommon.rng (diskMirror): Expose new values.
> * docs/formatdomain.html.in (elementsDisks): Document it.
> * tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml: Test it.
>
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
> docs/formatdomain.html.in | 10 +++++++---
> docs/schemas/domaincommon.rng | 6 +++++-
> src/conf/domain_conf.c | 23 +++++++++++++++++++---
> src/conf/domain_conf.h | 13 +++++++++++-
> src/qemu/qemu_driver.c | 12 +++++------
> src/qemu/qemu_process.c | 4 ++--
> .../qemuxml2argvdata/qemuxml2argv-disk-mirror.xml | 4 ++++
> 7 files changed, 56 insertions(+), 16 deletions(-)
>
> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
> index f6f697c..389c8c9 100644
> --- a/docs/schemas/domaincommon.rng
> +++ b/docs/schemas/domaincommon.rng
> @@ -4284,7 +4284,11 @@
> </choice>
> <optional>
> <attribute name='ready'>
> - <value>yes</value>
> + <choice>
> + <value>yes</value>
> + <value>abort</value>
Seems reasonable to store the state of the final step here.
> + <value>pivot</value>
> + </choice>
> </attribute>
> </optional>
> </element>
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index 910f6e2..1c8f8cf 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -747,6 +747,12 @@ VIR_ENUM_IMPL(virDomainDiskDiscard, VIR_DOMAIN_DISK_DISCARD_LAST,
> "unmap",
> "ignore")
>
> +VIR_ENUM_IMPL(virDomainDiskMirror, VIR_DOMAIN_DISK_MIRROR_LAST,
> + "default",
> + "yes",
> + "abort",
> + "pivot")
> +
> #define VIR_DOMAIN_XML_WRITE_FLAGS VIR_DOMAIN_XML_SECURE
> #define VIR_DOMAIN_XML_READ_FLAGS VIR_DOMAIN_XML_INACTIVE
>
> @@ -5482,7 +5488,14 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
> }
> ready = virXMLPropString(cur, "ready");
> if (ready) {
> - def->mirroring = true;
> + def->mirrorState = virDomainDiskMirrorTypeFromString(ready);
> + if (def->mirrorState < 0) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("unknown mirror ready state %s"),
> + ready);
> + VIR_FREE(ready);
> + goto error;
> + }
> VIR_FREE(ready);
> }
> } else if (xmlStrEqual(cur->name, BAD_CAST "auth")) {
> @@ -15273,8 +15286,12 @@ virDomainDiskDefFormat(virBufferPtr buf,
> virBufferEscapeString(buf, " file='%s'", def->mirror->path);
> virBufferEscapeString(buf, " format='%s'", formatStr);
> }
> - if (def->mirroring)
> - virBufferAddLit(buf, " ready='yes'");
> + if (def->mirrorState) {
> + const char *mirror;
> +
> + mirror = virDomainDiskMirrorTypeToString(def->mirrorState);
> + virBufferEscapeString(buf, " ready='%s'", mirror);
> + }
> virBufferAddLit(buf, ">\n");
> virBufferAdjustIndent(buf, 2);
> virBufferEscapeString(buf, "<format type='%s'/>\n", formatStr);
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index b988b17..bc8966d 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -610,6 +610,16 @@ struct _virDomainBlockIoTuneInfo {
> typedef virDomainBlockIoTuneInfo *virDomainBlockIoTuneInfoPtr;
>
>
> +typedef enum {
> + VIR_DOMAIN_DISK_MIRROR_DEFAULT = 0, /* No job, or job still not synced */
I'd go with _NONE here and ...
> + VIR_DOMAIN_DISK_MIRROR_READY, /* Job in second phase */
> + VIR_DOMAIN_DISK_MIRROR_ABORT, /* Job aborted, waiting for event */
> + VIR_DOMAIN_DISK_MIRROR_PIVOT, /* Job pivoted, waiting for event */
> +
> + VIR_DOMAIN_DISK_MIRROR_LAST
> +} virDomainDiskMirror;
... as this describes the ready state of the mirroring operation I'd go
with virDomainDiskMirrorState and rename those fields accordingly.
Without that it's not clear what the enum describes.
> +
> +
> /* Stores the virtual disk configuration */
> struct _virDomainDiskDef {
> virStorageSourcePtr src; /* non-NULL. XXX Allow NULL for empty cdrom? */
> @@ -621,7 +631,7 @@ struct _virDomainDiskDef {
> int removable; /* enum virTristateSwitch */
>
> virStorageSourcePtr mirror;
> - bool mirroring;
> + int mirrorState; /* enum virDomainDiskMirror */
>
> struct {
> unsigned int cylinders;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 704ba39..acc9ef0 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -14846,7 +14846,7 @@ qemuDomainBlockPivot(virConnectPtr conn,
> format = virStorageFileFormatTypeToString(disk->mirror->format);
>
> /* Probe the status, if needed. */
> - if (!disk->mirroring) {
> + if (!disk->mirrorState) {
> qemuDomainObjEnterMonitor(driver, vm);
> rc = qemuMonitorBlockJob(priv->mon, device, NULL, NULL, 0, &info,
> BLOCK_JOB_INFO, true);
> @@ -14860,10 +14860,10 @@ qemuDomainBlockPivot(virConnectPtr conn,
> }
> if (rc == 1 && info.cur == info.end &&
> info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY)
> - disk->mirroring = true;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_READY;
> }
>
> - if (!disk->mirroring) {
> + if (disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_READY) {
> virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
> _("disk '%s' not ready for pivot yet"),
> disk->dst);
> @@ -14939,7 +14939,7 @@ qemuDomainBlockPivot(virConnectPtr conn,
> }
>
> disk->mirror = NULL;
> - disk->mirroring = false;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_DEFAULT;
>
> cleanup:
> /* revert to original disk def on failure */
> @@ -15096,7 +15096,7 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
> * avoid checking if pivot is safe. */
> if (mode == BLOCK_JOB_INFO && ret == 1 && disk->mirror &&
> info->cur == info->end && info->type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY)
> - disk->mirroring = true;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_READY;
>
> /* A successful block job cancelation stops any mirroring. */
> if (mode == BLOCK_JOB_ABORT && disk->mirror) {
> @@ -15104,7 +15104,7 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
> * the mirror, and audit that fact, before dropping things. */
> virStorageSourceFree(disk->mirror);
> disk->mirror = NULL;
> - disk->mirroring = false;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_DEFAULT;
> }
>
> waitjob:
> diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> index 3d10732..73ad27d 100644
> --- a/src/qemu/qemu_process.c
> +++ b/src/qemu/qemu_process.c
> @@ -1040,11 +1040,11 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> qemuDomainDetermineDiskChain(driver, vm, disk, true);
> if (disk->mirror && type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) {
> if (status == VIR_DOMAIN_BLOCK_JOB_READY) {
> - disk->mirroring = true;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_READY;
> } else if (status == VIR_DOMAIN_BLOCK_JOB_FAILED) {
> virStorageSourceFree(disk->mirror);
> disk->mirror = NULL;
> - disk->mirroring = false;
> + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_DEFAULT;
> }
> }
> }
> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
> index 72b03c9..fa7af31 100644
> --- a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
> +++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
> @@ -42,6 +42,10 @@
> <disk type='file' device='disk'>
> <source file='/tmp/logs.img'/>
> <backingStore/>
> + <mirror type='file' file='/tmp/logcopy.img' format='qcow2' ready='abort'>
> + <format type='qcow2'/>
> + <source file='/tmp/logcopy.img'/>
> + </mirror>
> <target dev='vdb' bus='virtio'/>
> </disk>
> <controller type='usb' index='0'/>
>
Otherwise looks good. ACK if you change the enum name and default value
name.
Peter
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 884 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20140729/b9508b02/attachment-0001.sig>
More information about the libvir-list
mailing list