提交 ce7229a3 编写于 作者: P Peter Krempa

qemu: Add blockdev support for the block copy job

Implement job handling for the block copy job (drive/blockdev-mirror)
when using -blockdev. In contrast to the previously implemented
blockjobs the block copy job introduces new images to the running qemu
instance, thus requires a bit more handling.

When copying to new images the code now makes use of blockdev-create to
format the images explicitly rather than depending on automagic qemu
behaviour.
Signed-off-by: NPeter Krempa <pkrempa@redhat.com>
Reviewed-by: NJán Tomko <jtomko@redhat.com>
上级 545edb25
...@@ -309,6 +309,40 @@ qemuBlockJobNewCreate(virDomainObjPtr vm, ...@@ -309,6 +309,40 @@ qemuBlockJobNewCreate(virDomainObjPtr vm,
} }
qemuBlockJobDataPtr
qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
virDomainDiskDefPtr disk,
virStorageSourcePtr mirror,
bool shallow,
bool reuse)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
VIR_AUTOUNREF(qemuBlockJobDataPtr) job = NULL;
VIR_AUTOFREE(char *) jobname = NULL;
if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) {
if (virAsprintf(&jobname, "copy-%s-%s", disk->dst, disk->src->nodeformat) < 0)
return NULL;
} else {
if (!(jobname = qemuAliasDiskDriveFromDisk(disk)))
return NULL;
}
if (!(job = qemuBlockJobDataNew(QEMU_BLOCKJOB_TYPE_COPY, jobname)))
return NULL;
job->mirrorChain = virObjectRef(mirror);
if (shallow && !reuse)
job->data.copy.shallownew = true;
if (qemuBlockJobRegister(job, vm, disk, true) < 0)
return NULL;
VIR_RETURN_PTR(job);
}
/** /**
* qemuBlockJobDiskGetJob: * qemuBlockJobDiskGetJob:
* @disk: disk definition * @disk: disk definition
...@@ -1043,6 +1077,50 @@ qemuBlockJobProcessEventCompletedActiveCommit(virQEMUDriverPtr driver, ...@@ -1043,6 +1077,50 @@ qemuBlockJobProcessEventCompletedActiveCommit(virQEMUDriverPtr driver,
} }
static void
qemuBlockJobProcessEventConcludedCopyPivot(virQEMUDriverPtr driver,
virDomainObjPtr vm,
qemuBlockJobDataPtr job,
qemuDomainAsyncJob asyncJob)
{
VIR_DEBUG("copy job '%s' on VM '%s' pivoted", job->name, vm->def->name);
if (!job->disk)
return;
/* for shallow copy without reusing external image the user can either not
* specify the backing chain in which case libvirt will open and use the
* chain the user provided or not specify a chain in which case we'll
* inherit the rest of the chain */
if (job->data.copy.shallownew &&
!virStorageSourceIsBacking(job->disk->mirror->backingStore))
VIR_STEAL_PTR(job->disk->mirror->backingStore, job->disk->src->backingStore);
qemuBlockJobRewriteConfigDiskSource(vm, job->disk, job->disk->mirror);
qemuBlockJobEventProcessConcludedRemoveChain(driver, vm, asyncJob, job->disk->src);
virObjectUnref(job->disk->src);
VIR_STEAL_PTR(job->disk->src, job->disk->mirror);
}
static void
qemuBlockJobProcessEventConcludedCopyAbort(virQEMUDriverPtr driver,
virDomainObjPtr vm,
qemuBlockJobDataPtr job,
qemuDomainAsyncJob asyncJob)
{
VIR_DEBUG("copy job '%s' on VM '%s' aborted", job->name, vm->def->name);
if (!job->disk)
return;
qemuBlockJobEventProcessConcludedRemoveChain(driver, vm, asyncJob, job->disk->mirror);
virObjectUnref(job->disk->mirror);
job->disk->mirror = NULL;
}
static void static void
qemuBlockJobProcessEventConcludedCreate(virQEMUDriverPtr driver, qemuBlockJobProcessEventConcludedCreate(virQEMUDriverPtr driver,
virDomainObjPtr vm, virDomainObjPtr vm,
...@@ -1111,6 +1189,12 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job, ...@@ -1111,6 +1189,12 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job,
break; break;
case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_COPY:
if (job->state == QEMU_BLOCKJOB_STATE_PIVOTING)
qemuBlockJobProcessEventConcludedCopyPivot(driver, vm, job, asyncJob);
else
qemuBlockJobProcessEventConcludedCopyAbort(driver, vm, job, asyncJob);
break;
case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_NONE:
case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_INTERNAL:
case QEMU_BLOCKJOB_TYPE_LAST: case QEMU_BLOCKJOB_TYPE_LAST:
...@@ -1138,6 +1222,9 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job, ...@@ -1138,6 +1222,9 @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobDataPtr job,
break; break;
case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_COPY:
qemuBlockJobProcessEventConcludedCopyAbort(driver, vm, job, asyncJob);
break;
case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_NONE:
case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_INTERNAL:
case QEMU_BLOCKJOB_TYPE_LAST: case QEMU_BLOCKJOB_TYPE_LAST:
......
...@@ -97,6 +97,14 @@ struct _qemuBlockJobCreateData { ...@@ -97,6 +97,14 @@ struct _qemuBlockJobCreateData {
}; };
typedef struct _qemuBlockJobCopyData qemuBlockJobCopyData;
typedef qemuBlockJobCopyData *qemuBlockJobDataCopyPtr;
struct _qemuBlockJobCopyData {
bool shallownew;
};
typedef struct _qemuBlockJobData qemuBlockJobData; typedef struct _qemuBlockJobData qemuBlockJobData;
typedef qemuBlockJobData *qemuBlockJobDataPtr; typedef qemuBlockJobData *qemuBlockJobDataPtr;
...@@ -113,6 +121,7 @@ struct _qemuBlockJobData { ...@@ -113,6 +121,7 @@ struct _qemuBlockJobData {
qemuBlockJobPullData pull; qemuBlockJobPullData pull;
qemuBlockJobCommitData commit; qemuBlockJobCommitData commit;
qemuBlockJobCreateData create; qemuBlockJobCreateData create;
qemuBlockJobCopyData copy;
} data; } data;
int type; /* qemuBlockJobType */ int type; /* qemuBlockJobType */
...@@ -163,6 +172,13 @@ qemuBlockJobNewCreate(virDomainObjPtr vm, ...@@ -163,6 +172,13 @@ qemuBlockJobNewCreate(virDomainObjPtr vm,
virStorageSourcePtr chain, virStorageSourcePtr chain,
bool storage); bool storage);
qemuBlockJobDataPtr
qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
virDomainDiskDefPtr disk,
virStorageSourcePtr mirror,
bool shallow,
bool reuse);
qemuBlockJobDataPtr qemuBlockJobDataPtr
qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk)
ATTRIBUTE_NONNULL(1); ATTRIBUTE_NONNULL(1);
......
...@@ -2426,6 +2426,10 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload, ...@@ -2426,6 +2426,10 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
break; break;
case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_COPY:
if (job->data.copy.shallownew)
virBufferAddLit(&attrBuf, " shallownew='yes'");
break;
case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_NONE:
case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_INTERNAL:
case QEMU_BLOCKJOB_TYPE_LAST: case QEMU_BLOCKJOB_TYPE_LAST:
...@@ -2873,6 +2877,7 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job, ...@@ -2873,6 +2877,7 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job,
virDomainXMLOptionPtr xmlopt) virDomainXMLOptionPtr xmlopt)
{ {
VIR_AUTOFREE(char *) createmode = NULL; VIR_AUTOFREE(char *) createmode = NULL;
VIR_AUTOFREE(char *) shallownew = NULL;
xmlNodePtr tmp; xmlNodePtr tmp;
switch ((qemuBlockJobType) job->type) { switch ((qemuBlockJobType) job->type) {
...@@ -2922,6 +2927,14 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job, ...@@ -2922,6 +2927,14 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job,
break; break;
case QEMU_BLOCKJOB_TYPE_COPY: case QEMU_BLOCKJOB_TYPE_COPY:
if ((shallownew = virXPathString("string(./@shallownew)", ctxt))) {
if (STRNEQ(shallownew, "yes"))
goto broken;
job->data.copy.shallownew = true;
}
break;
case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_NONE:
case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_INTERNAL:
case QEMU_BLOCKJOB_TYPE_LAST: case QEMU_BLOCKJOB_TYPE_LAST:
......
...@@ -18351,7 +18351,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18351,7 +18351,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
{ {
virQEMUDriverPtr driver = conn->privateData; virQEMUDriverPtr driver = conn->privateData;
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
VIR_AUTOFREE(char *) device = NULL;
virDomainDiskDefPtr disk = NULL; virDomainDiskDefPtr disk = NULL;
int ret = -1; int ret = -1;
bool need_unlink = false; bool need_unlink = false;
...@@ -18363,6 +18362,11 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18363,6 +18362,11 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
qemuBlockJobDataPtr job = NULL; qemuBlockJobDataPtr job = NULL;
VIR_AUTOUNREF(virStorageSourcePtr) mirror = mirrorsrc; VIR_AUTOUNREF(virStorageSourcePtr) mirror = mirrorsrc;
bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV); bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV);
VIR_AUTOPTR(qemuBlockStorageSourceChainData) data = NULL;
VIR_AUTOPTR(qemuBlockStorageSourceChainData) crdata = NULL;
virStorageSourcePtr n;
virStorageSourcePtr mirrorBacking = NULL;
int rc = 0;
/* Preliminaries: find the disk we are editing, sanity checks */ /* Preliminaries: find the disk we are editing, sanity checks */
virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW | virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW |
...@@ -18392,9 +18396,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18392,9 +18396,6 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
if (!(disk = qemuDomainDiskByName(vm->def, path))) if (!(disk = qemuDomainDiskByName(vm->def, path)))
goto endjob; goto endjob;
if (!(device = qemuAliasDiskDriveFromDisk(disk)))
goto endjob;
if (qemuDomainDiskBlockJobIsActive(disk)) if (qemuDomainDiskBlockJobIsActive(disk))
goto endjob; goto endjob;
...@@ -18469,7 +18470,8 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18469,7 +18470,8 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
goto endjob; goto endjob;
} }
/* pre-create the image file */ /* pre-create the image file. In case when 'blockdev' is used this is
* required so that libvirt can properly label the image for access by qemu */
if (!existing) { if (!existing) {
if (virStorageFileCreate(mirror) < 0) { if (virStorageFileCreate(mirror) < 0) {
virReportSystemError(errno, "%s", _("failed to create copy target")); virReportSystemError(errno, "%s", _("failed to create copy target"));
...@@ -18486,6 +18488,15 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18486,6 +18488,15 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
keepParentLabel) < 0) keepParentLabel) < 0)
goto endjob; goto endjob;
/* we must initialize XML-provided chain prior to detecting to keep semantics
* with VM startup */
if (blockdev) {
for (n = mirror; virStorageSourceIsBacking(n); n = n->backingStore) {
if (qemuDomainPrepareStorageSourceBlockdev(disk, n, priv, cfg) < 0)
goto endjob;
}
}
/* If reusing an external image that includes a backing file but the user /* If reusing an external image that includes a backing file but the user
* did not enumerate the chain in the XML we need to detect the chain */ * did not enumerate the chain in the XML we need to detect the chain */
if (mirror_reuse && if (mirror_reuse &&
...@@ -18497,18 +18508,72 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18497,18 +18508,72 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
if (qemuDomainStorageSourceChainAccessAllow(driver, vm, mirror) < 0) if (qemuDomainStorageSourceChainAccessAllow(driver, vm, mirror) < 0)
goto endjob; goto endjob;
if (!(job = qemuBlockJobDiskNew(vm, disk, QEMU_BLOCKJOB_TYPE_COPY, device))) if (blockdev) {
if (mirror_reuse) {
if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror,
priv->qemuCaps)))
goto endjob;
} else {
if (qemuBlockStorageSourceCreateDetectSize(vm, mirror, disk->src, QEMU_ASYNC_JOB_NONE) < 0)
goto endjob;
if (mirror_shallow) {
/* if external backing store is populated we'll need to open it */
if (virStorageSourceHasBacking(mirror)) {
if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(mirror->backingStore,
priv->qemuCaps)))
goto endjob;
mirrorBacking = mirror->backingStore;
} else {
/* backing store of original image will be reused, but the
* new image must refer to it in the metadata */
mirrorBacking = disk->src->backingStore;
}
}
if (!(crdata = qemuBuildStorageSourceChainAttachPrepareBlockdevTop(mirror,
priv->qemuCaps)))
goto endjob;
}
if (data) {
qemuDomainObjEnterMonitor(driver, vm);
rc = qemuBlockStorageSourceChainAttach(priv->mon, data);
if (qemuDomainObjExitMonitor(driver, vm) < 0)
goto endjob;
if (rc < 0)
goto endjob;
}
if (crdata &&
qemuBlockStorageSourceCreate(vm, mirror, mirrorBacking, mirror->backingStore,
crdata->srcdata[0], QEMU_ASYNC_JOB_NONE) < 0)
goto endjob;
}
if (!(job = qemuBlockJobDiskNewCopy(vm, disk, mirror, mirror_shallow, mirror_reuse)))
goto endjob; goto endjob;
disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
/* Actually start the mirroring */ /* Actually start the mirroring */
qemuDomainObjEnterMonitor(driver, vm); qemuDomainObjEnterMonitor(driver, vm);
/* qemuMonitorDriveMirror needs to honor the REUSE_EXT flag as specified
* by the user */ if (blockdev) {
ret = qemuMonitorDriveMirror(priv->mon, device, mirror->path, format, ret = qemuMonitorBlockdevMirror(priv->mon, job->name, true,
bandwidth, granularity, buf_size, disk->src->nodeformat,
mirror_shallow, mirror_reuse); mirror->nodeformat, bandwidth,
granularity, buf_size, mirror_shallow);
} else {
/* qemuMonitorDriveMirror needs to honor the REUSE_EXT flag as specified
* by the user */
ret = qemuMonitorDriveMirror(priv->mon, job->name, mirror->path, format,
bandwidth, granularity, buf_size,
mirror_shallow, mirror_reuse);
}
virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0); virDomainAuditDisk(vm, NULL, mirror, "mirror", ret >= 0);
if (qemuDomainObjExitMonitor(driver, vm) < 0) if (qemuDomainObjExitMonitor(driver, vm) < 0)
ret = -1; ret = -1;
...@@ -18525,6 +18590,16 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, ...@@ -18525,6 +18590,16 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm,
qemuBlockJobStarted(job, vm); qemuBlockJobStarted(job, vm);
endjob: endjob:
if (rc < 0 &&
virDomainObjIsActive(vm) &&
(data || crdata)) {
qemuDomainObjEnterMonitor(driver, vm);
if (data)
qemuBlockStorageSourceChainDetach(priv->mon, data);
if (crdata)
qemuBlockStorageSourceAttachRollback(priv->mon, crdata->srcdata[0]);
ignore_value(qemuDomainObjExitMonitor(driver, vm));
}
if (need_unlink && virStorageFileUnlink(mirror) < 0) if (need_unlink && virStorageFileUnlink(mirror) < 0)
VIR_WARN("%s", _("unable to remove just-created copy target")); VIR_WARN("%s", _("unable to remove just-created copy target"));
virStorageFileDeinit(mirror); virStorageFileDeinit(mirror);
......
...@@ -260,6 +260,9 @@ ...@@ -260,6 +260,9 @@
</source> </source>
</src> </src>
</blockjob> </blockjob>
<blockjob name='pull-vdc-libvirt-4321-format' type='copy' state='ready' shallownew='yes'>
<disk dst='vdc'/>
</blockjob>
<blockjob name='pull-vdc-libvirt-9-format' type='commit' state='running'> <blockjob name='pull-vdc-libvirt-9-format' type='commit' state='running'>
<disk dst='vdc'/> <disk dst='vdc'/>
<base node='libvirt-11-format'/> <base node='libvirt-11-format'/>
...@@ -571,6 +574,17 @@ ...@@ -571,6 +574,17 @@
</backingStore> </backingStore>
</backingStore> </backingStore>
</backingStore> </backingStore>
<mirror type='file' file='/tmp/copy2' format='qcow2' job='copy' ready='yes'>
<format type='qcow2'/>
<source file='/tmp/copy2' index='4321'>
<privateData>
<nodenames>
<nodename type='storage' name='libvirt-4321-storage'/>
<nodename type='format' name='libvirt-4321-format'/>
</nodenames>
</privateData>
</source>
</mirror>
<target dev='vdc' bus='virtio'/> <target dev='vdc' bus='virtio'/>
<alias name='virtio-disk3'/> <alias name='virtio-disk3'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x0d' function='0x0'/>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册