[libvirt] [PATCH RFC 00/51] refactor block job handling and add support for new event/commands (blockdev-add saga)

This series refactors how libvirt stores block job data and handles block job events in general. This is done in preparation for -blockdev support where we'll need to add support for the 'blockdev-create' job which is not associated to any frontend 'disk' in some cases. This series introduces a new structure and table of blockjobs currently running and implements handlers for the new even "JOB_STATUS_CHANGE". RFC status excuses: - the series needs much more testing than I did - the last few patches are true RFC - I didn't port/refactor the rest of the blockjob finishing code that I have for the individual job types after the big refactor so there aren't any examples for the -blockdev case yet. I'll post the link to a repo containing the patches some time later. Peter Krempa (51): qemu: blockjob: Extract emitting of libvirt events qemu: blockjob: Emit VIR_DOMAIN_EVENT_ID_BLOCK_JOB only for local disks qemu: processBlockJobEvent: Use qemuBlockJobUpdate to process block job events qemu: blockjob: Unexport qemuBlockJobEventProcess qemu: blockjob: Rename public APIs qemu: blockjob: Remove header dependency on qemu_domain.h qemu: Consolidate disk blockjob variables into a structure qemu: process: Consolidate error paths in qemuProcessHandleBlockJob qemu: blockjob: Rename qemuBlockJobEventProcess to qemuBlockJobEventProcessLegacy qemu: blockjob: Split out handling of comlpleted jobs qemu: migration: Properly note that non-shared-storage migration uses a blockjob qemu: process: refresh block jobs on reconnect qemu: driver: Remove block job status reprobing from qemuDomainBlockPivot qemu: migration: Simplify cancellation of migration blockjobs qemu: blockjob: Turn struct qemuBlockJobData into a virObject qemu: blockjob: Clarify that job 'status' field contains new state qemu: migration: Separate startup of disk mirror from migration logic qemu: blockjob: Add functions for block job state control qemu: blockjob: Drop unnecessary calls to qemuBlockJobSyncEndDisk qemu: blockjob: Add reference to disk into struct qemuBlockJobData qemu: blockjob: Record job type when starting the job qemu: blockjob: Pass in job to qemuBlockJobEventProcessLegacy qemu: blockjob: Convert qemuBlockJobSyncBeginDisk to work with the job qemu: blockjob: Track current state of blockjob qemu: migration: Extract reporting of disk migration error qemu: blockjob: Remove error propagation from qemuBlockJobUpdateDisk qemu: blockjob: Consume new block job state in the processing function qemu: blockjob: Pass job into qemuBlockJobUpdateDisk and rename it qemu: Allocate diskPriv->blockjob only when there's a blockjob qemu: migration: Don't call qemuBlockJobSyncEndDisk when block job has terminated qemu: blockjob: Convert qemuBlockJobSyncEndDisk to take job instead of disk qemu: blockjob: Add job name into the data qemu: domain: Add global table of blockjobs qemu: blockjob: Register new and running blockjobs in the global table qemu: blockjob: Add string convertors for blockjob type and state enums qemu: domain: Store blockjob data in the status XML tests: qemustatusxml2xml: Add test case for block job tracking qemu: monitor: Add new fields for 'block-stream' command qemu: monitor: Add new fields for 'block-commit' command qemu: monitor: Add new fields for 'blockdev-mirror' command qemu: monitor: Add support for 'job-dismiss' command qemu: monitor: Add support for 'job-cancel' command qemu: monitor: Add support for 'job-complete' command qemu: monitor: Add infrastructure for 'query-jobs' qemu: blockjob: Add 'concluded' state for a block job qemu: monitor: Implement support for 'JOB_STATUS_CHANGE' event qemu: process: Don't trigger BLOCK_JOB* events with -blockdev qemu: blockjob: Add helper to convert monitor job status to internal state qemu: Add handler for job state change event qemu: blockjob: Add modern block job event handler qemu: process: Refresh -blockdev based blockjobs on reconnect to qemu src/qemu/qemu_blockjob.c | 687 ++++++++++++++---- src/qemu/qemu_blockjob.h | 111 ++- src/qemu/qemu_domain.c | 123 +++- src/qemu/qemu_domain.h | 13 +- src/qemu/qemu_driver.c | 114 ++- src/qemu/qemu_migration.c | 233 +++--- src/qemu/qemu_monitor.c | 120 ++- src/qemu/qemu_monitor.h | 82 ++- src/qemu/qemu_monitor_json.c | 239 +++++- src/qemu/qemu_monitor_json.h | 28 +- src/qemu/qemu_process.c | 179 ++++- .../query-jobs-create.json | 20 + .../query-jobs-create.result | 11 + .../qemumonitorjsondata/query-jobs-empty.json | 1 + .../query-jobs-empty.result | 0 tests/qemumonitorjsontest.c | 101 ++- .../blockjob-blockdev-in.xml | 364 ++++++++++ .../blockjob-blockdev-out.xml | 1 + tests/qemuxml2xmltest.c | 2 + 19 files changed, 2097 insertions(+), 332 deletions(-) create mode 100644 tests/qemumonitorjsondata/query-jobs-create.json create mode 100644 tests/qemumonitorjsondata/query-jobs-create.result create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.json create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.result create mode 100644 tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml create mode 120000 tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml -- 2.19.2

Put the emitting of VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 into a separate function. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 0f52996ade..ea327b83fe 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -42,6 +42,30 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); +/** + * qemuBlockJobEmitEvents: + * + * Emits the VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 + * for a block job. + */ +static void +qemuBlockJobEmitEvents(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + virDomainBlockJobType type, + virConnectDomainEventBlockJobStatus status) +{ + virObjectEventPtr event = NULL; + virObjectEventPtr event2 = NULL; + + event = virDomainEventBlockJobNewFromObj(vm, disk->src->path, type, status); + virObjectEventStateQueue(driver->domainEventState, event); + + event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); + virObjectEventStateQueue(driver->domainEventState, event2); +} + + /** * qemuBlockJobUpdate: * @vm: domain @@ -101,9 +125,6 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, int type, int status) { - virObjectEventPtr event = NULL; - virObjectEventPtr event2 = NULL; - const char *path; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainDiskDefPtr persistDisk = NULL; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); @@ -114,14 +135,11 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, type, status); - /* Have to generate two variants of the event for old vs. new - * client callbacks */ if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT && disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) type = disk->mirrorJob; - path = virDomainDiskGetSource(disk); - event = virDomainEventBlockJobNewFromObj(vm, path, type, status); - event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); + + qemuBlockJobEmitEvents(driver, vm, disk, type, status); /* If we completed a block pull or commit, then update the XML * to match. */ @@ -210,9 +228,6 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, "after block job", vm->def->name); } - virObjectEventStateQueue(driver->domainEventState, event); - virObjectEventStateQueue(driver->domainEventState, event2); - virObjectUnref(cfg); } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:17PM +0100, Peter Krempa wrote:
Put the emitting of VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 into a separate function.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 0f52996ade..ea327b83fe 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -42,6 +42,30 @@ VIR_LOG_INIT("qemu.qemu_blockjob");
+/** + * qemuBlockJobEmitEvents: + * + * Emits the VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 + * for a block job. + */ +static void +qemuBlockJobEmitEvents(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + virDomainBlockJobType type, + virConnectDomainEventBlockJobStatus status) +{ + virObjectEventPtr event = NULL; + virObjectEventPtr event2 = NULL; + + event = virDomainEventBlockJobNewFromObj(vm, disk->src->path, type, status);
The original function uses the virDomainDiskGetSource wrapper, but here you access disk->src->path directly.
+ virObjectEventStateQueue(driver->domainEventState, event); + + event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); + virObjectEventStateQueue(driver->domainEventState, event2); +} + + /** * qemuBlockJobUpdate: * @vm: domain
With that addressed: Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The event reports the disk path to identify the disk which makes sense only for local disks. Additionally network backed disks like NBD don't need to have a path so the callback would return NULL. Report VIR_DOMAIN_EVENT_ID_BLOCK_JOB only for non-empty local disks. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index ea327b83fe..d38674a9d0 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -46,7 +46,7 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); * qemuBlockJobEmitEvents: * * Emits the VIR_DOMAIN_EVENT_ID_BLOCK_JOB and VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 - * for a block job. + * for a block job. The former event is emitted only for local disks. */ static void qemuBlockJobEmitEvents(virQEMUDriverPtr driver, @@ -58,8 +58,11 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, virObjectEventPtr event = NULL; virObjectEventPtr event2 = NULL; - event = virDomainEventBlockJobNewFromObj(vm, disk->src->path, type, status); - virObjectEventStateQueue(driver->domainEventState, event); + if (virStorageSourceIsLocalStorage(disk->src) && + !virStorageSourceIsEmpty(disk->src)) { + event = virDomainEventBlockJobNewFromObj(vm, disk->src->path, type, status); + virObjectEventStateQueue(driver->domainEventState, event); + } event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); virObjectEventStateQueue(driver->domainEventState, event2); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:18PM +0100, Peter Krempa wrote:
The event reports the disk path to identify the disk which makes sense only for local disks. Additionally network backed disks like NBD don't need to have a path so the callback would return NULL.
Report VIR_DOMAIN_EVENT_ID_BLOCK_JOB only for non-empty local disks.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Replace use of qemuBlockJobEventProcess with the general helper. A small tweak is required to pass in the 'type' and 'status' of the job via the appropriate private data variables. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d0cf2c115a..c582e255a2 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,6 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; + qemuDomainDiskPrivatePtr diskPriv; if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4725,8 +4726,17 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; } - if ((disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) - qemuBlockJobEventProcess(driver, vm, disk, QEMU_ASYNC_JOB_NONE, type, status); + if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) { + VIR_DEBUG("disk %s not found", diskAlias); + goto endjob; + } + + diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + + diskPriv->blockJobType = type; + diskPriv->blockJobStatus = status; + + qemuBlockJobUpdate(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); endjob: qemuDomainObjEndJob(driver, vm); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:19PM +0100, Peter Krempa wrote:
Replace use of qemuBlockJobEventProcess with the general helper. A small tweak is required to pass in the 'type' and 'status' of the job via the appropriate private data variables.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The function is now only called locally. Some code movement was necessary to avoid forward declarations. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 80 ++++++++++++++++++++-------------------- src/qemu/qemu_blockjob.h | 6 --- 2 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index d38674a9d0..32fa9876b5 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -69,45 +69,6 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, } -/** - * qemuBlockJobUpdate: - * @vm: domain - * @disk: domain disk - * @error: error (output parameter) - * - * Update disk's mirror state in response to a block job event stored in - * blockJobStatus by qemuProcessHandleBlockJob event handler. - * - * Returns the block job event processed or -1 if there was no pending event. - */ -int -qemuBlockJobUpdate(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, - virDomainDiskDefPtr disk, - char **error) -{ - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - qemuDomainObjPrivatePtr priv = vm->privateData; - int status = diskPriv->blockJobStatus; - - if (error) - *error = NULL; - - if (status != -1) { - qemuBlockJobEventProcess(priv->driver, vm, disk, asyncJob, - diskPriv->blockJobType, - diskPriv->blockJobStatus); - diskPriv->blockJobStatus = -1; - if (error) - VIR_STEAL_PTR(*error, diskPriv->blockJobError); - else - VIR_FREE(diskPriv->blockJobError); - } - - return status; -} - - /** * qemuBlockJobEventProcess: * @driver: qemu driver @@ -120,7 +81,7 @@ qemuBlockJobUpdate(virDomainObjPtr vm, * from QEMU. For mirror state's that must survive libvirt * restart, also update the domain's status XML. */ -void +static void qemuBlockJobEventProcess(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, @@ -235,6 +196,45 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, } +/** + * qemuBlockJobUpdate: + * @vm: domain + * @disk: domain disk + * @error: error (output parameter) + * + * Update disk's mirror state in response to a block job event stored in + * blockJobStatus by qemuProcessHandleBlockJob event handler. + * + * Returns the block job event processed or -1 if there was no pending event. + */ +int +qemuBlockJobUpdate(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virDomainDiskDefPtr disk, + char **error) +{ + qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuDomainObjPrivatePtr priv = vm->privateData; + int status = diskPriv->blockJobStatus; + + if (error) + *error = NULL; + + if (status != -1) { + qemuBlockJobEventProcess(priv->driver, vm, disk, asyncJob, + diskPriv->blockJobType, + diskPriv->blockJobStatus); + diskPriv->blockJobStatus = -1; + if (error) + VIR_STEAL_PTR(*error, diskPriv->blockJobError); + else + VIR_FREE(diskPriv->blockJobError); + } + + return status; +} + + /** * qemuBlockJobSyncBegin: * @disk: domain disk diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index cee3ee21c4..c14d26a6a1 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -30,12 +30,6 @@ int qemuBlockJobUpdate(virDomainObjPtr vm, qemuDomainAsyncJob asyncJob, virDomainDiskDefPtr disk, char **error); -void qemuBlockJobEventProcess(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - qemuDomainAsyncJob asyncJob, - int type, - int status); void qemuBlockJobSyncBegin(virDomainDiskDefPtr disk); void qemuBlockJobSyncEnd(virDomainObjPtr vm, -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:20PM +0100, Peter Krempa wrote:
The function is now only called locally. Some code movement was necessary to avoid forward declarations.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 80 ++++++++++++++++++++-------------------- src/qemu/qemu_blockjob.h | 6 --- 2 files changed, 40 insertions(+), 46 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

All the public APIs of the qemu_blockjob module operate on a 'disk'. Since I'll be adding APIs which operate on a job later let's rename the existing ones. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 28 ++++++++++++++-------------- src/qemu/qemu_blockjob.h | 16 ++++++++-------- src/qemu/qemu_driver.c | 10 +++++----- src/qemu/qemu_migration.c | 20 ++++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 32fa9876b5..8b51235795 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -197,7 +197,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, /** - * qemuBlockJobUpdate: + * qemuBlockJobUpdateDisk: * @vm: domain * @disk: domain disk * @error: error (output parameter) @@ -208,10 +208,10 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, * Returns the block job event processed or -1 if there was no pending event. */ int -qemuBlockJobUpdate(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, - virDomainDiskDefPtr disk, - char **error) +qemuBlockJobUpdateDisk(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virDomainDiskDefPtr disk, + char **error) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuDomainObjPrivatePtr priv = vm->privateData; @@ -236,20 +236,20 @@ qemuBlockJobUpdate(virDomainObjPtr vm, /** - * qemuBlockJobSyncBegin: + * qemuBlockJobSyncBeginDisk: * @disk: domain disk * * Begin a new synchronous block job for @disk. The synchronous - * block job is ended by a call to qemuBlockJobSyncEnd, or by + * block job is ended by a call to qemuBlockJobSyncEndDisk, or by * the guest quitting. * * During a synchronous block job, a block job event for @disk * will not be processed asynchronously. Instead, it will be - * processed only when qemuBlockJobUpdate or qemuBlockJobSyncEnd + * processed only when qemuBlockJobUpdateDisk or qemuBlockJobSyncEndDisk * is called. */ void -qemuBlockJobSyncBegin(virDomainDiskDefPtr disk) +qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); @@ -260,7 +260,7 @@ qemuBlockJobSyncBegin(virDomainDiskDefPtr disk) /** - * qemuBlockJobSyncEnd: + * qemuBlockJobSyncEndDisk: * @vm: domain * @disk: domain disk * @@ -268,11 +268,11 @@ qemuBlockJobSyncBegin(virDomainDiskDefPtr disk) * for the disk is processed. */ void -qemuBlockJobSyncEnd(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, - virDomainDiskDefPtr disk) +qemuBlockJobSyncEndDisk(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virDomainDiskDefPtr disk) { VIR_DEBUG("disk=%s", disk->dst); - qemuBlockJobUpdate(vm, asyncJob, disk, NULL); + qemuBlockJobUpdateDisk(vm, asyncJob, disk, NULL); QEMU_DOMAIN_DISK_PRIVATE(disk)->blockJobSync = false; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index c14d26a6a1..9f7677dc1e 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -26,14 +26,14 @@ # include "qemu_conf.h" # include "qemu_domain.h" -int qemuBlockJobUpdate(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, - virDomainDiskDefPtr disk, - char **error); +int qemuBlockJobUpdateDisk(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virDomainDiskDefPtr disk, + char **error); -void qemuBlockJobSyncBegin(virDomainDiskDefPtr disk); -void qemuBlockJobSyncEnd(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, - virDomainDiskDefPtr disk); +void qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk); +void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virDomainDiskDefPtr disk); #endif /* __QEMU_BLOCKJOB_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c582e255a2..1b2a2d70ec 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4736,7 +4736,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, diskPriv->blockJobType = type; diskPriv->blockJobStatus = status; - qemuBlockJobUpdate(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); endjob: qemuDomainObjEndJob(driver, vm); @@ -17361,7 +17361,7 @@ qemuDomainBlockJobAbort(virDomainPtr dom, } if (!async) - qemuBlockJobSyncBegin(disk); + qemuBlockJobSyncBeginDisk(disk); if (pivot) { if ((ret = qemuDomainBlockPivot(driver, vm, device, disk)) < 0) @@ -17400,19 +17400,19 @@ qemuDomainBlockJobAbort(virDomainPtr dom, * scheduled block jobs from confusing us. */ if (!async) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - qemuBlockJobUpdate(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); while (diskPriv->blockjob) { if (virDomainObjWait(vm) < 0) { ret = -1; goto endjob; } - qemuBlockJobUpdate(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); } } endjob: if (disk) - qemuBlockJobSyncEnd(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index a0b622d2ea..8720b34311 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -490,7 +490,7 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObjPtr vm, if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdate(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); if (status == VIR_DOMAIN_BLOCK_JOB_FAILED) { if (error) { virReportError(VIR_ERR_OPERATION_FAILED, @@ -549,7 +549,7 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdate(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: if (check) { @@ -566,7 +566,7 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, ATTRIBUTE_FALLTHROUGH; case VIR_DOMAIN_BLOCK_JOB_CANCELED: case VIR_DOMAIN_BLOCK_JOB_COMPLETED: - qemuBlockJobSyncEnd(vm, asyncJob, disk); + qemuBlockJobSyncEndDisk(vm, asyncJob, disk); diskPriv->migrating = false; break; @@ -630,7 +630,7 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, int status; int rv; - status = qemuBlockJobUpdate(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: case VIR_DOMAIN_BLOCK_JOB_CANCELED: @@ -712,7 +712,7 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, err = virSaveLastError(); failed = true; } - qemuBlockJobSyncEnd(vm, asyncJob, disk); + qemuBlockJobSyncEndDisk(vm, asyncJob, disk); diskPriv->migrating = false; } } @@ -954,7 +954,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) goto cleanup; - qemuBlockJobSyncBegin(disk); + qemuBlockJobSyncBeginDisk(disk); if (flags & VIR_MIGRATE_TLS) { rc = qemuMigrationSrcNBDStorageCopyBlockdev(driver, vm, @@ -971,7 +971,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, } if (rc < 0) { - qemuBlockJobSyncEnd(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); goto cleanup; } @@ -5355,7 +5355,7 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; if (QEMU_DOMAIN_DISK_PRIVATE(disk)->migrating) { - qemuBlockJobSyncBegin(disk); + qemuBlockJobSyncBeginDisk(disk); storage = true; } } @@ -5385,7 +5385,7 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, VIR_DEBUG("Drive mirror on disk %s is still running", disk->dst); } else { VIR_DEBUG("Drive mirror on disk %s is gone", disk->dst); - qemuBlockJobSyncEnd(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); diskPriv->migrating = false; } } @@ -5407,7 +5407,7 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); if (diskPriv->migrating) { - qemuBlockJobSyncEnd(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); diskPriv->migrating = false; } } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:21PM +0100, Peter Krempa wrote:
All the public APIs of the qemu_blockjob module operate on a 'disk'. Since I'll be adding APIs which operate on a job later let's rename the existing ones.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 28 ++++++++++++++-------------- src/qemu/qemu_blockjob.h | 16 ++++++++-------- src/qemu/qemu_driver.c | 10 +++++----- src/qemu/qemu_migration.c | 20 ++++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The blockjob module uses 'qemuDomainAsyncJob' in it's public headers. As I plan adding a new structure containing job data which will need to be included in "qemu_domain.h" it's necessary to break the circular dependency. Convert 'qemuDomainAsyncJob' type to 'int' as it's an enum. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 6 +++--- src/qemu/qemu_blockjob.h | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 8b51235795..1b6d16cbb9 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -85,7 +85,7 @@ static void qemuBlockJobEventProcess(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, - qemuDomainAsyncJob asyncJob, + int asyncJob, int type, int status) { @@ -209,7 +209,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, */ int qemuBlockJobUpdateDisk(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, + int asyncJob, virDomainDiskDefPtr disk, char **error) { @@ -269,7 +269,7 @@ qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) */ void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, + int asyncJob, virDomainDiskDefPtr disk) { VIR_DEBUG("disk=%s", disk->dst); diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 9f7677dc1e..0c440757f2 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -24,16 +24,15 @@ # include "internal.h" # include "qemu_conf.h" -# include "qemu_domain.h" int qemuBlockJobUpdateDisk(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, + int asyncJob, virDomainDiskDefPtr disk, char **error); void qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk); void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, - qemuDomainAsyncJob asyncJob, + int asyncJob, virDomainDiskDefPtr disk); #endif /* __QEMU_BLOCKJOB_H__ */ -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:22PM +0100, Peter Krempa wrote:
The blockjob module uses 'qemuDomainAsyncJob' in it's public headers. As I plan adding a new structure containing job data which will need to be included in "qemu_domain.h" it's necessary to break the circular dependency.
Convert 'qemuDomainAsyncJob' type to 'int' as it's an enum.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 6 +++--- src/qemu/qemu_blockjob.h | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Struct qemuDomainDiskPrivate was holding multiple variables connected to a disk block job. Consolidate them into a new struct qemuBlockJobData. This will also allow simpler extensions to the block job mechanisms. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 36 +++++++++++++++++++++++------------- src/qemu/qemu_blockjob.h | 15 +++++++++++++++ src/qemu/qemu_domain.c | 12 +++++++++--- src/qemu/qemu_domain.h | 9 ++------- src/qemu/qemu_driver.c | 16 ++++++++-------- src/qemu/qemu_process.c | 15 ++++++++------- 6 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 1b6d16cbb9..b1a30b1edb 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -42,6 +42,17 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); +void +qemuBlockJobDataFree(qemuBlockJobDataPtr job) +{ + if (!job) + return; + + VIR_FREE(job->errmsg); + VIR_FREE(job); +} + + /** * qemuBlockJobEmitEvents: * @@ -160,7 +171,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, virStorageSourceBackingStoreClear(disk->src); ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); - diskPriv->blockjob = false; + diskPriv->blockjob->started = false; break; case VIR_DOMAIN_BLOCK_JOB_READY: @@ -176,7 +187,7 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver, } disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; - diskPriv->blockjob = false; + diskPriv->blockjob->started = false; break; case VIR_DOMAIN_BLOCK_JOB_LAST: @@ -213,22 +224,21 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, virDomainDiskDefPtr disk, char **error) { - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; qemuDomainObjPrivatePtr priv = vm->privateData; - int status = diskPriv->blockJobStatus; + int status = job->status; if (error) *error = NULL; if (status != -1) { qemuBlockJobEventProcess(priv->driver, vm, disk, asyncJob, - diskPriv->blockJobType, - diskPriv->blockJobStatus); - diskPriv->blockJobStatus = -1; + job->type, status); + job->status = -1; if (error) - VIR_STEAL_PTR(*error, diskPriv->blockJobError); + VIR_STEAL_PTR(*error, job->errmsg); else - VIR_FREE(diskPriv->blockJobError); + VIR_FREE(job->errmsg); } return status; @@ -251,11 +261,11 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, void qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) { - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; VIR_DEBUG("disk=%s", disk->dst); - diskPriv->blockJobSync = true; - diskPriv->blockJobStatus = -1; + job->synchronous = true; + job->status = -1; } @@ -274,5 +284,5 @@ qemuBlockJobSyncEndDisk(virDomainObjPtr vm, { VIR_DEBUG("disk=%s", disk->dst); qemuBlockJobUpdateDisk(vm, asyncJob, disk, NULL); - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockJobSync = false; + QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->synchronous = false; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 0c440757f2..1479c6f720 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -25,6 +25,21 @@ # include "internal.h" # include "qemu_conf.h" + +typedef struct _qemuBlockJobData qemuBlockJobData; +typedef qemuBlockJobData *qemuBlockJobDataPtr; + +struct _qemuBlockJobData { + bool started; + int type; + int status; + char *errmsg; + bool synchronous; /* API call is waiting for this job */ +}; + +void +qemuBlockJobDataFree(qemuBlockJobDataPtr job); + int qemuBlockJobUpdateDisk(virDomainObjPtr vm, int asyncJob, virDomainDiskDefPtr disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 1eb0e31df0..5a64231f95 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1062,6 +1062,11 @@ qemuDomainDiskPrivateNew(void) if (!(priv = virObjectNew(qemuDomainDiskPrivateClass))) return NULL; + if (VIR_ALLOC(priv->blockjob) < 0) { + virObjectUnref(priv); + priv = NULL; + } + return (virObjectPtr) priv; } @@ -1070,10 +1075,10 @@ qemuDomainDiskPrivateDispose(void *obj) { qemuDomainDiskPrivatePtr priv = obj; - VIR_FREE(priv->blockJobError); virStorageSourceFree(priv->migrSource); VIR_FREE(priv->qomName); VIR_FREE(priv->nodeCopyOnRead); + qemuBlockJobDataFree(priv->blockjob); } static virClassPtr qemuDomainStorageSourcePrivateClass; @@ -9255,7 +9260,8 @@ qemuDomainDiskBlockJobIsActive(virDomainDiskDefPtr disk) return true; } - if (diskPriv->blockjob) { + if (diskPriv->blockjob && + diskPriv->blockjob->started) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk '%s' already in active block job"), disk->dst); @@ -9284,7 +9290,7 @@ qemuDomainHasBlockjob(virDomainObjPtr vm, virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (!copy_only && diskPriv->blockjob) + if (!copy_only && diskPriv->blockjob && diskPriv->blockjob->started) return true; if (disk->mirror && disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 53b5ea1678..a03950e77b 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -32,6 +32,7 @@ # include "snapshot_conf.h" # include "qemu_monitor.h" # include "qemu_agent.h" +# include "qemu_blockjob.h" # include "qemu_conf.h" # include "qemu_capabilities.h" # include "qemu_migration_params.h" @@ -388,13 +389,7 @@ struct _qemuDomainDiskPrivate { /* ideally we want a smarter way to interlock block jobs on single qemu disk * in the future, but for now we just disallow any concurrent job on a * single disk */ - bool blockjob; - - /* for some synchronous block jobs, we need to notify the owner */ - int blockJobType; /* type of the block job from the event */ - int blockJobStatus; /* status of the finished block job */ - char *blockJobError; /* block job completed event error */ - bool blockJobSync; /* the block job needs synchronized termination */ + qemuBlockJobDataPtr blockjob; bool migrating; /* the disk is being migrated */ virStorageSourcePtr migrSource; /* disk source object used for NBD migration */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1b2a2d70ec..d06eabb265 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; - qemuDomainDiskPrivatePtr diskPriv; + qemuBlockJobDataPtr job; if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4731,10 +4731,10 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; } - diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; - diskPriv->blockJobType = type; - diskPriv->blockJobStatus = status; + job->type = type; + job->status = status; qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); @@ -17294,7 +17294,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, if (ret < 0) goto endjob; - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = true; + QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after state change", @@ -17401,7 +17401,7 @@ qemuDomainBlockJobAbort(virDomainPtr dom, if (!async) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); - while (diskPriv->blockjob) { + while (diskPriv->blockjob->started) { if (virDomainObjWait(vm) < 0) { ret = -1; goto endjob; @@ -17825,7 +17825,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, disk->mirror = mirror; mirror = NULL; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = true; + QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after state change", @@ -18225,7 +18225,7 @@ qemuDomainBlockCommit(virDomainPtr dom, } if (ret == 0) { - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = true; + QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; mirror = NULL; } else { disk->mirror = NULL; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 2f8e19d29d..72725fcdf1 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -926,7 +926,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virQEMUDriverPtr driver = opaque; struct qemuProcessEvent *processEvent = NULL; virDomainDiskDefPtr disk; - qemuDomainDiskPrivatePtr diskPriv; + qemuBlockJobDataPtr job; char *data = NULL; virObjectLock(vm); @@ -936,14 +936,15 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) goto error; - diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (diskPriv->blockJobSync) { + job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + if (job->synchronous) { /* We have a SYNC API waiting for this event, dispatch it back */ - diskPriv->blockJobType = type; - diskPriv->blockJobStatus = status; - VIR_FREE(diskPriv->blockJobError); - ignore_value(VIR_STRDUP_QUIET(diskPriv->blockJobError, error)); + job->type = type; + job->status = status; + VIR_FREE(job->errmsg); + ignore_value(VIR_STRDUP_QUIET(job->errmsg, error)); virDomainObjBroadcast(vm); } else { /* there is no waiting SYNC API, dispatch the update to a thread */ -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:23PM +0100, Peter Krempa wrote:
Struct qemuDomainDiskPrivate was holding multiple variables connected to a disk block job. Consolidate them into a new struct qemuBlockJobData.
This will also allow simpler extensions to the block job mechanisms.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 36 +++++++++++++++++++++++------------- src/qemu/qemu_blockjob.h | 15 +++++++++++++++ src/qemu/qemu_domain.c | 12 +++++++++--- src/qemu/qemu_domain.h | 9 ++------- src/qemu/qemu_driver.c | 16 ++++++++-------- src/qemu/qemu_process.c | 15 ++++++++------- 6 files changed, 65 insertions(+), 38 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

'cleanup' label was accessed only from a jump to 'error'. Consolidate everyting into 'cleanup'. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 72725fcdf1..3fccbc79bb 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -935,7 +935,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, diskAlias, vm, vm->def->name, type, status); if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) - goto error; + goto cleanup; job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; @@ -949,11 +949,11 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } else { /* there is no waiting SYNC API, dispatch the update to a thread */ if (VIR_ALLOC(processEvent) < 0) - goto error; + goto cleanup; processEvent->eventType = QEMU_PROCESS_EVENT_BLOCK_JOB; if (VIR_STRDUP(data, diskAlias) < 0) - goto error; + goto cleanup; processEvent->data = data; processEvent->vm = virObjectRef(vm); processEvent->action = type; @@ -961,16 +961,16 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { ignore_value(virObjectUnref(vm)); - goto error; + goto cleanup; } + + processEvent = NULL; } cleanup: + qemuProcessEventFree(processEvent); virObjectUnlock(vm); return 0; - error: - qemuProcessEventFree(processEvent); - goto cleanup; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:24PM +0100, Peter Krempa wrote:
'cleanup' label was accessed only from a jump to 'error'. Consolidate everyting into 'cleanup'.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This will handle blockjob finalizing for the old approach so rename it accordingly. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index b1a30b1edb..5934d58480 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -81,7 +81,7 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, /** - * qemuBlockJobEventProcess: + * qemuBlockJobEventProcessLegacy: * @driver: qemu driver * @vm: domain * @disk: domain disk @@ -93,12 +93,12 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, * restart, also update the domain's status XML. */ static void -qemuBlockJobEventProcess(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - int asyncJob, - int type, - int status) +qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + int asyncJob, + int type, + int status) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainDiskDefPtr persistDisk = NULL; @@ -232,8 +232,8 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, *error = NULL; if (status != -1) { - qemuBlockJobEventProcess(priv->driver, vm, disk, asyncJob, - job->type, status); + qemuBlockJobEventProcessLegacy(priv->driver, vm, disk, asyncJob, + job->type, status); job->status = -1; if (error) VIR_STEAL_PTR(*error, job->errmsg); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:25PM +0100, Peter Krempa wrote:
This will handle blockjob finalizing for the old approach so rename it accordingly.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

qemuBlockJobEventProcessLegacy was getting too big. Remove handling of completed jobs in a separate function. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 118 +++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 5934d58480..a7cdc3c068 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -80,6 +80,70 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, } +static void +qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + int asyncJob) +{ + qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + virDomainDiskDefPtr persistDisk = NULL; + + if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) { + if (vm->newDef) { + virStorageSourcePtr copy = NULL; + + if ((persistDisk = virDomainDiskByName(vm->newDef, + disk->dst, false))) { + copy = virStorageSourceCopy(disk->mirror, false); + if (!copy || + virStorageSourceInitChainElement(copy, + persistDisk->src, + true) < 0) { + VIR_WARN("Unable to update persistent definition " + "on vm %s after block job", + vm->def->name); + virStorageSourceFree(copy); + copy = NULL; + persistDisk = NULL; + } + } + if (copy) { + virStorageSourceFree(persistDisk->src); + persistDisk->src = copy; + } + } + + /* XXX We want to revoke security labels as well as audit that + * revocation, before dropping the original source. But it gets + * tricky if both source and mirror share common backing files (we + * want to only revoke the non-shared portion of the chain); so for + * now, we leak the access to the original. */ + virDomainLockImageDetach(driver->lockManager, vm, disk->src); + virStorageSourceFree(disk->src); + disk->src = disk->mirror; + } else { + if (disk->mirror) { + virDomainLockImageDetach(driver->lockManager, vm, disk->mirror); + virStorageSourceFree(disk->mirror); + } + } + + /* Recompute the cached backing chain to match our + * updates. Better would be storing the chain ourselves + * rather than reprobing, but we haven't quite completed + * that conversion to use our XML tracking. */ + disk->mirror = NULL; + disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; + disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; + disk->src->id = 0; + virStorageSourceBackingStoreClear(disk->src); + ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); + ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); + diskPriv->blockjob->started = false; +} + + /** * qemuBlockJobEventProcessLegacy: * @driver: qemu driver @@ -101,7 +165,6 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, int status) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - virDomainDiskDefPtr persistDisk = NULL; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, status=%d", @@ -120,58 +183,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, * to match. */ switch ((virConnectDomainEventBlockJobStatus) status) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: - if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) { - if (vm->newDef) { - virStorageSourcePtr copy = NULL; - - if ((persistDisk = virDomainDiskByName(vm->newDef, - disk->dst, false))) { - copy = virStorageSourceCopy(disk->mirror, false); - if (!copy || - virStorageSourceInitChainElement(copy, - persistDisk->src, - true) < 0) { - VIR_WARN("Unable to update persistent definition " - "on vm %s after block job", - vm->def->name); - virStorageSourceFree(copy); - copy = NULL; - persistDisk = NULL; - } - } - if (copy) { - virStorageSourceFree(persistDisk->src); - persistDisk->src = copy; - } - } - - /* XXX We want to revoke security labels as well as audit that - * revocation, before dropping the original source. But it gets - * tricky if both source and mirror share common backing files (we - * want to only revoke the non-shared portion of the chain); so for - * now, we leak the access to the original. */ - virDomainLockImageDetach(driver->lockManager, vm, disk->src); - virStorageSourceFree(disk->src); - disk->src = disk->mirror; - } else { - if (disk->mirror) { - virDomainLockImageDetach(driver->lockManager, vm, disk->mirror); - virStorageSourceFree(disk->mirror); - } - } - - /* Recompute the cached backing chain to match our - * updates. Better would be storing the chain ourselves - * rather than reprobing, but we haven't quite completed - * that conversion to use our XML tracking. */ - disk->mirror = NULL; - disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; - disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; - disk->src->id = 0; - virStorageSourceBackingStoreClear(disk->src); - ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); - ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); - diskPriv->blockjob->started = false; + qemuBlockJobEventProcessLegacyCompleted(driver, vm, disk, asyncJob); break; case VIR_DOMAIN_BLOCK_JOB_READY: -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:26PM +0100, Peter Krempa wrote:
qemuBlockJobEventProcessLegacy was getting too big. Remove handling of completed jobs in a separate function.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 118 +++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 53 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Also, s/comlpleted/completed/ in the subject. On Wed, Dec 12, 2018 at 06:08:26PM +0100, Peter Krempa wrote:
qemuBlockJobEventProcessLegacy was getting too big. Remove handling of completed jobs in a separate function.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 118 +++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 53 deletions(-)
Jano

Internally we do a 'block-copy' to accomodate non-shared storage migration but the code did not fill in that the block job was active on the disk when starting the copy job. Since we handle block jobs finishes regardless of having it registered it's not a problem but soon will become one. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 8720b34311..5d2eb60f03 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -977,6 +977,7 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, VIR_FREE(diskAlias); diskPriv->migrating = true; + diskPriv->blockjob->started = true; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) { VIR_WARN("Failed to save status on vm %s", vm->def->name); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:27PM +0100, Peter Krempa wrote:
Internally we do a 'block-copy' to accomodate non-shared storage migration but the code did not fill in that the block job was active on the disk when starting the copy job. Since we handle block jobs finishes regardless of having it registered it's not a problem but soon will become one.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Block job state was widely untracked by libvirt across restarts which was allowed by a stateless block job finishing handler which discarded disk state and redetected it. This is undesirable since we'll need to track more information for individual blockjobs due to -blockdev integration requirements. In case of legacy blockjobs we can recover whether the job is present at reconnect time by querying qemu. Adding tracking whether a job is present will allow simplification of the non-shared-storage cancellation code. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3fccbc79bb..471e11115f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7755,6 +7755,70 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, } +static int +qemuProcessRefreshLegacyBlockjob(void *payload, + const void *name, + void *opaque) +{ + const char *jobname = name; + virDomainObjPtr vm = opaque; + qemuMonitorBlockJobInfoPtr info = payload; + virDomainDiskDefPtr disk; + qemuDomainDiskPrivatePtr diskPriv; + qemuBlockJobDataPtr job; + + if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, jobname, jobname))) { + VIR_DEBUG("could not find disk for block job '%s'", jobname); + return 0; + } + + diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + job = diskPriv->blockjob; + + if (disk->mirror) { + if (info->ready == 1 || + (info->ready == -1 && info->end == info->cur)) + disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY; + } + + job->started = true; + job->status = -1; + + return 0; +} + + +static int +qemuProcessRefreshLegacyBlockjobs(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virHashTablePtr blockJobs = NULL; + int ret = -1; + + qemuDomainObjEnterMonitor(driver, vm); + blockJobs = qemuMonitorGetAllBlockJobInfo(qemuDomainGetMonitor(vm)); + if (qemuDomainObjExitMonitor(driver, vm) < 0 || !blockJobs) + goto cleanup; + + if (virHashForEach(blockJobs, qemuProcessRefreshLegacyBlockjob, vm) < 0) + goto cleanup; + + ret = 0; + + cleanup: + virHashFree(blockJobs); + return ret; +} + + +static int +qemuProcessRefreshBlockjobs(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + return qemuProcessRefreshLegacyBlockjobs(driver, vm); +} + + struct qemuProcessReconnectData { virQEMUDriverPtr driver; virDomainObjPtr obj; @@ -7958,6 +8022,9 @@ qemuProcessReconnect(void *opaque) qemuBlockNodeNamesDetect(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error; + if (qemuProcessRefreshBlockjobs(driver, obj) < 0) + goto error; + if (qemuRefreshVirtioChannelState(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) goto error; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:28PM +0100, Peter Krempa wrote:
Block job state was widely untracked by libvirt across restarts which was allowed by a stateless block job finishing handler which discarded disk state and redetected it. This is undesirable since we'll need to track more information for individual blockjobs due to -blockdev integration requirements.
In case of legacy blockjobs we can recover whether the job is present at reconnect time by querying qemu. Adding tracking whether a job is present will allow simplification of the non-shared-storage cancellation code.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3fccbc79bb..471e11115f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7755,6 +7755,70 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, }
+static int +qemuProcessRefreshLegacyBlockjob(void *payload, + const void *name, + void *opaque) +{ + const char *jobname = name; + virDomainObjPtr vm = opaque; + qemuMonitorBlockJobInfoPtr info = payload; + virDomainDiskDefPtr disk; + qemuDomainDiskPrivatePtr diskPriv; + qemuBlockJobDataPtr job; + + if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, jobname, jobname))) { + VIR_DEBUG("could not find disk for block job '%s'", jobname); + return 0; + } + + diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + job = diskPriv->blockjob; + + if (disk->mirror) { + if (info->ready == 1 || + (info->ready == -1 && info->end == info->cur)) + disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY;
mirrorState uses virDomainDiskMirrorState so this should be: VIR_DOMAIN_DISK_MIRROR_STATE_READY just like in the code in qemuDomainBlockPivot removed by the next patch Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Now that we reprobe the status of blockjobs when reconnecting in addition to handling job status events, the status reprobing can be removed as we always track the correct status internally. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d06eabb265..0598f6e5c7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17090,9 +17090,8 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, const char *device, virDomainDiskDefPtr disk) { - int ret = -1, rc; + int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; - qemuMonitorBlockJobInfo info; virStorageSourcePtr oldsrc = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); @@ -17103,23 +17102,6 @@ qemuDomainBlockPivot(virQEMUDriverPtr driver, goto cleanup; } - /* Probe the status, if needed. */ - if (!disk->mirrorState) { - qemuDomainObjEnterMonitor(driver, vm); - rc = qemuMonitorGetBlockJobInfo(priv->mon, disk->info.alias, &info); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - goto cleanup; - if (rc < 0) - goto cleanup; - if (rc == 1 && - (info.ready == 1 || - (info.ready == -1 && - info.end == info.cur && - (info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY || - info.type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT)))) - disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY; - } - if (disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_READY) { virReportError(VIR_ERR_BLOCK_COPY_ACTIVE, _("disk '%s' not ready for pivot yet"), -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:29PM +0100, Peter Krempa wrote:
Now that we reprobe the status of blockjobs when reconnecting in addition to handling job status events, the status reprobing can be removed as we always track the correct status internally.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

When cancelling job after a reconnect we can now use the disk block job state rather than having to re-detect it in the migration code. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 63 +++++++++++---------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 5d2eb60f03..5f4fcb4bad 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -678,7 +678,9 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, * @check: if true report an error when some of the mirrors fails * * Cancel all drive-mirrors started by qemuMigrationSrcNBDStorageCopy. - * Any pending block job events for the affected disks will be processed. + * Any pending block job events for the affected disks will be processed and + * synchronous block job terminated regardless of return value unless qemu + * has crashed. * * Returns 0 on success, -1 otherwise. */ @@ -701,6 +703,11 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + if (!diskPriv->blockjob->started) { + qemuBlockJobSyncEndDisk(vm, asyncJob, disk); + diskPriv->migrating = false; + } + if (!diskPriv->migrating) continue; @@ -5345,7 +5352,6 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; - virHashTablePtr blockJobs = NULL; bool storage = false; size_t i; int ret = -1; @@ -5353,67 +5359,34 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, VIR_DEBUG("Canceling unfinished outgoing migration of domain %s", vm->def->name); - for (i = 0; i < vm->def->ndisks; i++) { - virDomainDiskDefPtr disk = vm->def->disks[i]; - if (QEMU_DOMAIN_DISK_PRIVATE(disk)->migrating) { - qemuBlockJobSyncBeginDisk(disk); - storage = true; - } - } - qemuDomainObjEnterMonitor(driver, vm); - ignore_value(qemuMonitorMigrateCancel(priv->mon)); - if (storage) - blockJobs = qemuMonitorGetAllBlockJobInfo(priv->mon); - - if (qemuDomainObjExitMonitor(driver, vm) < 0 || (storage && !blockJobs)) - goto endsyncjob; - - if (!storage) { - ret = 0; + if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; - } for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (!diskPriv->migrating) - continue; - - if (virHashLookup(blockJobs, disk->info.alias)) { - VIR_DEBUG("Drive mirror on disk %s is still running", disk->dst); - } else { - VIR_DEBUG("Drive mirror on disk %s is gone", disk->dst); - qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); + if (!diskPriv->blockjob->started) diskPriv->migrating = false; + + if (diskPriv->migrating) { + qemuBlockJobSyncBeginDisk(disk); + storage = true; } } - if (qemuMigrationSrcNBDCopyCancel(driver, vm, false, + + if (storage && + qemuMigrationSrcNBDCopyCancel(driver, vm, false, QEMU_ASYNC_JOB_NONE, NULL) < 0) - goto endsyncjob; + goto cleanup; ret = 0; cleanup: - virHashFree(blockJobs); return ret; - - endsyncjob: - if (storage) { - for (i = 0; i < vm->def->ndisks; i++) { - virDomainDiskDefPtr disk = vm->def->disks[i]; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - - if (diskPriv->migrating) { - qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); - diskPriv->migrating = false; - } - } - } - goto cleanup; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:30PM +0100, Peter Krempa wrote:
When cancelling job after a reconnect we can now use the disk block job state rather than having to re-detect it in the migration code.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 63 +++++++++++---------------------------- 1 file changed, 18 insertions(+), 45 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Reference counting will simplify semantics of the lifecycle of the object. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 33 ++++++++++++++++++++++++++++----- src/qemu/qemu_blockjob.h | 5 +++-- src/qemu/qemu_domain.c | 4 ++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index a7cdc3c068..cf1710a0e8 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -42,14 +42,37 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); -void -qemuBlockJobDataFree(qemuBlockJobDataPtr job) +static virClassPtr qemuBlockJobDataClass; + + +static void +qemuBlockJobDataDispose(void *obj) { - if (!job) - return; + qemuBlockJobDataPtr job = obj; VIR_FREE(job->errmsg); - VIR_FREE(job); +} + + +static int +qemuBlockJobDataOnceInit(void) +{ + if (!VIR_CLASS_NEW(qemuBlockJobData, virClassForObject())) + return -1; + + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(qemuBlockJobData) + +qemuBlockJobDataPtr +qemuBlockJobDataNew(void) +{ + if (qemuBlockJobDataInitialize() < 0) + return NULL; + + return virObjectNew(qemuBlockJobDataClass); } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 1479c6f720..de1ad7039d 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -30,6 +30,8 @@ typedef struct _qemuBlockJobData qemuBlockJobData; typedef qemuBlockJobData *qemuBlockJobDataPtr; struct _qemuBlockJobData { + virObject parent; + bool started; int type; int status; @@ -37,8 +39,7 @@ struct _qemuBlockJobData { bool synchronous; /* API call is waiting for this job */ }; -void -qemuBlockJobDataFree(qemuBlockJobDataPtr job); +qemuBlockJobDataPtr qemuBlockJobDataNew(void); int qemuBlockJobUpdateDisk(virDomainObjPtr vm, int asyncJob, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5a64231f95..9c3ab426bd 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1062,7 +1062,7 @@ qemuDomainDiskPrivateNew(void) if (!(priv = virObjectNew(qemuDomainDiskPrivateClass))) return NULL; - if (VIR_ALLOC(priv->blockjob) < 0) { + if (!(priv->blockjob = qemuBlockJobDataNew())) { virObjectUnref(priv); priv = NULL; } @@ -1078,7 +1078,7 @@ qemuDomainDiskPrivateDispose(void *obj) virStorageSourceFree(priv->migrSource); VIR_FREE(priv->qomName); VIR_FREE(priv->nodeCopyOnRead); - qemuBlockJobDataFree(priv->blockjob); + virObjectUnref(priv->blockjob); } static virClassPtr qemuDomainStorageSourcePrivateClass; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:31PM +0100, Peter Krempa wrote:
Reference counting will simplify semantics of the lifecycle of the object.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 33 ++++++++++++++++++++++++++++----- src/qemu/qemu_blockjob.h | 5 +++-- src/qemu/qemu_domain.c | 4 ++-- 3 files changed, 33 insertions(+), 9 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The field is used to note the state the job has transitioned to while handling the blockjob state change event. Rename the field so that it's obvious that this is the new state and not the general state of the blockjob. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 12 ++++++------ src/qemu/qemu_blockjob.h | 3 ++- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_process.c | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index cf1710a0e8..252f1640b2 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -261,22 +261,22 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; qemuDomainObjPrivatePtr priv = vm->privateData; - int status = job->status; + int state = job->newstate; if (error) *error = NULL; - if (status != -1) { + if (state != -1) { qemuBlockJobEventProcessLegacy(priv->driver, vm, disk, asyncJob, - job->type, status); - job->status = -1; + job->type, state); + job->newstate = -1; if (error) VIR_STEAL_PTR(*error, job->errmsg); else VIR_FREE(job->errmsg); } - return status; + return state; } @@ -300,7 +300,7 @@ qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) VIR_DEBUG("disk=%s", disk->dst); job->synchronous = true; - job->status = -1; + job->newstate = -1; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index de1ad7039d..7761eee6ae 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -34,9 +34,10 @@ struct _qemuBlockJobData { bool started; int type; - int status; char *errmsg; bool synchronous; /* API call is waiting for this job */ + + int newstate; /* virConnectDomainEventBlockJobStatus - new state to be processed */ }; qemuBlockJobDataPtr qemuBlockJobDataNew(void); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0598f6e5c7..637307806b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4734,7 +4734,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; job->type = type; - job->status = status; + job->newstate = status; qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 471e11115f..2cead713f6 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -942,7 +942,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (job->synchronous) { /* We have a SYNC API waiting for this event, dispatch it back */ job->type = type; - job->status = status; + job->newstate = status; VIR_FREE(job->errmsg); ignore_value(VIR_STRDUP_QUIET(job->errmsg, error)); virDomainObjBroadcast(vm); @@ -7782,7 +7782,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload, } job->started = true; - job->status = -1; + job->newstate = -1; return 0; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:32PM +0100, Peter Krempa wrote:
The field is used to note the state the job has transitioned to while handling the blockjob state change event. Rename the field so that it's obvious that this is the new state and not the general state of the blockjob.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 12 ++++++------ src/qemu/qemu_blockjob.h | 3 ++- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_process.c | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Extract the disk mirroring startup code from the loop into a separate function to allow cleaner cleanup paths. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 85 +++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 5f4fcb4bad..9d165e27aa 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -890,6 +890,58 @@ qemuMigrationSrcNBDStorageCopyDriveMirror(virQEMUDriverPtr driver, } +static int +qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + const char *host, + int port, + unsigned long long mirror_speed, + unsigned int mirror_flags, + const char *tlsAlias, + unsigned int flags) +{ + qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + char *diskAlias = NULL; + int rc; + int ret = -1; + + if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) + goto cleanup; + + qemuBlockJobSyncBeginDisk(disk); + + if (flags & VIR_MIGRATE_TLS) { + rc = qemuMigrationSrcNBDStorageCopyBlockdev(driver, vm, + disk, diskAlias, + host, port, + mirror_speed, + mirror_flags, + tlsAlias); + } else { + rc = qemuMigrationSrcNBDStorageCopyDriveMirror(driver, vm, diskAlias, + host, port, + mirror_speed, + mirror_flags); + } + + if (rc < 0) { + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); + goto cleanup; + } + + VIR_FREE(diskAlias); + diskPriv->migrating = true; + diskPriv->blockjob->started = true; + + ret = 0; + + cleanup: + VIR_FREE(diskAlias); + return ret; +} + + /** * qemuMigrationSrcNBDStorageCopy: * @driver: qemu driver @@ -926,7 +978,6 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, int ret = -1; int port; size_t i; - char *diskAlias = NULL; unsigned long long mirror_speed = speed; unsigned int mirror_flags = VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT; int rv; @@ -951,40 +1002,15 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - int rc; /* check whether disk should be migrated */ if (!qemuMigrationAnyCopyDisk(disk, nmigrate_disks, migrate_disks)) continue; - if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) - goto cleanup; - - qemuBlockJobSyncBeginDisk(disk); - - if (flags & VIR_MIGRATE_TLS) { - rc = qemuMigrationSrcNBDStorageCopyBlockdev(driver, vm, - disk, diskAlias, - host, port, - mirror_speed, - mirror_flags, - tlsAlias); - } else { - rc = qemuMigrationSrcNBDStorageCopyDriveMirror(driver, vm, diskAlias, - host, port, - mirror_speed, - mirror_flags); - } - - if (rc < 0) { - qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); + if (qemuMigrationSrcNBDStorageCopyOne(driver, vm, disk, host, port, + mirror_speed, mirror_flags, + tlsAlias, flags) < 0) goto cleanup; - } - - VIR_FREE(diskAlias); - diskPriv->migrating = true; - diskPriv->blockjob->started = true; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) { VIR_WARN("Failed to save status on vm %s", vm->def->name); @@ -1024,7 +1050,6 @@ qemuMigrationSrcNBDStorageCopy(virQEMUDriverPtr driver, cleanup: virObjectUnref(cfg); - VIR_FREE(diskAlias); return ret; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:33PM +0100, Peter Krempa wrote:
Extract the disk mirroring startup code from the loop into a separate function to allow cleaner cleanup paths.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 85 +++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 30 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 5f4fcb4bad..9d165e27aa 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -890,6 +890,58 @@ qemuMigrationSrcNBDStorageCopyDriveMirror(virQEMUDriverPtr driver, }
+static int +qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + const char *host, + int port, + unsigned long long mirror_speed, + unsigned int mirror_flags, + const char *tlsAlias, + unsigned int flags) +{ + qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + char *diskAlias = NULL; + int rc; + int ret = -1; + + if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) + goto cleanup; + + qemuBlockJobSyncBeginDisk(disk); + + if (flags & VIR_MIGRATE_TLS) { + rc = qemuMigrationSrcNBDStorageCopyBlockdev(driver, vm, + disk, diskAlias, + host, port, + mirror_speed, + mirror_flags, + tlsAlias); + } else { + rc = qemuMigrationSrcNBDStorageCopyDriveMirror(driver, vm, diskAlias, + host, port, + mirror_speed, + mirror_flags); + } + + if (rc < 0) { + qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); + goto cleanup; + } +
+ VIR_FREE(diskAlias);
You can drop this line now that the success path will fall through to cleanup instead of leading to another iteration of the loop.
+ diskPriv->migrating = true; + diskPriv->blockjob->started = true; + + ret = 0; + + cleanup: + VIR_FREE(diskAlias); + return ret; +} +
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Rather than direclty modifying fields in the qemuBlockJobDataPtr structure add a bunch of fields which allow to do the transitions. This will help later when adding more complexity to the job handing. APIs introduced in this patch are: qemuBlockJobDiskNew - prepare for starting a new blockjob on a disk qemuBlockJobDiskGetJob - get the block job data structure for a disk For individual job state manipulation the following APIs are added: qemuBlockJobStarted - Sets the job as started with qemu. Until that the job can be cancelled without asking qemu. qemuBlockJobStartupFinalize - finalize job startup. If the job was started in qemu already, just releases reference to the job object. Otherwise clears everything as if the job was never started. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 81 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 19 +++++++++ src/qemu/qemu_driver.c | 31 ++++++++++++--- src/qemu/qemu_migration.c | 7 +++- src/qemu/qemu_process.c | 17 ++++---- 5 files changed, 141 insertions(+), 14 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 252f1640b2..a3dc231b32 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -76,6 +76,87 @@ qemuBlockJobDataNew(void) } +static void +qemuBlockJobDataReset(qemuBlockJobDataPtr job) +{ + job->started = false; + job->type = -1; + job->newstate = -1; + VIR_FREE(job->errmsg); + job->synchronous = false; +} + + +/** + * qemuBlockJobDiskNew: + * @disk: disk definition + * + * Start/associate a new blockjob with @disk. + * + * Returns 0 on success and -1 on failure. + */ +qemuBlockJobDataPtr +qemuBlockJobDiskNew(virDomainDiskDefPtr disk) +{ + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + qemuBlockJobDataReset(job); + return virObjectRef(job); +} + + +/** + * qemuBlockJobDiskGetJob: + * @disk: disk definition + * + * Get a reference to the block job data object associated with @disk. + */ +qemuBlockJobDataPtr +qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) +{ + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + if (!job) + return NULL; + + return virObjectRef(job); +} + + +/** + * qemuBlockJobStarted: + * @job: job data + * + * Mark @job as started in qemu. + */ +void +qemuBlockJobStarted(qemuBlockJobDataPtr job) +{ + job->started = true; +} + + +/** + * qemuBlockJobStartupFinalize: + * @job: job being started + * + * Cancels and clears the job private data if the job was not started with + * qemu (see qemuBlockJobStarted) or just clears up the local reference + * to @job if it was started. + */ +void +qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job) +{ + if (!job) + return; + + if (!job->started) + qemuBlockJobDataReset(job); + + virObjectUnref(job); +} + + /** * qemuBlockJobEmitEvents: * diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 7761eee6ae..3d1b389dec 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -42,6 +42,21 @@ struct _qemuBlockJobData { qemuBlockJobDataPtr qemuBlockJobDataNew(void); +qemuBlockJobDataPtr +qemuBlockJobDiskNew(virDomainDiskDefPtr disk) + ATTRIBUTE_NONNULL(1); + +qemuBlockJobDataPtr +qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) + ATTRIBUTE_NONNULL(1); + +void +qemuBlockJobStarted(qemuBlockJobDataPtr job) + ATTRIBUTE_NONNULL(1); + +void +qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job); + int qemuBlockJobUpdateDisk(virDomainObjPtr vm, int asyncJob, virDomainDiskDefPtr disk, @@ -52,4 +67,8 @@ void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, int asyncJob, virDomainDiskDefPtr disk); +qemuBlockJobDataPtr +qemuBlockJobGetByDisk(virDomainDiskDefPtr disk) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + #endif /* __QEMU_BLOCKJOB_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 637307806b..9e5171744e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; - qemuBlockJobDataPtr job; + qemuBlockJobDataPtr job = NULL; if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4731,7 +4731,11 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; } - job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + if (!(job = qemuBlockJobDiskGetJob(disk))) { + if (!(job = qemuBlockJobDiskNew(disk))) + goto endjob; + qemuBlockJobStarted(job); + } job->type = type; job->newstate = status; @@ -4739,6 +4743,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); endjob: + qemuBlockJobStartupFinalize(job); qemuDomainObjEndJob(driver, vm); } @@ -17197,6 +17202,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, char *basePath = NULL; char *backingPath = NULL; unsigned long long speed = bandwidth; + qemuBlockJobDataPtr job = NULL; int ret = -1; if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE && !base) { @@ -17263,6 +17269,9 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, speed <<= 20; } + if (!(job = qemuBlockJobDiskNew(disk))) + goto endjob; + qemuDomainObjEnterMonitor(driver, vm); if (baseSource) basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src, @@ -17276,7 +17285,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, if (ret < 0) goto endjob; - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; + qemuBlockJobStarted(job); if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after state change", @@ -17286,6 +17295,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, qemuDomainObjEndJob(driver, vm); cleanup: + qemuBlockJobStartupFinalize(job); virObjectUnref(cfg); VIR_FREE(basePath); VIR_FREE(backingPath); @@ -17663,6 +17673,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, const char *format = NULL; virErrorPtr monitor_error = NULL; bool reuse = !!(flags & VIR_DOMAIN_BLOCK_COPY_REUSE_EXT); + qemuBlockJobDataPtr job = NULL; /* Preliminaries: find the disk we are editing, sanity checks */ virCheckFlags(VIR_DOMAIN_BLOCK_COPY_SHALLOW | @@ -17786,6 +17797,9 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, goto endjob; } + if (!(job = qemuBlockJobDiskNew(disk))) + goto endjob; + /* Actually start the mirroring */ qemuDomainObjEnterMonitor(driver, vm); /* qemuMonitorDriveMirror needs to honor the REUSE_EXT flag as specified @@ -17802,12 +17816,12 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, } /* Update vm in place to match changes. */ + qemuBlockJobStarted(job); need_unlink = false; virStorageFileDeinit(mirror); disk->mirror = mirror; mirror = NULL; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after state change", @@ -17822,6 +17836,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, virSetError(monitor_error); virFreeError(monitor_error); } + qemuBlockJobStartupFinalize(job); cleanup: VIR_FREE(device); @@ -18032,6 +18047,7 @@ qemuDomainBlockCommit(virDomainPtr dom, char *backingPath = NULL; virStorageSourcePtr mirror = NULL; unsigned long long speed = bandwidth; + qemuBlockJobDataPtr job = NULL; /* XXX Add support for COMMIT_DELETE */ virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW | @@ -18192,6 +18208,10 @@ qemuDomainBlockCommit(virDomainPtr dom, disk->mirror = mirror; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT; } + + if (!(job = qemuBlockJobDiskNew(disk))) + goto endjob; + qemuDomainObjEnterMonitor(driver, vm); basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src, baseSource); @@ -18207,7 +18227,7 @@ qemuDomainBlockCommit(virDomainPtr dom, } if (ret == 0) { - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->started = true; + qemuBlockJobStarted(job); mirror = NULL; } else { disk->mirror = NULL; @@ -18232,6 +18252,7 @@ qemuDomainBlockCommit(virDomainPtr dom, } } virStorageSourceFree(mirror); + qemuBlockJobStartupFinalize(job); qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 9d165e27aa..79ad2efe75 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -902,6 +902,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, unsigned int flags) { qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job = NULL; char *diskAlias = NULL; int rc; int ret = -1; @@ -909,6 +910,9 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) goto cleanup; + if (!(job = qemuBlockJobDiskNew(disk))) + goto cleanup; + qemuBlockJobSyncBeginDisk(disk); if (flags & VIR_MIGRATE_TLS) { @@ -932,11 +936,12 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, VIR_FREE(diskAlias); diskPriv->migrating = true; - diskPriv->blockjob->started = true; + qemuBlockJobStarted(job); ret = 0; cleanup: + qemuBlockJobStartupFinalize(job); VIR_FREE(diskAlias); return ret; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 2cead713f6..b1fdbca15e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -926,7 +926,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virQEMUDriverPtr driver = opaque; struct qemuProcessEvent *processEvent = NULL; virDomainDiskDefPtr disk; - qemuBlockJobDataPtr job; + qemuBlockJobDataPtr job = NULL; char *data = NULL; virObjectLock(vm); @@ -937,9 +937,9 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) goto cleanup; - job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + job = qemuBlockJobDiskGetJob(disk); - if (job->synchronous) { + if (job && job->synchronous) { /* We have a SYNC API waiting for this event, dispatch it back */ job->type = type; job->newstate = status; @@ -968,6 +968,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } cleanup: + qemuBlockJobStartupFinalize(job); qemuProcessEventFree(processEvent); virObjectUnlock(vm); return 0; @@ -7764,7 +7765,6 @@ qemuProcessRefreshLegacyBlockjob(void *payload, virDomainObjPtr vm = opaque; qemuMonitorBlockJobInfoPtr info = payload; virDomainDiskDefPtr disk; - qemuDomainDiskPrivatePtr diskPriv; qemuBlockJobDataPtr job; if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, jobname, jobname))) { @@ -7772,8 +7772,10 @@ qemuProcessRefreshLegacyBlockjob(void *payload, return 0; } - diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - job = diskPriv->blockjob; + if (!(job = qemuBlockJobDiskNew(disk))) + return -1; + + qemuBlockJobStarted(job); if (disk->mirror) { if (info->ready == 1 || @@ -7781,8 +7783,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload, disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY; } - job->started = true; - job->newstate = -1; + qemuBlockJobStartupFinalize(job); return 0; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:34PM +0100, Peter Krempa wrote:
Rather than direclty modifying fields in the qemuBlockJobDataPtr
directly
structure add a bunch of fields which allow to do the transitions.
This will help later when adding more complexity to the job handing.
handling
APIs introduced in this patch are:
qemuBlockJobDiskNew - prepare for starting a new blockjob on a disk qemuBlockJobDiskGetJob - get the block job data structure for a disk
For individual job state manipulation the following APIs are added: qemuBlockJobStarted - Sets the job as started with qemu. Until that the job can be cancelled without asking qemu.
qemuBlockJobStartupFinalize - finalize job startup. If the job was started in qemu already, just releases reference to the job object. Otherwise clears everything as if the job was never started.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 81 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 19 +++++++++ src/qemu/qemu_driver.c | 31 ++++++++++++--- src/qemu/qemu_migration.c | 7 +++- src/qemu/qemu_process.c | 17 ++++---- 5 files changed, 141 insertions(+), 14 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 637307806b..9e5171744e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; - qemuBlockJobDataPtr job; + qemuBlockJobDataPtr job = NULL;
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4731,7 +4731,11 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; }
- job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + if (!(job = qemuBlockJobDiskGetJob(disk))) {
Can qemuBlockJobDiskGetJob really return NULL here?
+ if (!(job = qemuBlockJobDiskNew(disk))) + goto endjob; + qemuBlockJobStarted(job); + }
job->type = type; job->newstate = status; @@ -937,9 +937,9 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, diskAlias, NULL))) goto cleanup;
- job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + job = qemuBlockJobDiskGetJob(disk);
- if (job->synchronous) { + if (job && job->synchronous) {
Same question
/* We have a SYNC API waiting for this event, dispatch it back */ job->type = type; job->newstate = status;
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

On Mon, Jan 07, 2019 at 14:35:08 +0100, Ján Tomko wrote:
On Wed, Dec 12, 2018 at 06:08:34PM +0100, Peter Krempa wrote:
Rather than direclty modifying fields in the qemuBlockJobDataPtr
directly
structure add a bunch of fields which allow to do the transitions.
This will help later when adding more complexity to the job handing.
handling
APIs introduced in this patch are:
qemuBlockJobDiskNew - prepare for starting a new blockjob on a disk qemuBlockJobDiskGetJob - get the block job data structure for a disk
For individual job state manipulation the following APIs are added: qemuBlockJobStarted - Sets the job as started with qemu. Until that the job can be cancelled without asking qemu.
qemuBlockJobStartupFinalize - finalize job startup. If the job was started in qemu already, just releases reference to the job object. Otherwise clears everything as if the job was never started.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 81 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 19 +++++++++ src/qemu/qemu_driver.c | 31 ++++++++++++--- src/qemu/qemu_migration.c | 7 +++- src/qemu/qemu_process.c | 17 ++++---- 5 files changed, 141 insertions(+), 14 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 637307806b..9e5171744e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; - qemuBlockJobDataPtr job; + qemuBlockJobDataPtr job = NULL;
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4731,7 +4731,11 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; }
- job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + if (!(job = qemuBlockJobDiskGetJob(disk))) {
Can qemuBlockJobDiskGetJob really return NULL here?
Not at this point in the series. In patch 29 though the block job data struct is allocated only when there's a job. I don't remember whether this originates from the forethought that such a change will happen or I've reorganized it, but I'd prefer to keep the checks here.

On Mon, Jan 07, 2019 at 14:35:08 +0100, Ján Tomko wrote:
On Wed, Dec 12, 2018 at 06:08:34PM +0100, Peter Krempa wrote:
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
[...]
index 637307806b..9e5171744e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4716,7 +4716,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, int status) { virDomainDiskDefPtr disk; - qemuBlockJobDataPtr job; + qemuBlockJobDataPtr job = NULL;
if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) return; @@ -4731,7 +4731,11 @@ processBlockJobEvent(virQEMUDriverPtr driver, goto endjob; }
- job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + if (!(job = qemuBlockJobDiskGetJob(disk))) {
Can qemuBlockJobDiskGetJob really return NULL here?
Not at this point in the series but it will become the case later.

If the job wasn't started, we don't need to end the synchronous job. Add a note and drop the unnecessary calls. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 4 +++- src/qemu/qemu_migration.c | 8 ++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index a3dc231b32..17657a9d9b 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -391,7 +391,9 @@ qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) * @disk: domain disk * * End a synchronous block job for @disk. Any pending block job event - * for the disk is processed. + * for the disk is processed. Note that it's not necessary to call this function + * in case the block job was not started successfully if + * qemuBlockJobStartupFinalize will be called. */ void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 79ad2efe75..dbf35b0829 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -703,10 +703,8 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (!diskPriv->blockjob->started) { - qemuBlockJobSyncEndDisk(vm, asyncJob, disk); + if (!diskPriv->blockjob->started) diskPriv->migrating = false; - } if (!diskPriv->migrating) continue; @@ -929,10 +927,8 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, mirror_flags); } - if (rc < 0) { - qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_MIGRATION_OUT, disk); + if (rc < 0) goto cleanup; - } VIR_FREE(diskAlias); diskPriv->migrating = true; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:35PM +0100, Peter Krempa wrote:
If the job wasn't started, we don't need to end the synchronous job. Add a note and drop the unnecessary calls.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 4 +++- src/qemu/qemu_migration.c | 8 ++------ 2 files changed, 5 insertions(+), 7 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Block jobs can also happen on objects which are not a disk at a given point (e.g. the frontend was not hotplugged yet) and thus will be eventually kept separately. Add a reference back to the disk for blockjobs which do correspond to a disk. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 1 + src/qemu/qemu_blockjob.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 17657a9d9b..7bbe582722 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -99,6 +99,7 @@ qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk) { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + job->disk = disk; qemuBlockJobDataReset(job); return virObjectRef(job); diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 3d1b389dec..feaee4dee3 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -32,6 +32,8 @@ typedef qemuBlockJobData *qemuBlockJobDataPtr; struct _qemuBlockJobData { virObject parent; + virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not corrspond to any disk */ + bool started; int type; char *errmsg; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:36PM +0100, Peter Krempa wrote:
Block jobs can also happen on objects which are not a disk at a given point (e.g. the frontend was not hotplugged yet) and thus will be eventually kept separately. Add a reference back to the disk for blockjobs which do correspond to a disk.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 1 + src/qemu/qemu_blockjob.h | 2 ++ 2 files changed, 3 insertions(+)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 17657a9d9b..7bbe582722 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -99,6 +99,7 @@ qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk) { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + job->disk = disk;
qemuBlockJobDataReset(job); return virObjectRef(job); diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 3d1b389dec..feaee4dee3 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -32,6 +32,8 @@ typedef qemuBlockJobData *qemuBlockJobDataPtr; struct _qemuBlockJobData { virObject parent;
+ virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not corrspond to any disk */
correspond
+ bool started; int type; char *errmsg;
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

We can properly track the job type when starting the job so that we don't have to infer it later. This patch also adds an enum of block job types specific to qemu (qemuBlockjobType) which mirrors the public block job types (virDomainBlockJobType) but allows for other types to be added later which will not be public. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 10 +++++----- src/qemu/qemu_blockjob.h | 20 ++++++++++++++++++-- src/qemu/qemu_driver.c | 11 ++++++----- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 8 ++++++-- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 7bbe582722..531de035af 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -96,12 +96,16 @@ qemuBlockJobDataReset(qemuBlockJobDataPtr job) * Returns 0 on success and -1 on failure. */ qemuBlockJobDataPtr -qemuBlockJobDiskNew(virDomainDiskDefPtr disk) +qemuBlockJobDiskNew(virDomainDiskDefPtr disk, + qemuBlockjobType type) { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; job->disk = disk; qemuBlockJobDataReset(job); + + job->type = type; + return virObjectRef(job); } @@ -278,10 +282,6 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, type, status); - if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT && - disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) - type = disk->mirrorJob; - qemuBlockJobEmitEvents(driver, vm, disk, type, status); /* If we completed a block pull or commit, then update the XML diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index feaee4dee3..6f6d0aa691 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -25,6 +25,21 @@ # include "internal.h" # include "qemu_conf.h" +/** + * This enum has to map all known block job types from enum virDomainBlockJobType + * to the same values. All internal blockjobs can be mapped after and don't + * need to have stable values. + */ +typedef enum { + QEMU_BLOCKJOB_TYPE_NONE = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN, + QEMU_BLOCKJOB_TYPE_PULL = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, + QEMU_BLOCKJOB_TYPE_COPY = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, + QEMU_BLOCKJOB_TYPE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT, + QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT, + QEMU_BLOCKJOB_TYPE_INTERNAL, + QEMU_BLOCKJOB_TYPE_LAST +} qemuBlockjobType; +verify((int)QEMU_BLOCKJOB_TYPE_INTERNAL == VIR_DOMAIN_BLOCK_JOB_TYPE_LAST); typedef struct _qemuBlockJobData qemuBlockJobData; typedef qemuBlockJobData *qemuBlockJobDataPtr; @@ -35,7 +50,7 @@ struct _qemuBlockJobData { virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not corrspond to any disk */ bool started; - int type; + int type; /* qemuBlockjobType */ char *errmsg; bool synchronous; /* API call is waiting for this job */ @@ -45,7 +60,8 @@ struct _qemuBlockJobData { qemuBlockJobDataPtr qemuBlockJobDataNew(void); qemuBlockJobDataPtr -qemuBlockJobDiskNew(virDomainDiskDefPtr disk) +qemuBlockJobDiskNew(virDomainDiskDefPtr disk, + qemuBlockjobType type) ATTRIBUTE_NONNULL(1); qemuBlockJobDataPtr diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9e5171744e..5eace06099 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4732,12 +4732,11 @@ processBlockJobEvent(virQEMUDriverPtr driver, } if (!(job = qemuBlockJobDiskGetJob(disk))) { - if (!(job = qemuBlockJobDiskNew(disk))) + if (!(job = qemuBlockJobDiskNew(disk, type))) goto endjob; qemuBlockJobStarted(job); } - job->type = type; job->newstate = status; qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); @@ -17269,7 +17268,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, speed <<= 20; } - if (!(job = qemuBlockJobDiskNew(disk))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_PULL))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); @@ -17797,7 +17796,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, goto endjob; } - if (!(job = qemuBlockJobDiskNew(disk))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY))) goto endjob; /* Actually start the mirroring */ @@ -18048,6 +18047,7 @@ qemuDomainBlockCommit(virDomainPtr dom, virStorageSourcePtr mirror = NULL; unsigned long long speed = bandwidth; qemuBlockJobDataPtr job = NULL; + qemuBlockjobType jobtype = QEMU_BLOCKJOB_TYPE_COMMIT; /* XXX Add support for COMMIT_DELETE */ virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW | @@ -18207,9 +18207,10 @@ qemuDomainBlockCommit(virDomainPtr dom, disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirror = mirror; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT; + jobtype = QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT; } - if (!(job = qemuBlockJobDiskNew(disk))) + if (!(job = qemuBlockJobDiskNew(disk, jobtype))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index dbf35b0829..837ed39efe 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -908,7 +908,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) goto cleanup; - if (!(job = qemuBlockJobDiskNew(disk))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY))) goto cleanup; qemuBlockJobSyncBeginDisk(disk); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index b1fdbca15e..91c99a2a11 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -941,7 +941,6 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (job && job->synchronous) { /* We have a SYNC API waiting for this event, dispatch it back */ - job->type = type; job->newstate = status; VIR_FREE(job->errmsg); ignore_value(VIR_STRDUP_QUIET(job->errmsg, error)); @@ -7766,13 +7765,18 @@ qemuProcessRefreshLegacyBlockjob(void *payload, qemuMonitorBlockJobInfoPtr info = payload; virDomainDiskDefPtr disk; qemuBlockJobDataPtr job; + virDomainBlockJobType jobtype = info->type; if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, jobname, jobname))) { VIR_DEBUG("could not find disk for block job '%s'", jobname); return 0; } - if (!(job = qemuBlockJobDiskNew(disk))) + if (jobtype == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT && + disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) + jobtype = disk->mirrorJob; + + if (!(job = qemuBlockJobDiskNew(disk, jobtype))) return -1; qemuBlockJobStarted(job); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:37PM +0100, Peter Krempa wrote:
We can properly track the job type when starting the job so that we don't have to infer it later.
This patch also adds an enum of block job types specific to qemu (qemuBlockjobType) which mirrors the public block job types (virDomainBlockJobType) but allows for other types to be added later which will not be public.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 10 +++++----- src/qemu/qemu_blockjob.h | 20 ++++++++++++++++++-- src/qemu/qemu_driver.c | 11 ++++++----- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 8 ++++++-- 5 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index feaee4dee3..6f6d0aa691 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -25,6 +25,21 @@ # include "internal.h" # include "qemu_conf.h"
+/** + * This enum has to map all known block job types from enum virDomainBlockJobType + * to the same values. All internal blockjobs can be mapped after and don't + * need to have stable values. + */ +typedef enum { + QEMU_BLOCKJOB_TYPE_NONE = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN, + QEMU_BLOCKJOB_TYPE_PULL = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, + QEMU_BLOCKJOB_TYPE_COPY = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, + QEMU_BLOCKJOB_TYPE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT, + QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT, + QEMU_BLOCKJOB_TYPE_INTERNAL, + QEMU_BLOCKJOB_TYPE_LAST +} qemuBlockjobType;
qemuBlockjobType does not have a capital J like qemuBlockJobData and lots of the functions.
+verify((int)QEMU_BLOCKJOB_TYPE_INTERNAL == VIR_DOMAIN_BLOCK_JOB_TYPE_LAST);
typedef struct _qemuBlockJobData qemuBlockJobData; typedef qemuBlockJobData *qemuBlockJobDataPtr; @@ -7766,13 +7765,18 @@ qemuProcessRefreshLegacyBlockjob(void *payload, qemuMonitorBlockJobInfoPtr info = payload; virDomainDiskDefPtr disk; qemuBlockJobDataPtr job; + virDomainBlockJobType jobtype = info->type;
if (!(disk = qemuProcessFindDomainDiskByAliasOrQOM(vm, jobname, jobname))) { VIR_DEBUG("could not find disk for block job '%s'", jobname); return 0; }
- if (!(job = qemuBlockJobDiskNew(disk))) + if (jobtype == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT && + disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) + jobtype = disk->mirrorJob; + + if (!(job = qemuBlockJobDiskNew(disk, jobtype)))
This fails to compile on Clang: qemu/qemu_process.c:7780:43: error: implicit conversion from enumeration type 'virDomainBlockJobType' to different enumeration type 'qemuBlockjobType' [-Werror,-Wenum-conversion] if (!(job = qemuBlockJobDiskNew(disk, jobtype))) ~~~~~~~~~~~~~~~~~~~ ^~~~~~~ 1 error generated. Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Don't split out individual fields, just pass in the job. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 531de035af..c6b70c5388 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -257,9 +257,7 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, * qemuBlockJobEventProcessLegacy: * @driver: qemu driver * @vm: domain - * @disk: domain disk - * @type: block job type - * @status: block job status + * @job: job to process events for * * Update disk's mirror state in response to a block job event * from QEMU. For mirror state's that must survive libvirt @@ -268,25 +266,24 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, static void qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainDiskDefPtr disk, - int asyncJob, - int type, - int status) + qemuBlockJobDataPtr job, + int asyncJob) { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainDiskDefPtr disk = job->disk; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, status=%d", + VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, newstate=%d", disk->dst, NULLSTR(virDomainDiskMirrorStateTypeToString(disk->mirrorState)), - type, - status); + job->type, + job->newstate); - qemuBlockJobEmitEvents(driver, vm, disk, type, status); + qemuBlockJobEmitEvents(driver, vm, disk, job->type, job->newstate); /* If we completed a block pull or commit, then update the XML * to match. */ - switch ((virConnectDomainEventBlockJobStatus) status) { + switch ((virConnectDomainEventBlockJobStatus) job->newstate) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: qemuBlockJobEventProcessLegacyCompleted(driver, vm, disk, asyncJob); break; @@ -314,7 +311,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after block job", vm->def->name); - if (status == VIR_DOMAIN_BLOCK_JOB_COMPLETED && vm->newDef) { + if (job->newstate == VIR_DOMAIN_BLOCK_JOB_COMPLETED && vm->newDef) { if (virDomainSaveConfig(cfg->configDir, driver->caps, vm->newDef) < 0) VIR_WARN("Unable to update persistent definition on vm %s " "after block job", vm->def->name); @@ -349,8 +346,7 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, *error = NULL; if (state != -1) { - qemuBlockJobEventProcessLegacy(priv->driver, vm, disk, asyncJob, - job->type, state); + qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); job->newstate = -1; if (error) VIR_STEAL_PTR(*error, job->errmsg); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:38PM +0100, Peter Krempa wrote:
Don't split out individual fields, just pass in the job.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Modify qemuBlockJobSyncBeginDisk to operate on qemuBlockt sJobDataPtr and rename it accordingly. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 11 +++++++---- src/qemu/qemu_blockjob.h | 2 +- src/qemu/qemu_driver.c | 10 +++++++++- src/qemu/qemu_migration.c | 11 +++++++---- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index c6b70c5388..0b2c667dae 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -359,7 +359,7 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, /** - * qemuBlockJobSyncBeginDisk: + * qemuBlockJobSyncBegin: * @disk: domain disk * * Begin a new synchronous block job for @disk. The synchronous @@ -372,11 +372,14 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, * is called. */ void -qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk) +qemuBlockJobSyncBegin(qemuBlockJobDataPtr job) { - qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + const char *diskdst = NULL; - VIR_DEBUG("disk=%s", disk->dst); + if (job->disk) + diskdst = job->disk->dst; + + VIR_DEBUG("disk=%s", NULLSTR(diskdst)); job->synchronous = true; job->newstate = -1; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 6f6d0aa691..0c2ce46986 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -80,7 +80,7 @@ int qemuBlockJobUpdateDisk(virDomainObjPtr vm, virDomainDiskDefPtr disk, char **error); -void qemuBlockJobSyncBeginDisk(virDomainDiskDefPtr disk); +void qemuBlockJobSyncBegin(qemuBlockJobDataPtr job); void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, int asyncJob, virDomainDiskDefPtr disk); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5eace06099..5fd92ca0c1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17316,6 +17316,7 @@ qemuDomainBlockJobAbort(virDomainPtr dom, bool save = false; bool pivot = !!(flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT); bool async = !!(flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC); + qemuBlockJobDataPtr job = NULL; virDomainObjPtr vm; int ret = -1; @@ -17343,6 +17344,12 @@ qemuDomainBlockJobAbort(virDomainPtr dom, if (!(device = qemuAliasDiskDriveFromDisk(disk))) goto endjob; + if (!(job = qemuBlockJobDiskGetJob(disk))) { + virReportError(VIR_ERR_INVALID_ARG, + _("disk %s does not have an active block job"), disk->dst); + goto endjob; + } + if (disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_NONE && disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_READY) { virReportError(VIR_ERR_OPERATION_INVALID, @@ -17352,7 +17359,7 @@ qemuDomainBlockJobAbort(virDomainPtr dom, } if (!async) - qemuBlockJobSyncBeginDisk(disk); + qemuBlockJobSyncBegin(job); if (pivot) { if ((ret = qemuDomainBlockPivot(driver, vm, device, disk)) < 0) @@ -17407,6 +17414,7 @@ qemuDomainBlockJobAbort(virDomainPtr dom, qemuDomainObjEndJob(driver, vm); cleanup: + virObjectUnref(job); virObjectUnref(cfg); VIR_FREE(device); virDomainObjEndAPI(&vm); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 837ed39efe..b9119c5b89 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -911,7 +911,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY))) goto cleanup; - qemuBlockJobSyncBeginDisk(disk); + qemuBlockJobSyncBegin(job); if (flags & VIR_MIGRATE_TLS) { rc = qemuMigrationSrcNBDStorageCopyBlockdev(driver, vm, @@ -5393,16 +5393,19 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job; - if (!diskPriv->blockjob->started) + if (!(job = qemuBlockJobDiskGetJob(disk)) || + !job->started) diskPriv->migrating = false; if (diskPriv->migrating) { - qemuBlockJobSyncBeginDisk(disk); + qemuBlockJobSyncBegin(job); storage = true; } - } + virObjectUnref(job); + } if (storage && qemuMigrationSrcNBDCopyCancel(driver, vm, false, -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:39PM +0100, Peter Krempa wrote:
Modify qemuBlockJobSyncBeginDisk to operate on qemuBlockt sJobDataPtr and rename it accordingly.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 11 +++++++---- src/qemu/qemu_blockjob.h | 2 +- src/qemu/qemu_driver.c | 10 +++++++++- src/qemu/qemu_migration.c | 11 +++++++---- 4 files changed, 24 insertions(+), 10 deletions(-)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index c6b70c5388..0b2c667dae 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -359,7 +359,7 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm,
/** - * qemuBlockJobSyncBeginDisk: + * qemuBlockJobSyncBegin: * @disk: domain disk
The documentation is now out of date.
* * Begin a new synchronous block job for @disk. The synchronous
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

On Wed, Dec 12, 2018 at 06:08:39PM +0100, Peter Krempa wrote:
Modify qemuBlockJobSyncBeginDisk to operate on qemuBlockt sJobDataPtr and
Also, s/Blockt s/Block/g Jano
rename it accordingly.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 11 +++++++---- src/qemu/qemu_blockjob.h | 2 +- src/qemu/qemu_driver.c | 10 +++++++++- src/qemu/qemu_migration.c | 11 +++++++---- 4 files changed, 24 insertions(+), 10 deletions(-)

Add a field tracking the current state of job so that it can be queried later. Until now the job state e.g. that the job is _READY for finalizing was tracked only for mirror jobs. Add tracking of state for all jobs. Similarly to 'qemuBlockJobType' this maps the existing states of the blockjob from virConnectDomainEventBlockJobStatus to 'qemuBlockJobState' so that we can track some internal states as well. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 23 +++++++++++++++-------- src/qemu/qemu_blockjob.h | 22 +++++++++++++++++++++- src/qemu/qemu_domain.c | 5 +++-- src/qemu/qemu_driver.c | 3 +-- src/qemu/qemu_migration.c | 8 ++++++-- src/qemu/qemu_process.c | 4 +++- 6 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 0b2c667dae..e778e5b7ce 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -79,7 +79,6 @@ qemuBlockJobDataNew(void) static void qemuBlockJobDataReset(qemuBlockJobDataPtr job) { - job->started = false; job->type = -1; job->newstate = -1; VIR_FREE(job->errmsg); @@ -104,6 +103,7 @@ qemuBlockJobDiskNew(virDomainDiskDefPtr disk, qemuBlockJobDataReset(job); + job->state = QEMU_BLOCKJOB_STATE_NEW; job->type = type; return virObjectRef(job); @@ -137,7 +137,7 @@ qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) void qemuBlockJobStarted(qemuBlockJobDataPtr job) { - job->started = true; + job->state = QEMU_BLOCKJOB_STATE_RUNNING; } @@ -155,13 +155,21 @@ qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job) if (!job) return; - if (!job->started) + if (job->state == QEMU_BLOCKJOB_STATE_NEW) qemuBlockJobDataReset(job); virObjectUnref(job); } +bool +qemuBlockJobIsRunning(qemuBlockJobDataPtr job) +{ + return job->state == QEMU_BLOCKJOB_STATE_RUNNING || + job->state == QEMU_BLOCKJOB_STATE_READY; +} + + /** * qemuBlockJobEmitEvents: * @@ -195,7 +203,6 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, virDomainDiskDefPtr disk, int asyncJob) { - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); virDomainDiskDefPtr persistDisk = NULL; if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) { @@ -249,7 +256,6 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, virStorageSourceBackingStoreClear(disk->src); ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); - diskPriv->blockjob->started = false; } @@ -271,12 +277,12 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, { virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainDiskDefPtr disk = job->disk; - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, newstate=%d", + VIR_DEBUG("disk=%s, mirrorState=%s, type=%d, state=%d, newstate=%d", disk->dst, NULLSTR(virDomainDiskMirrorStateTypeToString(disk->mirrorState)), job->type, + job->state, job->newstate); qemuBlockJobEmitEvents(driver, vm, disk, job->type, job->newstate); @@ -301,13 +307,14 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, } disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; - diskPriv->blockjob->started = false; break; case VIR_DOMAIN_BLOCK_JOB_LAST: break; } + job->state = job->newstate; + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after block job", vm->def->name); diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 0c2ce46986..67b9c94b64 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -25,6 +25,22 @@ # include "internal.h" # include "qemu_conf.h" +/** + * This enum has to map all known block job states from enum virDomainBlockJobType + * to the same values. All internal blockjobs can be mapped after and don't + * need to have stable values. + */ +typedef enum { + QEMU_BLOCKJOB_STATE_COMPLETED = VIR_DOMAIN_BLOCK_JOB_COMPLETED, + QEMU_BLOCKJOB_STATE_FAILED = VIR_DOMAIN_BLOCK_JOB_FAILED, + QEMU_BLOCKJOB_STATE_CANCELLED = VIR_DOMAIN_BLOCK_JOB_CANCELED, + QEMU_BLOCKJOB_STATE_READY = VIR_DOMAIN_BLOCK_JOB_READY, + QEMU_BLOCKJOB_STATE_NEW = VIR_DOMAIN_BLOCK_JOB_LAST, + QEMU_BLOCKJOB_STATE_RUNNING, + QEMU_BLOCKJOB_STATE_LAST +} qemuBlockjobState; +verify((int)QEMU_BLOCKJOB_STATE_NEW == VIR_DOMAIN_BLOCK_JOB_LAST); + /** * This enum has to map all known block job types from enum virDomainBlockJobType * to the same values. All internal blockjobs can be mapped after and don't @@ -49,8 +65,8 @@ struct _qemuBlockJobData { virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not corrspond to any disk */ - bool started; int type; /* qemuBlockjobType */ + int state; /* qemuBlockjobState */ char *errmsg; bool synchronous; /* API call is waiting for this job */ @@ -72,6 +88,10 @@ void qemuBlockJobStarted(qemuBlockJobDataPtr job) ATTRIBUTE_NONNULL(1); +bool +qemuBlockJobIsRunning(qemuBlockJobDataPtr job) + ATTRIBUTE_NONNULL(1); + void qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9c3ab426bd..01b4c97a91 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -9261,7 +9261,7 @@ qemuDomainDiskBlockJobIsActive(virDomainDiskDefPtr disk) } if (diskPriv->blockjob && - diskPriv->blockjob->started) { + qemuBlockJobIsRunning(diskPriv->blockjob)) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("disk '%s' already in active block job"), disk->dst); @@ -9290,7 +9290,8 @@ qemuDomainHasBlockjob(virDomainObjPtr vm, virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - if (!copy_only && diskPriv->blockjob && diskPriv->blockjob->started) + if (!copy_only && diskPriv->blockjob && + qemuBlockJobIsRunning(diskPriv->blockjob)) return true; if (disk->mirror && disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5fd92ca0c1..f203f89521 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17397,9 +17397,8 @@ qemuDomainBlockJobAbort(virDomainPtr dom, * do the waiting while still holding the VM job, to prevent newly * scheduled block jobs from confusing us. */ if (!async) { - qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); - while (diskPriv->blockjob->started) { + while (qemuBlockJobIsRunning(job)) { if (virDomainObjWait(vm) < 0) { ret = -1; goto endjob; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index b9119c5b89..bde8697ede 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -702,8 +702,10 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job; - if (!diskPriv->blockjob->started) + if (!(job = qemuBlockJobDiskGetJob(disk)) || + !qemuBlockJobIsRunning(job)) diskPriv->migrating = false; if (!diskPriv->migrating) @@ -720,6 +722,8 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, qemuBlockJobSyncEndDisk(vm, asyncJob, disk); diskPriv->migrating = false; } + + virObjectUnref(job); } while ((rv = qemuMigrationSrcNBDCopyCancelled(vm, asyncJob, check)) != 1) { @@ -5396,7 +5400,7 @@ qemuMigrationSrcCancel(virQEMUDriverPtr driver, qemuBlockJobDataPtr job; if (!(job = qemuBlockJobDiskGetJob(disk)) || - !job->started) + !qemuBlockJobIsRunning(job)) diskPriv->migrating = false; if (diskPriv->migrating) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 91c99a2a11..1171da62a8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7783,8 +7783,10 @@ qemuProcessRefreshLegacyBlockjob(void *payload, if (disk->mirror) { if (info->ready == 1 || - (info->ready == -1 && info->end == info->cur)) + (info->ready == -1 && info->end == info->cur)) { disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY; + job->state = VIR_DOMAIN_BLOCK_JOB_READY; + } } qemuBlockJobStartupFinalize(job); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:40PM +0100, Peter Krempa wrote:
Add a field tracking the current state of job so that it can be queried later. Until now the job state e.g. that the job is _READY for finalizing was tracked only for mirror jobs. Add tracking of state for all jobs.
Similarly to 'qemuBlockJobType' this maps the existing states of the blockjob from virConnectDomainEventBlockJobStatus to 'qemuBlockJobState' so that we can track some internal states as well.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 23 +++++++++++++++-------- src/qemu/qemu_blockjob.h | 22 +++++++++++++++++++++- src/qemu/qemu_domain.c | 5 +++-- src/qemu/qemu_driver.c | 3 +-- src/qemu/qemu_migration.c | 8 ++++++-- src/qemu/qemu_process.c | 4 +++- 6 files changed, 49 insertions(+), 16 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index b9119c5b89..bde8697ede 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -702,8 +702,10 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job;
- if (!diskPriv->blockjob->started) + if (!(job = qemuBlockJobDiskGetJob(disk)) || + !qemuBlockJobIsRunning(job)) diskPriv->migrating = false;
if (!diskPriv->migrating)
There is 'continue;' right after, so we'll forget to unref job in the case qemuBlockJobDiskGetJob returns non-NULL, but the job is not running.
@@ -720,6 +722,8 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, qemuBlockJobSyncEndDisk(vm, asyncJob, disk); diskPriv->migrating = false; } + + virObjectUnref(job); }
while ((rv = qemuMigrationSrcNBDCopyCancelled(vm, asyncJob, check)) != 1) {
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The same message is reported in 3 distinct places. Move it out into a single function. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 43 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index bde8697ede..1ce7863460 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -463,6 +463,21 @@ qemuMigrationDstStopNBDServer(virQEMUDriverPtr driver, } +static void +qemuMigrationNBDReportMirrorError(virDomainDiskDefPtr disk, + const char *errmsg) +{ + if (errmsg) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("migration of disk %s failed: %s"), + disk->dst, errmsg); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("migration of disk %s failed"), disk->dst); + } +} + + /** * qemuMigrationSrcNBDStorageCopyReady: * @vm: domain @@ -492,15 +507,7 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObjPtr vm, status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); if (status == VIR_DOMAIN_BLOCK_JOB_FAILED) { - if (error) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed: %s"), - disk->dst, error); - VIR_FREE(error); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed"), disk->dst); - } + qemuMigrationNBDReportMirrorError(disk, error); return -1; } VIR_FREE(error); @@ -553,14 +560,7 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: if (check) { - if (error) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed: %s"), - disk->dst, error); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed"), disk->dst); - } + qemuMigrationNBDReportMirrorError(disk, error); failed = true; } ATTRIBUTE_FALLTHROUGH; @@ -635,14 +635,7 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, case VIR_DOMAIN_BLOCK_JOB_FAILED: case VIR_DOMAIN_BLOCK_JOB_CANCELED: if (failNoJob) { - if (error) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed: %s"), - disk->dst, error); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed"), disk->dst); - } + qemuMigrationNBDReportMirrorError(disk, error); goto cleanup; } ATTRIBUTE_FALLTHROUGH; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:41PM +0100, Peter Krempa wrote:
The same message is reported in 3 distinct places. Move it out into a single function.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 43 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The job error can be safely accessed in the job structure, so we don't need to propagate it through qemuBlockJobUpdateDisk. Drop the propagation and refactor any caller that pased non-NULL error. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 12 ++---------- src/qemu/qemu_blockjob.h | 3 +-- src/qemu/qemu_driver.c | 6 +++--- src/qemu/qemu_migration.c | 28 +++++++++++----------------- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index e778e5b7ce..7aaa439791 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -342,23 +342,15 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, int qemuBlockJobUpdateDisk(virDomainObjPtr vm, int asyncJob, - virDomainDiskDefPtr disk, - char **error) + virDomainDiskDefPtr disk) { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; qemuDomainObjPrivatePtr priv = vm->privateData; int state = job->newstate; - if (error) - *error = NULL; - if (state != -1) { qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); job->newstate = -1; - if (error) - VIR_STEAL_PTR(*error, job->errmsg); - else - VIR_FREE(job->errmsg); } return state; @@ -408,6 +400,6 @@ qemuBlockJobSyncEndDisk(virDomainObjPtr vm, virDomainDiskDefPtr disk) { VIR_DEBUG("disk=%s", disk->dst); - qemuBlockJobUpdateDisk(vm, asyncJob, disk, NULL); + qemuBlockJobUpdateDisk(vm, asyncJob, disk); QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->synchronous = false; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 67b9c94b64..4527ee2a93 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -97,8 +97,7 @@ qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job); int qemuBlockJobUpdateDisk(virDomainObjPtr vm, int asyncJob, - virDomainDiskDefPtr disk, - char **error); + virDomainDiskDefPtr disk); void qemuBlockJobSyncBegin(qemuBlockJobDataPtr job); void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f203f89521..2e47ec021a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4739,7 +4739,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, job->newstate = status; - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); endjob: qemuBlockJobStartupFinalize(job); @@ -17397,13 +17397,13 @@ qemuDomainBlockJobAbort(virDomainPtr dom, * do the waiting while still holding the VM job, to prevent newly * scheduled block jobs from confusing us. */ if (!async) { - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); while (qemuBlockJobIsRunning(job)) { if (virDomainObjWait(vm) < 0) { ret = -1; goto endjob; } - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk, NULL); + qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); } } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 1ce7863460..ac3c609067 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -464,13 +464,14 @@ qemuMigrationDstStopNBDServer(virQEMUDriverPtr driver, static void -qemuMigrationNBDReportMirrorError(virDomainDiskDefPtr disk, - const char *errmsg) +qemuMigrationNBDReportMirrorError(virDomainDiskDefPtr disk) { - if (errmsg) { + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + if (job->errmsg) { virReportError(VIR_ERR_OPERATION_FAILED, _("migration of disk %s failed: %s"), - disk->dst, errmsg); + disk->dst, job->errmsg); } else { virReportError(VIR_ERR_OPERATION_FAILED, _("migration of disk %s failed"), disk->dst); @@ -500,17 +501,15 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObjPtr vm, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - char *error = NULL; if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); if (status == VIR_DOMAIN_BLOCK_JOB_FAILED) { - qemuMigrationNBDReportMirrorError(disk, error); + qemuMigrationNBDReportMirrorError(disk); return -1; } - VIR_FREE(error); if (disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_READY) notReady++; @@ -551,16 +550,15 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); - char *error = NULL; if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: if (check) { - qemuMigrationNBDReportMirrorError(disk, error); + qemuMigrationNBDReportMirrorError(disk); failed = true; } ATTRIBUTE_FALLTHROUGH; @@ -576,8 +574,6 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, if (status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) completed++; - - VIR_FREE(error); } /* Updating completed block job drops the lock thus we have to recheck @@ -625,17 +621,16 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, { qemuDomainObjPrivatePtr priv = vm->privateData; char *diskAlias = NULL; - char *error = NULL; int ret = -1; int status; int rv; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk, &error); + status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: case VIR_DOMAIN_BLOCK_JOB_CANCELED: if (failNoJob) { - qemuMigrationNBDReportMirrorError(disk, error); + qemuMigrationNBDReportMirrorError(disk); goto cleanup; } ATTRIBUTE_FALLTHROUGH; @@ -659,7 +654,6 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, cleanup: VIR_FREE(diskAlias); - VIR_FREE(error); return ret; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:42PM +0100, Peter Krempa wrote:
The job error can be safely accessed in the job structure, so we don't need to propagate it through qemuBlockJobUpdateDisk.
Drop the propagation and refactor any caller that pased non-NULL error.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 12 ++---------- src/qemu/qemu_blockjob.h | 3 +-- src/qemu/qemu_driver.c | 6 +++--- src/qemu/qemu_migration.c | 28 +++++++++++----------------- 4 files changed, 17 insertions(+), 32 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

The processing function modifies the job state so it should make sure that the variable holding the new state is cleared properly and not the caller. The caller should only deal with the job state and not the transition that happened. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 7aaa439791..49e747ebbb 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -285,6 +285,9 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, job->state, job->newstate); + if (job->newstate == -1) + return; + qemuBlockJobEmitEvents(driver, vm, disk, job->type, job->newstate); /* If we completed a block pull or commit, then update the XML @@ -314,6 +317,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, } job->state = job->newstate; + job->newstate = -1; if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) VIR_WARN("Unable to save status on vm %s after block job", vm->def->name); @@ -346,14 +350,13 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, { qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; qemuDomainObjPrivatePtr priv = vm->privateData; - int state = job->newstate; - if (state != -1) { - qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); - job->newstate = -1; - } + if (job->newstate == -1) + return -1; + + qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); - return state; + return job->state; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:43PM +0100, Peter Krempa wrote:
The processing function modifies the job state so it should make sure that the variable holding the new state is cleared properly and not the caller. The caller should only deal with the job state and not the transition that happened.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Instead of passing in the disk information, pass in the job and name the function accordingly. Few callers needed to be modified to have the job pointer handy. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++++-------- src/qemu/qemu_blockjob.h | 6 +++--- src/qemu/qemu_driver.c | 6 +++--- src/qemu/qemu_migration.c | 40 +++++++++++++++++++++++++++------------ 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 49e747ebbb..c1826baa3c 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -333,7 +333,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, /** - * qemuBlockJobUpdateDisk: + * qemuBlockJobUpdate: * @vm: domain * @disk: domain disk * @error: error (output parameter) @@ -344,11 +344,10 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, * Returns the block job event processed or -1 if there was no pending event. */ int -qemuBlockJobUpdateDisk(virDomainObjPtr vm, - int asyncJob, - virDomainDiskDefPtr disk) +qemuBlockJobUpdate(virDomainObjPtr vm, + qemuBlockJobDataPtr job, + int asyncJob) { - qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; qemuDomainObjPrivatePtr priv = vm->privateData; if (job->newstate == -1) @@ -370,7 +369,7 @@ qemuBlockJobUpdateDisk(virDomainObjPtr vm, * * During a synchronous block job, a block job event for @disk * will not be processed asynchronously. Instead, it will be - * processed only when qemuBlockJobUpdateDisk or qemuBlockJobSyncEndDisk + * processed only when qemuBlockJobUpdate or qemuBlockJobSyncEndDisk * is called. */ void @@ -402,7 +401,12 @@ qemuBlockJobSyncEndDisk(virDomainObjPtr vm, int asyncJob, virDomainDiskDefPtr disk) { + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + if (!job) + return; + VIR_DEBUG("disk=%s", disk->dst); - qemuBlockJobUpdateDisk(vm, asyncJob, disk); - QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob->synchronous = false; + qemuBlockJobUpdate(vm, job, asyncJob); + job->synchronous = false; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 4527ee2a93..9dad47f732 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -95,9 +95,9 @@ qemuBlockJobIsRunning(qemuBlockJobDataPtr job) void qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job); -int qemuBlockJobUpdateDisk(virDomainObjPtr vm, - int asyncJob, - virDomainDiskDefPtr disk); +int qemuBlockJobUpdate(virDomainObjPtr vm, + qemuBlockJobDataPtr job, + int asyncJob); void qemuBlockJobSyncBegin(qemuBlockJobDataPtr job); void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2e47ec021a..4ffa5b573d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4739,7 +4739,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, job->newstate = status; - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); endjob: qemuBlockJobStartupFinalize(job); @@ -17397,13 +17397,13 @@ qemuDomainBlockJobAbort(virDomainPtr dom, * do the waiting while still holding the VM job, to prevent newly * scheduled block jobs from confusing us. */ if (!async) { - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); while (qemuBlockJobIsRunning(job)) { if (virDomainObjWait(vm) < 0) { ret = -1; goto endjob; } - qemuBlockJobUpdateDisk(vm, QEMU_ASYNC_JOB_NONE, disk); + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); } } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index ac3c609067..92fcfa6278 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -464,17 +464,16 @@ qemuMigrationDstStopNBDServer(virQEMUDriverPtr driver, static void -qemuMigrationNBDReportMirrorError(virDomainDiskDefPtr disk) +qemuMigrationNBDReportMirrorError(qemuBlockJobDataPtr job, + const char *diskdst) { - qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; - if (job->errmsg) { virReportError(VIR_ERR_OPERATION_FAILED, _("migration of disk %s failed: %s"), - disk->dst, job->errmsg); + diskdst, job->errmsg); } else { virReportError(VIR_ERR_OPERATION_FAILED, - _("migration of disk %s failed"), disk->dst); + _("migration of disk %s failed"), diskdst); } } @@ -501,16 +500,26 @@ qemuMigrationSrcNBDStorageCopyReady(virDomainObjPtr vm, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job; if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); + if (!(job = qemuBlockJobDiskGetJob(disk))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing block job data for disk '%s'"), disk->dst); + return -1; + } + + status = qemuBlockJobUpdate(vm, job, asyncJob); if (status == VIR_DOMAIN_BLOCK_JOB_FAILED) { - qemuMigrationNBDReportMirrorError(disk); + qemuMigrationNBDReportMirrorError(job, disk->dst); + virObjectUnref(job); return -1; } + virObjectUnref(job); + if (disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_READY) notReady++; } @@ -550,15 +559,19 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk); + qemuBlockJobDataPtr job; if (!diskPriv->migrating) continue; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); + if (!(job = qemuBlockJobDiskGetJob(disk))) + continue; + + status = qemuBlockJobUpdate(vm, job, asyncJob); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: if (check) { - qemuMigrationNBDReportMirrorError(disk); + qemuMigrationNBDReportMirrorError(job, disk->dst); failed = true; } ATTRIBUTE_FALLTHROUGH; @@ -574,6 +587,8 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, if (status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) completed++; + + virObjectUnref(job); } /* Updating completed block job drops the lock thus we have to recheck @@ -616,6 +631,7 @@ static int qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, + qemuBlockJobDataPtr job, bool failNoJob, qemuDomainAsyncJob asyncJob) { @@ -625,12 +641,12 @@ qemuMigrationSrcNBDCopyCancelOne(virQEMUDriverPtr driver, int status; int rv; - status = qemuBlockJobUpdateDisk(vm, asyncJob, disk); + status = qemuBlockJobUpdate(vm, job, asyncJob); switch (status) { case VIR_DOMAIN_BLOCK_JOB_FAILED: case VIR_DOMAIN_BLOCK_JOB_CANCELED: if (failNoJob) { - qemuMigrationNBDReportMirrorError(disk); + qemuMigrationNBDReportMirrorError(job, disk->dst); goto cleanup; } ATTRIBUTE_FALLTHROUGH; @@ -698,7 +714,7 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, if (!diskPriv->migrating) continue; - rv = qemuMigrationSrcNBDCopyCancelOne(driver, vm, disk, + rv = qemuMigrationSrcNBDCopyCancelOne(driver, vm, disk, job, check, asyncJob); if (rv != 0) { if (rv < 0) { -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:44PM +0100, Peter Krempa wrote:
Instead of passing in the disk information, pass in the job and name the function accordingly.
Few callers needed to be modified to have the job pointer handy.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++++-------- src/qemu/qemu_blockjob.h | 6 +++--- src/qemu/qemu_driver.c | 6 +++--- src/qemu/qemu_migration.c | 40 +++++++++++++++++++++++++++------------ 4 files changed, 46 insertions(+), 26 deletions(-)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 49e747ebbb..c1826baa3c 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -333,7 +333,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver,
/** - * qemuBlockJobUpdateDisk: + * qemuBlockJobUpdate: * @vm: domain * @disk: domain disk
Again, the documentation is out of date.
* @error: error (output parameter)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Rather than storing the presence of the blockjob in a flag we can bind together the lifecycle of the job with the lifecycle of the object which is tracking the data for it. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 76 +++++++++++++++++++++++++++++----------- src/qemu/qemu_blockjob.h | 1 - src/qemu/qemu_domain.c | 5 --- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index c1826baa3c..87dc520f2c 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -66,23 +66,22 @@ qemuBlockJobDataOnceInit(void) VIR_ONCE_GLOBAL_INIT(qemuBlockJobData) -qemuBlockJobDataPtr -qemuBlockJobDataNew(void) +static qemuBlockJobDataPtr +qemuBlockJobDataNew(qemuBlockjobType type) { + qemuBlockJobDataPtr job = NULL; + if (qemuBlockJobDataInitialize() < 0) return NULL; - return virObjectNew(qemuBlockJobDataClass); -} - + if (!(job = virObjectNew(qemuBlockJobDataClass))) + return NULL; -static void -qemuBlockJobDataReset(qemuBlockJobDataPtr job) -{ - job->type = -1; + job->state = QEMU_BLOCKJOB_STATE_NEW; job->newstate = -1; - VIR_FREE(job->errmsg); - job->synchronous = false; + job->type = type; + + return job; } @@ -98,15 +97,16 @@ qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk, qemuBlockjobType type) { - qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; - job->disk = disk; + qemuBlockJobDataPtr job = NULL; - qemuBlockJobDataReset(job); + if (!(job = qemuBlockJobDataNew(type))) + return NULL; - job->state = QEMU_BLOCKJOB_STATE_NEW; - job->type = type; + job->disk = disk; + if (disk) + QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job); - return virObjectRef(job); + return job; } @@ -141,6 +141,22 @@ qemuBlockJobStarted(qemuBlockJobDataPtr job) } +static void +qemuBlockJobTerminate(qemuBlockJobDataPtr job) +{ + qemuDomainDiskPrivatePtr diskPriv; + + if (job->disk) { + diskPriv = QEMU_DOMAIN_DISK_PRIVATE(job->disk); + + if (job == diskPriv->blockjob) { + virObjectUnref(diskPriv->blockjob); + diskPriv->blockjob = NULL; + } + } +} + + /** * qemuBlockJobStartupFinalize: * @job: job being started @@ -156,7 +172,7 @@ qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job) return; if (job->state == QEMU_BLOCKJOB_STATE_NEW) - qemuBlockJobDataReset(job); + qemuBlockJobTerminate(job); virObjectUnref(job); } @@ -200,11 +216,15 @@ qemuBlockJobEmitEvents(virQEMUDriverPtr driver, static void qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, virDomainObjPtr vm, - virDomainDiskDefPtr disk, + qemuBlockJobDataPtr job, int asyncJob) { + virDomainDiskDefPtr disk = job->disk; virDomainDiskDefPtr persistDisk = NULL; + if (!disk) + return; + if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) { if (vm->newDef) { virStorageSourcePtr copy = NULL; @@ -256,6 +276,7 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, virStorageSourceBackingStoreClear(disk->src); ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); + qemuBlockJobTerminate(job); } @@ -294,7 +315,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, * to match. */ switch ((virConnectDomainEventBlockJobStatus) job->newstate) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: - qemuBlockJobEventProcessLegacyCompleted(driver, vm, disk, asyncJob); + qemuBlockJobEventProcessLegacyCompleted(driver, vm, job, asyncJob); break; case VIR_DOMAIN_BLOCK_JOB_READY: @@ -310,6 +331,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, } disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; + qemuBlockJobTerminate(job); break; case VIR_DOMAIN_BLOCK_JOB_LAST: @@ -333,7 +355,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, /** - * qemuBlockJobUpdate: + * qemuBlockJobUpdateDisk: * @vm: domain * @disk: domain disk * @error: error (output parameter) @@ -410,3 +432,15 @@ qemuBlockJobSyncEndDisk(virDomainObjPtr vm, qemuBlockJobUpdate(vm, job, asyncJob); job->synchronous = false; } + + +qemuBlockJobDataPtr +qemuBlockJobGetByDisk(virDomainDiskDefPtr disk) +{ + qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + + if (!job) + return NULL; + + return virObjectRef(job); +} diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 9dad47f732..8c567ec886 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -73,7 +73,6 @@ struct _qemuBlockJobData { int newstate; /* virConnectDomainEventBlockJobStatus - new state to be processed */ }; -qemuBlockJobDataPtr qemuBlockJobDataNew(void); qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 01b4c97a91..9961ba67d8 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1062,11 +1062,6 @@ qemuDomainDiskPrivateNew(void) if (!(priv = virObjectNew(qemuDomainDiskPrivateClass))) return NULL; - if (!(priv->blockjob = qemuBlockJobDataNew())) { - virObjectUnref(priv); - priv = NULL; - } - return (virObjectPtr) priv; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:45PM +0100, Peter Krempa wrote:
Rather than storing the presence of the blockjob in a flag we can bind together the lifecycle of the job with the lifecycle of the object which is tracking the data for it.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 76 +++++++++++++++++++++++++++++----------- src/qemu/qemu_blockjob.h | 1 - src/qemu/qemu_domain.c | 5 --- 3 files changed, 55 insertions(+), 27 deletions(-)
@@ -333,7 +355,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver,
/** - * qemuBlockJobUpdate: + * qemuBlockJobUpdateDisk:
Unrelated change.
* @vm: domain * @disk: domain disk * @error: error (output parameter)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Now that the data is per-job, we don't really need to bother with finishing the synchronous job handling if the job is already terminated. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 92fcfa6278..bed8eed563 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -577,7 +577,6 @@ qemuMigrationSrcNBDCopyCancelled(virDomainObjPtr vm, ATTRIBUTE_FALLTHROUGH; case VIR_DOMAIN_BLOCK_JOB_CANCELED: case VIR_DOMAIN_BLOCK_JOB_COMPLETED: - qemuBlockJobSyncEndDisk(vm, asyncJob, disk); diskPriv->migrating = false; break; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:46PM +0100, Peter Krempa wrote:
Now that the data is per-job, we don't really need to bother with finishing the synchronous job handling if the job is already terminated.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 1 - 1 file changed, 1 deletion(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

And rename it in accordance with the change. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++---------- src/qemu/qemu_blockjob.h | 6 +++--- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_migration.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 87dc520f2c..c98d393f4b 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -386,12 +386,12 @@ qemuBlockJobUpdate(virDomainObjPtr vm, * @disk: domain disk * * Begin a new synchronous block job for @disk. The synchronous - * block job is ended by a call to qemuBlockJobSyncEndDisk, or by + * block job is ended by a call to qemuBlockJobSyncEnd, or by * the guest quitting. * * During a synchronous block job, a block job event for @disk * will not be processed asynchronously. Instead, it will be - * processed only when qemuBlockJobUpdate or qemuBlockJobSyncEndDisk + * processed only when qemuBlockJobUpdate or qemuBlockJobSyncEnd * is called. */ void @@ -409,7 +409,7 @@ qemuBlockJobSyncBegin(qemuBlockJobDataPtr job) /** - * qemuBlockJobSyncEndDisk: + * qemuBlockJobSyncEnd: * @vm: domain * @disk: domain disk * @@ -419,16 +419,16 @@ qemuBlockJobSyncBegin(qemuBlockJobDataPtr job) * qemuBlockJobStartupFinalize will be called. */ void -qemuBlockJobSyncEndDisk(virDomainObjPtr vm, - int asyncJob, - virDomainDiskDefPtr disk) +qemuBlockJobSyncEnd(virDomainObjPtr vm, + qemuBlockJobDataPtr job, + int asyncJob) { - qemuBlockJobDataPtr job = QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob; + const char *diskdst = NULL; - if (!job) - return; + if (job->disk) + diskdst = job->disk->dst; - VIR_DEBUG("disk=%s", disk->dst); + VIR_DEBUG("disk=%s", NULLSTR(diskdst)); qemuBlockJobUpdate(vm, job, asyncJob); job->synchronous = false; } diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 8c567ec886..0ec9fd17b7 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -99,9 +99,9 @@ int qemuBlockJobUpdate(virDomainObjPtr vm, int asyncJob); void qemuBlockJobSyncBegin(qemuBlockJobDataPtr job); -void qemuBlockJobSyncEndDisk(virDomainObjPtr vm, - int asyncJob, - virDomainDiskDefPtr disk); +void qemuBlockJobSyncEnd(virDomainObjPtr vm, + qemuBlockJobDataPtr job, + int asyncJob); qemuBlockJobDataPtr qemuBlockJobGetByDisk(virDomainDiskDefPtr disk) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4ffa5b573d..ff113ae57b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17408,8 +17408,8 @@ qemuDomainBlockJobAbort(virDomainPtr dom, } endjob: - if (disk) - qemuBlockJobSyncEndDisk(vm, QEMU_ASYNC_JOB_NONE, disk); + if (job && !async) + qemuBlockJobSyncEnd(vm, job, QEMU_ASYNC_JOB_NONE); qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index bed8eed563..4a6f631689 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -721,7 +721,7 @@ qemuMigrationSrcNBDCopyCancel(virQEMUDriverPtr driver, err = virSaveLastError(); failed = true; } - qemuBlockJobSyncEndDisk(vm, asyncJob, disk); + qemuBlockJobSyncEnd(vm, job, asyncJob); diskPriv->migrating = false; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:47PM +0100, Peter Krempa wrote:
And rename it in accordance with the change.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++---------- src/qemu/qemu_blockjob.h | 6 +++--- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_migration.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Currently the job name corresponds to the disk the job belongs to. For jobs which will not correspond to disks we'll need to track the name separately. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++++++++---- src/qemu/qemu_blockjob.h | 7 +++++-- src/qemu/qemu_driver.c | 8 ++++---- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index c98d393f4b..27e854e2b2 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -36,6 +36,7 @@ #include "virtime.h" #include "locking/domain_lock.h" #include "viralloc.h" +#include "virstring.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -50,6 +51,7 @@ qemuBlockJobDataDispose(void *obj) { qemuBlockJobDataPtr job = obj; + VIR_FREE(job->name); VIR_FREE(job->errmsg); } @@ -67,9 +69,11 @@ qemuBlockJobDataOnceInit(void) VIR_ONCE_GLOBAL_INIT(qemuBlockJobData) static qemuBlockJobDataPtr -qemuBlockJobDataNew(qemuBlockjobType type) +qemuBlockJobDataNew(qemuBlockjobType type, + const char *name) { qemuBlockJobDataPtr job = NULL; + qemuBlockJobDataPtr ret = NULL; if (qemuBlockJobDataInitialize() < 0) return NULL; @@ -77,11 +81,18 @@ qemuBlockJobDataNew(qemuBlockjobType type) if (!(job = virObjectNew(qemuBlockJobDataClass))) return NULL; + if (VIR_STRDUP(job->name, name) < 0) + goto cleanup; + job->state = QEMU_BLOCKJOB_STATE_NEW; job->newstate = -1; job->type = type; - return job; + VIR_STEAL_PTR(ret, job); + + cleanup: + virObjectUnref(job); + return ret; } @@ -95,11 +106,12 @@ qemuBlockJobDataNew(qemuBlockjobType type) */ qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk, - qemuBlockjobType type) + qemuBlockjobType type, + const char *jobname) { qemuBlockJobDataPtr job = NULL; - if (!(job = qemuBlockJobDataNew(type))) + if (!(job = qemuBlockJobDataNew(type, jobname))) return NULL; job->disk = disk; diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 0ec9fd17b7..f67b0f39be 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -63,6 +63,8 @@ typedef qemuBlockJobData *qemuBlockJobDataPtr; struct _qemuBlockJobData { virObject parent; + char *name; + virDomainDiskDefPtr disk; /* may be NULL, if blockjob does not corrspond to any disk */ int type; /* qemuBlockjobType */ @@ -76,8 +78,9 @@ struct _qemuBlockJobData { qemuBlockJobDataPtr qemuBlockJobDiskNew(virDomainDiskDefPtr disk, - qemuBlockjobType type) - ATTRIBUTE_NONNULL(1); + qemuBlockjobType type, + const char *jobname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); qemuBlockJobDataPtr qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ff113ae57b..5675d2dc87 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4732,7 +4732,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, } if (!(job = qemuBlockJobDiskGetJob(disk))) { - if (!(job = qemuBlockJobDiskNew(disk, type))) + if (!(job = qemuBlockJobDiskNew(disk, type, diskAlias))) goto endjob; qemuBlockJobStarted(job); } @@ -17268,7 +17268,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, speed <<= 20; } - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_PULL))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_PULL, device))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); @@ -17803,7 +17803,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, goto endjob; } - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY, device))) goto endjob; /* Actually start the mirroring */ @@ -18217,7 +18217,7 @@ qemuDomainBlockCommit(virDomainPtr dom, jobtype = QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT; } - if (!(job = qemuBlockJobDiskNew(disk, jobtype))) + if (!(job = qemuBlockJobDiskNew(disk, jobtype, device))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4a6f631689..4ce3141465 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -914,7 +914,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) goto cleanup; - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY))) + if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY, diskAlias))) goto cleanup; qemuBlockJobSyncBegin(job); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1171da62a8..3f1dd662e9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7776,7 +7776,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload, disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) jobtype = disk->mirrorJob; - if (!(job = qemuBlockJobDiskNew(disk, jobtype))) + if (!(job = qemuBlockJobDiskNew(disk, jobtype, jobname))) return -1; qemuBlockJobStarted(job); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:48PM +0100, Peter Krempa wrote:
Currently the job name corresponds to the disk the job belongs to. For jobs which will not correspond to disks we'll need to track the name separately.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 20 ++++++++++++++++---- src/qemu/qemu_blockjob.h | 7 +++++-- src/qemu/qemu_driver.c | 8 ++++---- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Block jobs currently belong to disks only so we can look up the block job data for them in the corresponding disks. This won't be the case when using blockdev as certain jobs don't even correspond to a disk and most of them can run on a part of the backing chain. Add a global table of blockjobs which can be used to look up the data for the blockjobs when the job events need to be processed. The table is a hash table organized by job name and has a reference to the job. New and running jobs will later be added to this table. Reference counting will allow to reap job state for synchronous callers. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 7 +++++++ src/qemu/qemu_domain.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9961ba67d8..7a9a94efcf 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1906,6 +1906,9 @@ qemuDomainObjPrivateAlloc(void *opaque) if (!(priv->devs = virChrdevAlloc())) goto error; + if (!(priv->blockjobs = virHashCreate(5, virObjectFreeHashData))) + goto error; + priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX; priv->driver = opaque; @@ -1973,6 +1976,8 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivatePtr priv) qemuDomainObjResetJob(priv); qemuDomainObjResetAsyncJob(priv); + + virHashRemoveAll(priv->blockjobs); } @@ -2004,6 +2009,8 @@ qemuDomainObjPrivateFree(void *data) qemuDomainSecretInfoFree(&priv->migSecinfo); qemuDomainMasterKeyFree(priv); + virHashFree(priv->blockjobs); + VIR_FREE(priv); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index a03950e77b..a243bdc80e 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -373,6 +373,9 @@ struct _qemuDomainObjPrivate { /* true if libvirt remembers the original owner for files */ bool rememberOwner; + + /* running block jobs */ + virHashTablePtr blockjobs; }; # define QEMU_DOMAIN_PRIVATE(vm) \ -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:49PM +0100, Peter Krempa wrote:
Block jobs currently belong to disks only so we can look up the block job data for them in the corresponding disks. This won't be the case when using blockdev as certain jobs don't even correspond to a disk and most of them can run on a part of the backing chain.
Add a global table of blockjobs which can be used to look up the data for the blockjobs when the job events need to be processed.
The table is a hash table organized by job name and has a reference to the job. New and running jobs will later be added to this table. Reference counting will allow to reap job state for synchronous callers.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 7 +++++++ src/qemu/qemu_domain.h | 3 +++ 2 files changed, 10 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Add the job structure to the table when instantiating a new job and remove it when it terminates/fails. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 31 ++++++++++++++++++++++++------- src/qemu/qemu_blockjob.h | 6 ++++-- src/qemu/qemu_driver.c | 16 ++++++++-------- src/qemu/qemu_migration.c | 4 ++-- src/qemu/qemu_process.c | 6 +++--- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 27e854e2b2..97b9d7e5a6 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -105,20 +105,32 @@ qemuBlockJobDataNew(qemuBlockjobType type, * Returns 0 on success and -1 on failure. */ qemuBlockJobDataPtr -qemuBlockJobDiskNew(virDomainDiskDefPtr disk, +qemuBlockJobDiskNew(virDomainObjPtr vm, + virDomainDiskDefPtr disk, qemuBlockjobType type, const char *jobname) { + qemuDomainObjPrivatePtr priv = vm->privateData; qemuBlockJobDataPtr job = NULL; + qemuBlockJobDataPtr ret = NULL; if (!(job = qemuBlockJobDataNew(type, jobname))) return NULL; + if (virHashAddEntry(priv->blockjobs, jobname, virObjectRef(job)) < 0) { + virObjectUnref(job); + goto cleanup; + } + job->disk = disk; if (disk) QEMU_DOMAIN_DISK_PRIVATE(disk)->blockjob = virObjectRef(job); - return job; + VIR_STEAL_PTR(ret, job); + + cleanup: + virObjectUnref(job); + return ret; } @@ -154,10 +166,14 @@ qemuBlockJobStarted(qemuBlockJobDataPtr job) static void -qemuBlockJobTerminate(qemuBlockJobDataPtr job) +qemuBlockJobTerminate(virDomainObjPtr vm, + qemuBlockJobDataPtr job) { + qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainDiskPrivatePtr diskPriv; + virHashRemoveEntry(priv->blockjobs, job->name); + if (job->disk) { diskPriv = QEMU_DOMAIN_DISK_PRIVATE(job->disk); @@ -178,13 +194,14 @@ qemuBlockJobTerminate(qemuBlockJobDataPtr job) * to @job if it was started. */ void -qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job) +qemuBlockJobStartupFinalize(virDomainObjPtr vm, + qemuBlockJobDataPtr job) { if (!job) return; if (job->state == QEMU_BLOCKJOB_STATE_NEW) - qemuBlockJobTerminate(job); + qemuBlockJobTerminate(vm, job); virObjectUnref(job); } @@ -288,7 +305,7 @@ qemuBlockJobEventProcessLegacyCompleted(virQEMUDriverPtr driver, virStorageSourceBackingStoreClear(disk->src); ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk, true)); ignore_value(qemuBlockNodeNamesDetect(driver, vm, asyncJob)); - qemuBlockJobTerminate(job); + qemuBlockJobTerminate(vm, job); } @@ -343,7 +360,7 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, } disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE; disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; - qemuBlockJobTerminate(job); + qemuBlockJobTerminate(vm, job); break; case VIR_DOMAIN_BLOCK_JOB_LAST: diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index f67b0f39be..6f58d323d0 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -77,7 +77,8 @@ struct _qemuBlockJobData { qemuBlockJobDataPtr -qemuBlockJobDiskNew(virDomainDiskDefPtr disk, +qemuBlockJobDiskNew(virDomainObjPtr vm, + virDomainDiskDefPtr disk, qemuBlockjobType type, const char *jobname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); @@ -95,7 +96,8 @@ qemuBlockJobIsRunning(qemuBlockJobDataPtr job) ATTRIBUTE_NONNULL(1); void -qemuBlockJobStartupFinalize(qemuBlockJobDataPtr job); +qemuBlockJobStartupFinalize(virDomainObjPtr vm, + qemuBlockJobDataPtr job); int qemuBlockJobUpdate(virDomainObjPtr vm, qemuBlockJobDataPtr job, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5675d2dc87..1b31132807 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4732,7 +4732,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, } if (!(job = qemuBlockJobDiskGetJob(disk))) { - if (!(job = qemuBlockJobDiskNew(disk, type, diskAlias))) + if (!(job = qemuBlockJobDiskNew(vm, disk, type, diskAlias))) goto endjob; qemuBlockJobStarted(job); } @@ -4742,7 +4742,7 @@ processBlockJobEvent(virQEMUDriverPtr driver, qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); endjob: - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); qemuDomainObjEndJob(driver, vm); } @@ -17268,7 +17268,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, speed <<= 20; } - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_PULL, device))) + if (!(job = qemuBlockJobDiskNew(vm, disk, QEMU_BLOCKJOB_TYPE_PULL, device))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); @@ -17294,7 +17294,7 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, qemuDomainObjEndJob(driver, vm); cleanup: - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); virObjectUnref(cfg); VIR_FREE(basePath); VIR_FREE(backingPath); @@ -17803,7 +17803,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, goto endjob; } - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY, device))) + if (!(job = qemuBlockJobDiskNew(vm, disk, QEMU_BLOCKJOB_TYPE_COPY, device))) goto endjob; /* Actually start the mirroring */ @@ -17842,7 +17842,7 @@ qemuDomainBlockCopyCommon(virDomainObjPtr vm, virSetError(monitor_error); virFreeError(monitor_error); } - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); cleanup: VIR_FREE(device); @@ -18217,7 +18217,7 @@ qemuDomainBlockCommit(virDomainPtr dom, jobtype = QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT; } - if (!(job = qemuBlockJobDiskNew(disk, jobtype, device))) + if (!(job = qemuBlockJobDiskNew(vm, disk, jobtype, device))) goto endjob; qemuDomainObjEnterMonitor(driver, vm); @@ -18260,7 +18260,7 @@ qemuDomainBlockCommit(virDomainPtr dom, } } virStorageSourceFree(mirror); - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4ce3141465..87eeec3d84 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -914,7 +914,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, if (!(diskAlias = qemuAliasDiskDriveFromDisk(disk))) goto cleanup; - if (!(job = qemuBlockJobDiskNew(disk, QEMU_BLOCKJOB_TYPE_COPY, diskAlias))) + if (!(job = qemuBlockJobDiskNew(vm, disk, QEMU_BLOCKJOB_TYPE_COPY, diskAlias))) goto cleanup; qemuBlockJobSyncBegin(job); @@ -943,7 +943,7 @@ qemuMigrationSrcNBDStorageCopyOne(virQEMUDriverPtr driver, ret = 0; cleanup: - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); VIR_FREE(diskAlias); return ret; } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3f1dd662e9..4b57411856 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -967,7 +967,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } cleanup: - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); qemuProcessEventFree(processEvent); virObjectUnlock(vm); return 0; @@ -7776,7 +7776,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload, disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT) jobtype = disk->mirrorJob; - if (!(job = qemuBlockJobDiskNew(disk, jobtype, jobname))) + if (!(job = qemuBlockJobDiskNew(vm, disk, jobtype, jobname))) return -1; qemuBlockJobStarted(job); @@ -7789,7 +7789,7 @@ qemuProcessRefreshLegacyBlockjob(void *payload, } } - qemuBlockJobStartupFinalize(job); + qemuBlockJobStartupFinalize(vm, job); return 0; } -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:50PM +0100, Peter Krempa wrote:
Add the job structure to the table when instantiating a new job and remove it when it terminates/fails.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 31 ++++++++++++++++++++++++------- src/qemu/qemu_blockjob.h | 6 ++++-- src/qemu/qemu_driver.c | 16 ++++++++-------- src/qemu/qemu_migration.c | 4 ++-- src/qemu/qemu_process.c | 6 +++--- 5 files changed, 41 insertions(+), 22 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Later on we'll format these values into the status XML so the from/to string functions will come handy. The implementation also notes that these will be used in the status XML to avoid somebody changing the values. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 7 +++++++ src/qemu/qemu_blockjob.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 97b9d7e5a6..24a0efe36f 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -42,6 +42,13 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); +/* Note that qemuBlockjobState and qemuBlockjobType values are formatted into + * the status XML */ +VIR_ENUM_IMPL(qemuBlockjobState, QEMU_BLOCKJOB_STATE_LAST, + "completed", "failed", "cancelled", "ready", "new", "running"); + +VIR_ENUM_IMPL(qemuBlockjob, QEMU_BLOCKJOB_TYPE_LAST, + "", "pull", "copy", "commit", "active-commit", ""); static virClassPtr qemuBlockJobDataClass; diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 6f58d323d0..d1e94dc066 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -41,6 +41,8 @@ typedef enum { } qemuBlockjobState; verify((int)QEMU_BLOCKJOB_STATE_NEW == VIR_DOMAIN_BLOCK_JOB_LAST); +VIR_ENUM_DECL(qemuBlockjobState) + /** * This enum has to map all known block job types from enum virDomainBlockJobType * to the same values. All internal blockjobs can be mapped after and don't @@ -57,6 +59,8 @@ typedef enum { } qemuBlockjobType; verify((int)QEMU_BLOCKJOB_TYPE_INTERNAL == VIR_DOMAIN_BLOCK_JOB_TYPE_LAST); +VIR_ENUM_DECL(qemuBlockjob) + typedef struct _qemuBlockJobData qemuBlockJobData; typedef qemuBlockJobData *qemuBlockJobDataPtr; -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:51PM +0100, Peter Krempa wrote:
Later on we'll format these values into the status XML so the from/to string functions will come handy. The implementation also notes that these will be used in the status XML to avoid somebody changing the values.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 7 +++++++ src/qemu/qemu_blockjob.h | 4 ++++ 2 files changed, 11 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

We need to store the block job state in the status XML so that we can properly recover any data when reconnecting after startup and also in the end to be able to do any transition of the backing chain that happened while libvirt was not connected to the monitor. First step is to note the name, type, state and corresponding disk into the status XML. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 105 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7a9a94efcf..7845889dec 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -35,6 +35,7 @@ #include "qemu_migration_params.h" #include "qemu_security.h" #include "qemu_extdevice.h" +#include "qemu_blockjob.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" @@ -2235,17 +2236,47 @@ qemuDomainObjPrivateXMLFormatAutomaticPlacement(virBufferPtr buf, } +static int +qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload, + const void *name ATTRIBUTE_UNUSED, + void *data) +{ + virBuffer attrBuf = VIR_BUFFER_INITIALIZER; + virBuffer childBuf = VIR_BUFFER_INITIALIZER; + qemuBlockJobDataPtr job = payload; + virBufferPtr buf = data; + + virBufferSetChildIndent(&childBuf, buf); + + virBufferAsprintf(&attrBuf, " name='%s'", job->name); + virBufferAsprintf(&attrBuf, " type='%s'", qemuBlockjobTypeToString(job->type)); + virBufferAsprintf(&attrBuf, " state='%s'", qemuBlockjobStateTypeToString(job->state)); + + if (job->disk) + virBufferAsprintf(&childBuf, "<disk dst='%s'/>\n", job->disk->dst); + + return virXMLFormatElement(buf, "blockjob", &attrBuf, &childBuf); +} + + static int qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData; virBuffer attrBuf = VIR_BUFFER_INITIALIZER; + virBuffer childBuf = VIR_BUFFER_INITIALIZER; bool bj = qemuDomainHasBlockjob(vm, false); virBufferAsprintf(&attrBuf, " active='%s'", virTristateBoolTypeToString(virTristateBoolFromBool(bj))); - return virXMLFormatElement(buf, "blockjobs", &attrBuf, NULL); + virBufferSetChildIndent(&childBuf, buf); + + virHashForEach(priv->blockjobs, + qemuDomainObjPrivateXMLFormatBlockjobIterator, &childBuf); + + return virXMLFormatElement(buf, "blockjobs", &attrBuf, &childBuf); } @@ -2599,18 +2630,84 @@ qemuDomainObjPrivateXMLParseAutomaticPlacement(xmlXPathContextPtr ctxt, static int -qemuDomainObjPrivateXMLParseBlockjobs(qemuDomainObjPrivatePtr priv, +qemuDomainObjPrivateXMLParseBlockjobData(virDomainObjPtr vm, + xmlNodePtr node, + xmlXPathContextPtr ctxt) +{ + xmlNodePtr save_node = ctxt->node; + virDomainDiskDefPtr disk = NULL; + qemuBlockJobDataPtr job = NULL; + char *name = NULL; + char *typestr = NULL; + int type; + char *statestr = NULL; + int state; + char *diskdst = NULL; + int ret = -1; + + ctxt->node = node; + + if (!(name = virXPathString("string(./@name)", ctxt)) || + !(typestr = virXPathString("string(./@type)", ctxt)) || + !(statestr = virXPathString("string(./@state)", ctxt)) || + (type = qemuBlockjobTypeFromString(typestr)) < 0 || + (state = qemuBlockjobStateTypeFromString(statestr)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed block job data")); + goto cleanup; + } + + if ((diskdst = virXPathString("string(./disk/@dst)", ctxt))) + disk = virDomainDiskByName(vm->def, diskdst, false); + + if (!(job = qemuBlockJobDiskNew(vm, disk, type, name))) + goto cleanup; + + job->state = state; + + ret = 0; + + cleanup: + virObjectUnref(job); + VIR_FREE(name); + VIR_FREE(typestr); + VIR_FREE(statestr); + VIR_FREE(diskdst); + ctxt->node = save_node; + return ret; +} + + +static int +qemuDomainObjPrivateXMLParseBlockjobs(virDomainObjPtr vm, + qemuDomainObjPrivatePtr priv, xmlXPathContextPtr ctxt) { + xmlNodePtr *nodes = NULL; + ssize_t nnodes = 0; char *active; int tmp; + size_t i; + int ret = -1; if ((active = virXPathString("string(./blockjobs/@active)", ctxt)) && (tmp = virTristateBoolTypeFromString(active)) > 0) priv->reconnectBlockjobs = tmp; + if ((nnodes = virXPathNodeSet("./blockjobs/blockjob", ctxt, &nodes)) < 0) + goto cleanup; + + for (i = 0; i < nnodes; i++) { + if (qemuDomainObjPrivateXMLParseBlockjobData(vm, nodes[i], ctxt) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: VIR_FREE(active); - return 0; + VIR_FREE(nodes); + return ret; } @@ -2989,7 +3086,7 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, qemuDomainObjPrivateXMLParsePR(ctxt, &priv->prDaemonRunning); - if (qemuDomainObjPrivateXMLParseBlockjobs(priv, ctxt) < 0) + if (qemuDomainObjPrivateXMLParseBlockjobs(vm, priv, ctxt) < 0) goto error; qemuDomainStorageIdReset(priv); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:52PM +0100, Peter Krempa wrote:
We need to store the block job state in the status XML so that we can properly recover any data when reconnecting after startup and also in the end to be able to do any transition of the backing chain that happened while libvirt was not connected to the monitor.
First step is to note the name, type, state and corresponding disk into the status XML.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 105 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- .../blockjob-blockdev-in.xml | 364 ++++++++++++++++++ .../blockjob-blockdev-out.xml | 1 + tests/qemuxml2xmltest.c | 2 + 3 files changed, 367 insertions(+) create mode 100644 tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml create mode 120000 tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml diff --git a/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml new file mode 100644 index 0000000000..23d2a209f3 --- /dev/null +++ b/tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml @@ -0,0 +1,364 @@ +<domstatus state='running' reason='booted' pid='7690'> + <taint flag='high-privileges'/> + <monitor path='/var/lib/libvirt/qemu/domain-4-copy/monitor.sock' json='1' type='unix'/> + <namespaces> + <mount/> + </namespaces> + <vcpus> + <vcpu id='0' pid='7696'/> + <vcpu id='1' pid='7697'/> + </vcpus> + <qemuCaps> + <flag name='kvm'/> + <flag name='no-hpet'/> + <flag name='spice'/> + <flag name='hda-duplex'/> + <flag name='ccid-emulated'/> + <flag name='ccid-passthru'/> + <flag name='virtio-tx-alg'/> + <flag name='virtio-blk-pci.ioeventfd'/> + <flag name='sga'/> + <flag name='virtio-blk-pci.event_idx'/> + <flag name='virtio-net-pci.event_idx'/> + <flag name='piix3-usb-uhci'/> + <flag name='piix4-usb-uhci'/> + <flag name='usb-ehci'/> + <flag name='ich9-usb-ehci1'/> + <flag name='vt82c686b-usb-uhci'/> + <flag name='pci-ohci'/> + <flag name='usb-redir'/> + <flag name='usb-hub'/> + <flag name='ich9-ahci'/> + <flag name='no-acpi'/> + <flag name='virtio-blk-pci.scsi'/> + <flag name='scsi-disk.channel'/> + <flag name='scsi-block'/> + <flag name='transaction'/> + <flag name='block-job-async'/> + <flag name='scsi-cd'/> + <flag name='ide-cd'/> + <flag name='hda-micro'/> + <flag name='dump-guest-memory'/> + <flag name='nec-usb-xhci'/> + <flag name='balloon-event'/> + <flag name='lsi'/> + <flag name='virtio-scsi-pci'/> + <flag name='blockio'/> + <flag name='disable-s3'/> + <flag name='disable-s4'/> + <flag name='usb-redir.filter'/> + <flag name='ide-drive.wwn'/> + <flag name='scsi-disk.wwn'/> + <flag name='seccomp-sandbox'/> + <flag name='reboot-timeout'/> + <flag name='seamless-migration'/> + <flag name='block-commit'/> + <flag name='vnc'/> + <flag name='drive-mirror'/> + <flag name='blockdev-snapshot-sync'/> + <flag name='qxl'/> + <flag name='VGA'/> + <flag name='cirrus-vga'/> + <flag name='vmware-svga'/> + <flag name='device-video-primary'/> + <flag name='usb-serial'/> + <flag name='nbd-server'/> + <flag name='virtio-rng'/> + <flag name='rng-random'/> + <flag name='rng-egd'/> + <flag name='megasas'/> + <flag name='tpm-passthrough'/> + <flag name='tpm-tis'/> + <flag name='pci-bridge'/> + <flag name='vfio-pci'/> + <flag name='mem-merge'/> + <flag name='drive-discard'/> + <flag name='mlock'/> + <flag name='device-del-event'/> + <flag name='dmi-to-pci-bridge'/> + <flag name='i440fx-pci-hole64-size'/> + <flag name='q35-pci-hole64-size'/> + <flag name='usb-storage'/> + <flag name='usb-storage.removable'/> + <flag name='ich9-intel-hda'/> + <flag name='kvm-pit-lost-tick-policy'/> + <flag name='boot-strict'/> + <flag name='pvpanic'/> + <flag name='spice-file-xfer-disable'/> + <flag name='usb-kbd'/> + <flag name='msg-timestamp'/> + <flag name='active-commit'/> + <flag name='change-backing-file'/> + <flag name='memory-backend-ram'/> + <flag name='numa'/> + <flag name='memory-backend-file'/> + <flag name='usb-audio'/> + <flag name='rtc-reset-reinjection'/> + <flag name='splash-timeout'/> + <flag name='iothread'/> + <flag name='migrate-rdma'/> + <flag name='ivshmem'/> + <flag name='drive-iotune-max'/> + <flag name='VGA.vgamem_mb'/> + <flag name='vmware-svga.vgamem_mb'/> + <flag name='qxl.vgamem_mb'/> + <flag name='pc-dimm'/> + <flag name='machine-vmport-opt'/> + <flag name='aes-key-wrap'/> + <flag name='dea-key-wrap'/> + <flag name='pci-serial'/> + <flag name='vhost-user-multiqueue'/> + <flag name='migration-event'/> + <flag name='ioh3420'/> + <flag name='x3130-upstream'/> + <flag name='xio3130-downstream'/> + <flag name='rtl8139'/> + <flag name='e1000'/> + <flag name='virtio-net'/> + <flag name='gic-version'/> + <flag name='incoming-defer'/> + <flag name='virtio-gpu'/> + <flag name='virtio-gpu.virgl'/> + <flag name='virtio-keyboard'/> + <flag name='virtio-mouse'/> + <flag name='virtio-tablet'/> + <flag name='virtio-input-host'/> + <flag name='chardev-file-append'/> + <flag name='ich9-disable-s3'/> + <flag name='ich9-disable-s4'/> + <flag name='vserport-change-event'/> + <flag name='virtio-balloon-pci.deflate-on-oom'/> + <flag name='mptsas1068'/> + <flag name='spice-gl'/> + <flag name='qxl.vram64_size_mb'/> + <flag name='chardev-logfile'/> + <flag name='debug-threads'/> + <flag name='secret'/> + <flag name='pxb'/> + <flag name='pxb-pcie'/> + <flag name='device-tray-moved-event'/> + <flag name='nec-usb-xhci-ports'/> + <flag name='virtio-scsi-pci.iothread'/> + <flag name='name-guest'/> + <flag name='qxl.max_outputs'/> + <flag name='spice-unix'/> + <flag name='drive-detect-zeroes'/> + <flag name='tls-creds-x509'/> + <flag name='intel-iommu'/> + <flag name='smm'/> + <flag name='virtio-pci-disable-legacy'/> + <flag name='query-hotpluggable-cpus'/> + <flag name='virtio-net.rx_queue_size'/> + <flag name='virtio-vga'/> + <flag name='drive-iotune-max-length'/> + <flag name='ivshmem-plain'/> + <flag name='ivshmem-doorbell'/> + <flag name='query-qmp-schema'/> + <flag name='gluster.debug_level'/> + <flag name='vhost-scsi'/> + <flag name='drive-iotune-group'/> + <flag name='query-cpu-model-expansion'/> + <flag name='virtio-net.host_mtu'/> + <flag name='spice-rendernode'/> + <flag name='nvdimm'/> + <flag name='pcie-root-port'/> + <flag name='query-cpu-definitions'/> + <flag name='block-write-threshold'/> + <flag name='query-named-block-nodes'/> + <flag name='cpu-cache'/> + <flag name='qemu-xhci'/> + <flag name='kernel-irqchip'/> + <flag name='kernel-irqchip.split'/> + <flag name='intel-iommu.intremap'/> + <flag name='intel-iommu.caching-mode'/> + <flag name='intel-iommu.eim'/> + <flag name='intel-iommu.device-iotlb'/> + <flag name='virtio.iommu_platform'/> + <flag name='virtio.ats'/> + <flag name='loadparm'/> + <flag name='vnc-multi-servers'/> + <flag name='virtio-net.tx_queue_size'/> + <flag name='chardev-reconnect'/> + <flag name='virtio-gpu.max_outputs'/> + <flag name='vxhs'/> + <flag name='virtio-blk.num-queues'/> + <flag name='vmcoreinfo'/> + <flag name='numa.dist'/> + <flag name='disk-share-rw'/> + <flag name='iscsi.password-secret'/> + <flag name='isa-serial'/> + <flag name='dump-completed'/> + <flag name='qcow2-luks'/> + <flag name='pcie-pci-bridge'/> + <flag name='seccomp-blacklist'/> + <flag name='query-cpus-fast'/> + <flag name='disk-write-cache'/> + <flag name='nbd-tls'/> + <flag name='tpm-crb'/> + <flag name='pr-manager-helper'/> + <flag name='qom-list-properties'/> + <flag name='memory-backend-file.discard-data'/> + <flag name='sdl-gl'/> + <flag name='screendump_device'/> + <flag name='hda-output'/> + <flag name='blockdev-del'/> + <flag name='vmgenid'/> + <flag name='vhost-vsock'/> + <flag name='chardev-fd-pass'/> + <flag name='tpm-emulator'/> + <flag name='mch'/> + <flag name='mch.extended-tseg-mbytes'/> + <flag name='usb-storage.werror'/> + <flag name='egl-headless'/> + <flag name='vfio-pci.display'/> + <flag name='memory-backend-memfd'/> + <flag name='memory-backend-memfd.hugetlb'/> + <flag name='iothread.poll-max-ns'/> + <flag name='egl-headless.rendernode'/> + </qemuCaps> + <devices> + <device alias='rng0'/> + <device alias='sound0-codec0'/> + <device alias='virtio-disk0'/> + <device alias='virtio-serial0'/> + <device alias='video0'/> + <device alias='serial0'/> + <device alias='sound0'/> + <device alias='channel1'/> + <device alias='channel0'/> + <device alias='usb'/> + </devices> + <numad nodeset='0' cpuset='0-7'/> + <libDir path='/var/lib/libvirt/qemu/domain-4-copy'/> + <channelTargetDir path='/var/lib/libvirt/qemu/channel/target/domain-4-copy'/> + <chardevStdioLogd/> + <allowReboot value='yes'/> + <blockjobs active='yes'> + <blockjob name='drive-virtio-disk0' type='copy' state='ready'> + <disk dst='vda'/> + </blockjob> + </blockjobs> + <domain type='kvm' id='4'> + <name>copy</name> + <uuid>0439a4a8-db56-4933-9183-d8681d7b0746</uuid> + <memory unit='KiB'>1024000</memory> + <currentMemory unit='KiB'>1024000</currentMemory> + <vcpu placement='auto' current='2'>8</vcpu> + <numatune> + <memory mode='strict' placement='auto'/> + </numatune> + <resource> + <partition>/machine</partition> + </resource> + <os> + <type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type> + <boot dev='hd'/> + <bootmenu enable='yes'/> + </os> + <features> + <acpi/> + <apic/> + <vmport state='off'/> + </features> + <cpu> + <numa> + <cell id='0' cpus='0,2,4,6' memory='512000' unit='KiB'/> + <cell id='1' cpus='1,3,5,7' memory='512000' unit='KiB'/> + </numa> + </cpu> + <clock offset='utc'> + <timer name='rtc' tickpolicy='catchup'/> + <timer name='pit' tickpolicy='delay'/> + <timer name='hpet' present='no'/> + </clock> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <pm> + <suspend-to-mem enabled='no'/> + <suspend-to-disk enabled='no'/> + </pm> + <devices> + <emulator>/home/pipo/git/qemu.git/x86_64-softmmu/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/tmp/src'/> + <backingStore/> + <mirror type='file' file='/tmp/copy' format='qcow2' job='copy' ready='yes'> + <format type='qcow2'/> + <source file='/tmp/copy'/> + </mirror> + <target dev='vda' bus='virtio'/> + <alias name='virtio-disk0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci.0'/> + </controller> + <controller type='virtio-serial' index='0'> + <alias name='virtio-serial0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </controller> + <serial type='pty'> + <source path='/dev/pts/34'/> + <target type='isa-serial' port='0'> + <model name='isa-serial'/> + </target> + <alias name='serial0'/> + </serial> + <console type='pty' tty='/dev/pts/34'> + <source path='/dev/pts/34'/> + <target type='serial' port='0'/> + <alias name='serial0'/> + </console> + <channel type='unix'> + <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-4-copy/org.qemu.guest_agent.0'/> + <target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/> + <alias name='channel0'/> + <address type='virtio-serial' controller='0' bus='0' port='1'/> + </channel> + <channel type='spicevmc'> + <target type='virtio' name='com.redhat.spice.0' state='disconnected'/> + <alias name='channel1'/> + <address type='virtio-serial' controller='0' bus='0' port='2'/> + </channel> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1' fromConfig='1' autoGenerated='no'/> + <image compression='off'/> + </graphics> + <sound model='ich6'> + <alias name='sound0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </sound> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> + <alias name='video0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <memballoon model='none'/> + <rng model='virtio'> + <backend model='random'>/dev/random</backend> + <alias name='rng0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </rng> + </devices> + <seclabel type='dynamic' model='selinux' relabel='yes'> + <label>unconfined_u:unconfined_r:svirt_t:s0:c550,c786</label> + <imagelabel>unconfined_u:object_r:svirt_image_t:s0:c550,c786</imagelabel> + </seclabel> + <seclabel type='dynamic' model='dac' relabel='yes'> + <label>+0:+0</label> + <imagelabel>+0:+0</imagelabel> + </seclabel> + </domain> +</domstatus> diff --git a/tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml b/tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml new file mode 120000 index 0000000000..cdca6e4a8b --- /dev/null +++ b/tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml @@ -0,0 +1 @@ +blockjob-blockdev-in.xml \ No newline at end of file diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 1062deee37..da9469b263 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -1252,6 +1252,8 @@ mymain(void) DO_TEST_STATUS("migration-out-nbd-tls"); DO_TEST_STATUS("disk-secinfo-upgrade"); + DO_TEST_STATUS("blockjob-blockdev"); + DO_TEST("vhost-vsock", QEMU_CAPS_DEVICE_VHOST_VSOCK); DO_TEST("vhost-vsock-auto", QEMU_CAPS_DEVICE_VHOST_VSOCK); DO_TEST("vhost-vsock-ccw", QEMU_CAPS_DEVICE_VHOST_VSOCK, -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:53PM +0100, Peter Krempa wrote:
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- .../blockjob-blockdev-in.xml | 364 ++++++++++++++++++ .../blockjob-blockdev-out.xml | 1 + tests/qemuxml2xmltest.c | 2 + 3 files changed, 367 insertions(+) create mode 100644 tests/qemustatusxml2xmldata/blockjob-blockdev-in.xml create mode 120000 tests/qemustatusxml2xmldata/blockjob-blockdev-out.xml
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Allow using the node name to specify the base of the 'stream' operation, allow specifying explicit job name and add support for delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_monitor.c | 18 +++++++++++++++--- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 14 ++++++++++++++ src/qemu/qemu_monitor_json.h | 3 +++ tests/qemumonitorjsontest.c | 2 +- 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1b31132807..133bfcfbf3 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -17276,8 +17276,8 @@ qemuDomainBlockPullCommon(virQEMUDriverPtr driver, basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src, baseSource); if (!baseSource || basePath) - ret = qemuMonitorBlockStream(priv->mon, device, basePath, backingPath, - speed); + ret = qemuMonitorBlockStream(priv->mon, device, NULL, false, basePath, + NULL, backingPath, speed); if (qemuDomainObjExitMonitor(driver, vm) < 0) ret = -1; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 1d40aef127..d6cad63863 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3388,16 +3388,28 @@ qemuMonitorScreendump(qemuMonitorPtr mon, int qemuMonitorBlockStream(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) { - VIR_DEBUG("device=%s, base=%s, backingName=%s, bandwidth=%lluB", - device, NULLSTR(base), NULLSTR(backingName), bandwidth); + VIR_DEBUG("device=%s, jobname=%s, persistjob=%d, base=%s, baseNode=%s, " + "backingName=%s, bandwidth=%lluB", + device, NULLSTR(jobname), persistjob, NULLSTR(base), + NULLSTR(baseNode), NULLSTR(backingName), bandwidth); QEMU_CHECK_MONITOR(mon); - return qemuMonitorJSONBlockStream(mon, device, base, backingName, bandwidth); + if (base && baseNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("'base' and 'baseNode' can't be used together")); + return -1; + } + + return qemuMonitorJSONBlockStream(mon, device, jobname, persistjob, base, + baseNode, backingName, bandwidth); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 66bfdb0e5c..bbdf88ae60 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -908,7 +908,10 @@ int qemuMonitorSendKey(qemuMonitorPtr mon, int qemuMonitorBlockStream(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) ATTRIBUTE_NONNULL(2); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index dfcc6d88b5..fe16a3d13c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4797,19 +4797,33 @@ qemuMonitorJSONBlockJobError(virJSONValuePtr cmd, int qemuMonitorJSONBlockStream(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *base, + const char *baseNode, const char *backingName, unsigned long long speed) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; + virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; + + if (persistjob) { + autofinalize = VIR_TRISTATE_BOOL_YES; + autodismiss = VIR_TRISTATE_BOOL_NO; + } if (!(cmd = qemuMonitorJSONMakeCommand("block-stream", "s:device", device, + "S:job-id", jobname, "Y:speed", speed, "S:base", base, + "S:base-node", baseNode, "S:backing-file", backingName, + "T:auto-finalize", autofinalize, + "T:auto-dismiss", autodismiss, NULL))) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index c3abd0ddf0..7420a4aec0 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -307,7 +307,10 @@ int qemuMonitorJSONScreendump(qemuMonitorPtr mon, int qemuMonitorJSONBlockStream(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *base, + const char *baseNode, const char *backingName, unsigned long long speed) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 5c77db337f..49982084af 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1347,7 +1347,7 @@ GEN_TEST_FUNC(qemuMonitorJSONDriveMirror, "vdb", "/foo/bar", "formatstr", 1024, VIR_DOMAIN_BLOCK_REBASE_SHALLOW | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", "vdb", "targetnode", 1024, 1234, 31234, VIR_DOMAIN_BLOCK_REBASE_SHALLOW | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) -GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "/foo/bar1", "backingfilename", 1024) +GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "jobname", true, "/foo/bar1", "backingnode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "/foo/bar1", "/foo/bar2", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONDrivePivot, "vdb") GEN_TEST_FUNC(qemuMonitorJSONScreendump, "devicename", 1, "/foo/bar") -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:54PM +0100, Peter Krempa wrote:
Allow using the node name to specify the base of the 'stream' operation, allow specifying explicit job name and add support for delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_monitor.c | 18 +++++++++++++++--- src/qemu/qemu_monitor.h | 3 +++ src/qemu/qemu_monitor_json.c | 14 ++++++++++++++ src/qemu/qemu_monitor_json.h | 3 +++ tests/qemumonitorjsontest.c | 2 +- 6 files changed, 38 insertions(+), 6 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Allow using the node name to specify the base and top of the 'commit' operation, allow specifying explicit job name and add support for delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_monitor.c | 21 +++++++++++++++------ src/qemu/qemu_monitor.h | 6 +++++- src/qemu/qemu_monitor_json.c | 22 ++++++++++++++++++++-- src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 +- 6 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 133bfcfbf3..f62d153c03 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -18226,8 +18226,8 @@ qemuDomainBlockCommit(virDomainPtr dom, topPath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src, topSource); if (basePath && topPath) - ret = qemuMonitorBlockCommit(priv->mon, device, - topPath, basePath, backingPath, + ret = qemuMonitorBlockCommit(priv->mon, device, NULL, false, + topPath, NULL, basePath, NULL, backingPath, speed); if (qemuDomainObjExitMonitor(driver, vm) < 0) { ret = -1; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d6cad63863..fe8d8d1bf7 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3280,18 +3280,27 @@ qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr *actions) /* Start a block-commit block job. bandwidth is in bytes/sec. */ int -qemuMonitorBlockCommit(qemuMonitorPtr mon, const char *device, - const char *top, const char *base, +qemuMonitorBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *jobname, + bool persistjob, + const char *top, + const char *topNode, + const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) { - VIR_DEBUG("device=%s, top=%s, base=%s, backingName=%s, bandwidth=%llu", - device, top, base, NULLSTR(backingName), bandwidth); + VIR_DEBUG("device=%s, jobname=%s, persistjob=%d, top=%s, topNode=%s, " + "base=%s, baseNode=%s, backingName=%s, bandwidth=%llu", + device, NULLSTR(jobname), persistjob, NULLSTR(top), NULLSTR(topNode), + NULLSTR(base), NULLSTR(baseNode), NULLSTR(backingName), bandwidth); QEMU_CHECK_MONITOR(mon); - return qemuMonitorJSONBlockCommit(mon, device, top, base, - backingName, bandwidth); + return qemuMonitorJSONBlockCommit(mon, device, jobname, persistjob, top, + topNode, base, baseNode, backingName, + bandwidth); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index bbdf88ae60..9cbd442f6c 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -877,11 +877,15 @@ int qemuMonitorDrivePivot(qemuMonitorPtr mon, int qemuMonitorBlockCommit(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *top, + const char *topNode, const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) - ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); + ATTRIBUTE_NONNULL(2); bool qemuMonitorSupportsActiveCommit(qemuMonitorPtr mon); char *qemuMonitorDiskNameLookup(qemuMonitorPtr mon, const char *device, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index fe16a3d13c..03e14ad79c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4401,21 +4401,39 @@ qemuMonitorJSONSupportsActiveCommit(qemuMonitorPtr mon) /* speed is in bytes/sec. Returns 0 on success, -1 with error message * emitted on failure. */ int -qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, - const char *top, const char *base, +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *jobname, + bool persistjob, + const char *top, + const char *topNode, + const char *base, + const char *baseNode, const char *backingName, unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; + virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; + + if (persistjob) { + autofinalize = VIR_TRISTATE_BOOL_YES; + autodismiss = VIR_TRISTATE_BOOL_NO; + } cmd = qemuMonitorJSONMakeCommand("block-commit", "s:device", device, + "S:job-id", jobname, "Y:speed", speed, "S:top", top, + "S:top-node", topNode, "S:base", base, + "S:base-node", baseNode, "S:backing-file", backingName, + "T:auto-finalize", autofinalize, + "T:auto-dismiss", autodismiss, NULL); if (!cmd) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 7420a4aec0..339c569684 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -275,8 +275,12 @@ bool qemuMonitorJSONSupportsActiveCommit(qemuMonitorPtr mon) int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *jobname, + bool persistjob, const char *top, + const char *topNode, const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 49982084af..940edcfb01 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1348,7 +1348,7 @@ GEN_TEST_FUNC(qemuMonitorJSONDriveMirror, "vdb", "/foo/bar", "formatstr", 1024, GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", "vdb", "targetnode", 1024, 1234, 31234, VIR_DOMAIN_BLOCK_REBASE_SHALLOW | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "jobname", true, "/foo/bar1", "backingnode", "backingfilename", 1024) -GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "/foo/bar1", "/foo/bar2", "backingfilename", 1024) +GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "jobname", true, "/foo/bar1", "topnode", "/foo/bar2", "basenode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONDrivePivot, "vdb") GEN_TEST_FUNC(qemuMonitorJSONScreendump, "devicename", 1, "/foo/bar") GEN_TEST_FUNC(qemuMonitorJSONOpenGraphics, "spice", "spicefd", false) -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:55PM +0100, Peter Krempa wrote:
Allow using the node name to specify the base and top of the 'commit' operation, allow specifying explicit job name and add support for delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_driver.c | 4 ++-- src/qemu/qemu_monitor.c | 21 +++++++++++++++------ src/qemu/qemu_monitor.h | 6 +++++- src/qemu/qemu_monitor_json.c | 22 ++++++++++++++++++++-- src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 +- 6 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d6cad63863..fe8d8d1bf7 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3280,18 +3280,27 @@ qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr *actions)
/* Start a block-commit block job. bandwidth is in bytes/sec. */ int -qemuMonitorBlockCommit(qemuMonitorPtr mon, const char *device, - const char *top, const char *base, +qemuMonitorBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *jobname, + bool persistjob, + const char *top, + const char *topNode, + const char *base, + const char *baseNode, const char *backingName, unsigned long long bandwidth) { - VIR_DEBUG("device=%s, top=%s, base=%s, backingName=%s, bandwidth=%llu", - device, top, base, NULLSTR(backingName), bandwidth); + VIR_DEBUG("device=%s, jobname=%s, persistjob=%d, top=%s, topNode=%s, " + "base=%s, baseNode=%s, backingName=%s, bandwidth=%llu", + device, NULLSTR(jobname), persistjob, NULLSTR(top), NULLSTR(topNode), + NULLSTR(base), NULLSTR(baseNode), NULLSTR(backingName), bandwidth);
No checks for mixing *Node and non-*Node variables?
QEMU_CHECK_MONITOR(mon);
- return qemuMonitorJSONBlockCommit(mon, device, top, base, - backingName, bandwidth); + return qemuMonitorJSONBlockCommit(mon, device, jobname, persistjob, top, + topNode, base, baseNode, backingName, + bandwidth); }
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Allow using the delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_monitor.c | 9 +++++---- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_json.c | 10 ++++++++++ src/qemu/qemu_monitor_json.h | 3 ++- tests/qemumonitorjsontest.c | 2 +- 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 87eeec3d84..f8daa2bce0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -831,7 +831,7 @@ qemuMigrationSrcNBDStorageCopyBlockdev(virQEMUDriverPtr driver, mon_ret = qemuBlockStorageSourceAttachApply(qemuDomainGetMonitor(vm), data); if (mon_ret == 0) - mon_ret = qemuMonitorBlockdevMirror(qemuDomainGetMonitor(vm), NULL, + mon_ret = qemuMonitorBlockdevMirror(qemuDomainGetMonitor(vm), NULL, false, diskAlias, copysrc->nodeformat, mirror_speed, 0, 0, mirror_flags); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index fe8d8d1bf7..23579b98ee 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3247,6 +3247,7 @@ qemuMonitorDriveMirror(qemuMonitorPtr mon, int qemuMonitorBlockdevMirror(qemuMonitorPtr mon, const char *jobname, + bool persistjob, const char *device, const char *target, unsigned long long bandwidth, @@ -3254,15 +3255,15 @@ qemuMonitorBlockdevMirror(qemuMonitorPtr mon, unsigned long long buf_size, unsigned int flags) { - VIR_DEBUG("jobname=%s, device=%s, target=%s, bandwidth=%lld, " + VIR_DEBUG("jobname=%s, persistjob=%d, device=%s, target=%s, bandwidth=%lld, " "granularity=%#x, buf_size=%lld, flags=0x%x", - NULLSTR(jobname), device, target, bandwidth, granularity, + NULLSTR(jobname), persistjob, device, target, bandwidth, granularity, buf_size, flags); QEMU_CHECK_MONITOR(mon); - return qemuMonitorJSONBlockdevMirror(mon, jobname, device, target, bandwidth, - granularity, buf_size, flags); + return qemuMonitorJSONBlockdevMirror(mon, jobname, persistjob, device, target, + bandwidth, granularity, buf_size, flags); } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 9cbd442f6c..64a7086a00 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -864,13 +864,14 @@ int qemuMonitorDriveMirror(qemuMonitorPtr mon, ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int qemuMonitorBlockdevMirror(qemuMonitorPtr mon, const char *jobname, + bool persistjob, const char *device, const char *target, unsigned long long bandwidth, unsigned int granularity, unsigned long long buf_size, unsigned int flags) - ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); + ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); int qemuMonitorDrivePivot(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(2); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 03e14ad79c..24388dab3b 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4303,6 +4303,7 @@ qemuMonitorJSONDriveMirror(qemuMonitorPtr mon, int qemuMonitorJSONBlockdevMirror(qemuMonitorPtr mon, const char *jobname, + bool persistjob, const char *device, const char *target, unsigned long long speed, @@ -4314,6 +4315,13 @@ qemuMonitorJSONBlockdevMirror(qemuMonitorPtr mon, virJSONValuePtr cmd; virJSONValuePtr reply = NULL; bool shallow = (flags & VIR_DOMAIN_BLOCK_REBASE_SHALLOW) != 0; + virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; + virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; + + if (persistjob) { + autofinalize = VIR_TRISTATE_BOOL_YES; + autodismiss = VIR_TRISTATE_BOOL_NO; + } cmd = qemuMonitorJSONMakeCommand("blockdev-mirror", "S:job-id", jobname, @@ -4323,6 +4331,8 @@ qemuMonitorJSONBlockdevMirror(qemuMonitorPtr mon, "z:granularity", granularity, "P:buf-size", buf_size, "s:sync", shallow ? "top" : "full", + "T:auto-finalize", autofinalize, + "T:auto-dismiss", autodismiss, NULL); if (!cmd) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 339c569684..df772e64c1 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -259,13 +259,14 @@ int qemuMonitorJSONDriveMirror(qemuMonitorPtr mon, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int qemuMonitorJSONBlockdevMirror(qemuMonitorPtr mon, const char *jobname, + bool persistjob, const char *device, const char *target, unsigned long long speed, unsigned int granularity, unsigned long long buf_size, unsigned int flags) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5); int qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 940edcfb01..2ac4ca1f69 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1345,7 +1345,7 @@ GEN_TEST_FUNC(qemuMonitorJSONDelDevice, "ide0") GEN_TEST_FUNC(qemuMonitorJSONAddDevice, "some_dummy_devicestr") GEN_TEST_FUNC(qemuMonitorJSONDriveMirror, "vdb", "/foo/bar", "formatstr", 1024, 1234, 31234, VIR_DOMAIN_BLOCK_REBASE_SHALLOW | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) -GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", "vdb", "targetnode", 1024, 1234, 31234, +GEN_TEST_FUNC(qemuMonitorJSONBlockdevMirror, "jobname", true, "vdb", "targetnode", 1024, 1234, 31234, VIR_DOMAIN_BLOCK_REBASE_SHALLOW | VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) GEN_TEST_FUNC(qemuMonitorJSONBlockStream, "vdb", "jobname", true, "/foo/bar1", "backingnode", "backingfilename", 1024) GEN_TEST_FUNC(qemuMonitorJSONBlockCommit, "vdb", "jobname", true, "/foo/bar1", "topnode", "/foo/bar2", "basenode", "backingfilename", 1024) -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:56PM +0100, Peter Krempa wrote:
Allow using the delayed dismiss of the job so that we can reap the state even if libvirtd was not running when qemu emitted the job completion event.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_monitor.c | 9 +++++---- src/qemu/qemu_monitor.h | 3 ++- src/qemu/qemu_monitor_json.c | 10 ++++++++++ src/qemu/qemu_monitor_json.h | 3 ++- tests/qemumonitorjsontest.c | 2 +- 6 files changed, 21 insertions(+), 8 deletions(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This belongs to the new job management API which can manage also non-block based jobs. The dismiss command is meant to remove a concluded job after we were able to get the final status. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 23579b98ee..6b73b97334 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3485,6 +3485,18 @@ qemuMonitorGetBlockJobInfo(qemuMonitorPtr mon, } +int +qemuMonitorJobDismiss(qemuMonitorPtr mon, + const char *jobname) +{ + VIR_DEBUG("jobname=%s", jobname); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONJobDismiss(mon, jobname); +} + + int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, const char *drivealias, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 64a7086a00..04ddc763a4 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -945,6 +945,10 @@ int qemuMonitorGetBlockJobInfo(qemuMonitorPtr mon, qemuMonitorBlockJobInfoPtr info) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); +int qemuMonitorJobDismiss(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(2); + int qemuMonitorOpenGraphics(qemuMonitorPtr mon, const char *protocol, int fd, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 24388dab3b..bcbab10d90 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4956,6 +4956,33 @@ qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, } +int +qemuMonitorJSONJobDismiss(qemuMonitorPtr mon, + const char *jobname) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("job-dismiss", + "s:id", jobname, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) + goto cleanup; + + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, const char *protocol, const char *fdname, diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index df772e64c1..ec44292fab 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -332,6 +332,10 @@ int qemuMonitorJSONBlockJobSetSpeed(qemuMonitorPtr mon, virHashTablePtr qemuMonitorJSONGetAllBlockJobInfo(qemuMonitorPtr mon) ATTRIBUTE_NONNULL(1); +int qemuMonitorJSONJobDismiss(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int qemuMonitorJSONSetLink(qemuMonitorPtr mon, const char *name, virDomainNetInterfaceLinkState state); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 2ac4ca1f69..4132182a5d 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1359,6 +1359,7 @@ GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayOpen, "foodev", true) GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayClose, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumRemove, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumInsert, "foodev", "newnode") +GEN_TEST_FUNC(qemuMonitorJSONJobDismiss, "jobname") static bool testQemuMonitorJSONqemuMonitorJSONQueryCPUsEqual(struct qemuMonitorQueryCpusEntry *a, @@ -3005,6 +3006,7 @@ mymain(void) DO_TEST_GEN(qemuMonitorJSONBlockdevTrayClose); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumRemove); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumInsert); + DO_TEST_GEN(qemuMonitorJSONJobDismiss); DO_TEST(qemuMonitorJSONGetBalloonInfo); DO_TEST(qemuMonitorJSONGetBlockInfo); DO_TEST(qemuMonitorJSONGetAllBlockStatsInfo); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:57PM +0100, Peter Krempa wrote:
This belongs to the new job management API which can manage also non-block based jobs.
The dismiss command is meant to remove a concluded job after we were able to get the final status.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This belongs to the new job management API which can manage also non-block based jobs. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6b73b97334..294b2a3785 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3497,6 +3497,18 @@ qemuMonitorJobDismiss(qemuMonitorPtr mon, } +int +qemuMonitorJobCancel(qemuMonitorPtr mon, + const char *jobname) +{ + VIR_DEBUG("jobname=%s", jobname); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONJobCancel(mon, jobname); +} + + int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, const char *drivealias, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 04ddc763a4..13ba7be304 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -949,6 +949,10 @@ int qemuMonitorJobDismiss(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(2); +int qemuMonitorJobCancel(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(2); + int qemuMonitorOpenGraphics(qemuMonitorPtr mon, const char *protocol, int fd, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index bcbab10d90..d780a916a6 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -4983,6 +4983,33 @@ qemuMonitorJSONJobDismiss(qemuMonitorPtr mon, } +int +qemuMonitorJSONJobCancel(qemuMonitorPtr mon, + const char *jobname) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("job-cancel", + "s:id", jobname, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) + goto cleanup; + + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, const char *protocol, const char *fdname, diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index ec44292fab..5c6228e88a 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -336,6 +336,10 @@ int qemuMonitorJSONJobDismiss(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONJobCancel(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int qemuMonitorJSONSetLink(qemuMonitorPtr mon, const char *name, virDomainNetInterfaceLinkState state); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 4132182a5d..7065f0f49b 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1360,6 +1360,7 @@ GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayClose, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumRemove, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumInsert, "foodev", "newnode") GEN_TEST_FUNC(qemuMonitorJSONJobDismiss, "jobname") +GEN_TEST_FUNC(qemuMonitorJSONJobCancel, "jobname") static bool testQemuMonitorJSONqemuMonitorJSONQueryCPUsEqual(struct qemuMonitorQueryCpusEntry *a, @@ -3007,6 +3008,7 @@ mymain(void) DO_TEST_GEN(qemuMonitorJSONBlockdevMediumRemove); DO_TEST_GEN(qemuMonitorJSONBlockdevMediumInsert); DO_TEST_GEN(qemuMonitorJSONJobDismiss); + DO_TEST_GEN(qemuMonitorJSONJobCancel); DO_TEST(qemuMonitorJSONGetBalloonInfo); DO_TEST(qemuMonitorJSONGetBlockInfo); DO_TEST(qemuMonitorJSONGetAllBlockStatsInfo); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:58PM +0100, Peter Krempa wrote:
This belongs to the new job management API which can manage also non-block based jobs.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This belongs to the new job management API which can manage also non-block based jobs. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 294b2a3785..398d3b4d8b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3509,6 +3509,18 @@ qemuMonitorJobCancel(qemuMonitorPtr mon, } +int +qemuMonitorJobComplete(qemuMonitorPtr mon, + const char *jobname) +{ + VIR_DEBUG("jobname=%s", jobname); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONJobComplete(mon, jobname); +} + + int qemuMonitorSetBlockIoThrottle(qemuMonitorPtr mon, const char *drivealias, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 13ba7be304..7feed8a427 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -953,6 +953,10 @@ int qemuMonitorJobCancel(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(2); +int qemuMonitorJobComplete(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(2); + int qemuMonitorOpenGraphics(qemuMonitorPtr mon, const char *protocol, int fd, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d780a916a6..0a63cff7e2 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5010,6 +5010,33 @@ qemuMonitorJSONJobCancel(qemuMonitorPtr mon, } +int +qemuMonitorJSONJobComplete(qemuMonitorPtr mon, + const char *jobname) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("job-complete", + "s:id", jobname, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) + goto cleanup; + + ret = 0; + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, const char *protocol, const char *fdname, diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 5c6228e88a..8de1d8d547 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -340,6 +340,10 @@ int qemuMonitorJSONJobCancel(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int qemuMonitorJSONJobComplete(qemuMonitorPtr mon, + const char *jobname) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + int qemuMonitorJSONSetLink(qemuMonitorPtr mon, const char *name, virDomainNetInterfaceLinkState state); diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 7065f0f49b..5284fe60c6 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -1361,6 +1361,7 @@ GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumRemove, "foodev") GEN_TEST_FUNC(qemuMonitorJSONBlockdevMediumInsert, "foodev", "newnode") GEN_TEST_FUNC(qemuMonitorJSONJobDismiss, "jobname") GEN_TEST_FUNC(qemuMonitorJSONJobCancel, "jobname") +GEN_TEST_FUNC(qemuMonitorJSONJobComplete, "jobname") static bool testQemuMonitorJSONqemuMonitorJSONQueryCPUsEqual(struct qemuMonitorQueryCpusEntry *a, @@ -3009,6 +3010,7 @@ mymain(void) DO_TEST_GEN(qemuMonitorJSONBlockdevMediumInsert); DO_TEST_GEN(qemuMonitorJSONJobDismiss); DO_TEST_GEN(qemuMonitorJSONJobCancel); + DO_TEST_GEN(qemuMonitorJSONJobComplete); DO_TEST(qemuMonitorJSONGetBalloonInfo); DO_TEST(qemuMonitorJSONGetBlockInfo); DO_TEST(qemuMonitorJSONGetAllBlockStatsInfo); -- 2.19.2

On Wed, Dec 12, 2018 at 06:08:59PM +0100, Peter Krempa wrote:
This belongs to the new job management API which can manage also non-block based jobs.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ tests/qemumonitorjsontest.c | 2 ++ 5 files changed, 49 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 23 +++++ src/qemu/qemu_monitor.h | 49 ++++++++++ src/qemu/qemu_monitor_json.c | 86 ++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ .../query-jobs-create.json | 20 +++++ .../query-jobs-create.result | 11 +++ .../qemumonitorjsondata/query-jobs-empty.json | 1 + .../query-jobs-empty.result | 0 tests/qemumonitorjsontest.c | 89 +++++++++++++++++++ 9 files changed, 285 insertions(+) create mode 100644 tests/qemumonitorjsondata/query-jobs-create.json create mode 100644 tests/qemumonitorjsondata/query-jobs-create.result create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.json create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.result diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 398d3b4d8b..7d061a48ab 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4505,3 +4505,26 @@ qemuMonitorGetPRManagerInfo(qemuMonitorPtr mon, virHashFree(info); return ret; } + + +void +qemuMonitorJobInfoFree(qemuMonitorJobInfoPtr job) +{ + if (!job) + return; + + VIR_FREE(job->id); + VIR_FREE(job->error); + VIR_FREE(job); +} + + +int +qemuMonitorGetJobInfo(qemuMonitorPtr mon, + qemuMonitorJobInfoPtr **jobs, + size_t *njobs) +{ + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONGetJobInfo(mon, jobs, njobs); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 7feed8a427..e51177bf44 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -109,6 +109,49 @@ struct _qemuMonitorEventPanicInfo { } data; }; + +typedef enum { + QEMU_MONITOR_JOB_TYPE_UNKNOWN, /* internal value, not exposed by qemu */ + QEMU_MONITOR_JOB_TYPE_COMMIT, + QEMU_MONITOR_JOB_TYPE_STREAM, + QEMU_MONITOR_JOB_TYPE_MIRROR, + QEMU_MONITOR_JOB_TYPE_BACKUP, + QEMU_MONITOR_JOB_TYPE_CREATE, + QEMU_MONITOR_JOB_TYPE_LAST +} qemuMonitorJobType; + +VIR_ENUM_DECL(qemuMonitorJob) + +typedef enum { + QEMU_MONITOR_JOB_STATUS_UNKNOWN, /* internal value, not exposed by qemu */ + QEMU_MONITOR_JOB_STATUS_CREATED, + QEMU_MONITOR_JOB_STATUS_RUNNING, + QEMU_MONITOR_JOB_STATUS_PAUSED, + QEMU_MONITOR_JOB_STATUS_READY, + QEMU_MONITOR_JOB_STATUS_STANDBY, + QEMU_MONITOR_JOB_STATUS_WAITING, + QEMU_MONITOR_JOB_STATUS_PENDING, + QEMU_MONITOR_JOB_STATUS_ABORTING, + QEMU_MONITOR_JOB_STATUS_CONCLUDED, + QEMU_MONITOR_JOB_STATUS_UNDEFINED, /* the job states below should not be visible outside of qemu */ + QEMU_MONITOR_JOB_STATUS_NULL, + QEMU_MONITOR_JOB_STATUS_LAST +} qemuMonitorJobStatus; + +VIR_ENUM_DECL(qemuMonitorJobStatus) + +typedef struct _qemuMonitorJobInfo qemuMonitorJobInfo; +typedef qemuMonitorJobInfo *qemuMonitorJobInfoPtr; +struct _qemuMonitorJobInfo { + char *id; + qemuMonitorJobType type; + qemuMonitorJobStatus status; + char *error; + long long progressCurrent; + long long progressTotal; +}; + + char *qemuMonitorGuestPanicEventInfoFormatMsg(qemuMonitorEventPanicInfoPtr info); void qemuMonitorEventPanicInfoFree(qemuMonitorEventPanicInfoPtr info); @@ -1217,4 +1260,10 @@ struct _qemuMonitorPRManagerInfo { int qemuMonitorGetPRManagerInfo(qemuMonitorPtr mon, virHashTablePtr *retinfo); +void qemuMonitorJobInfoFree(qemuMonitorJobInfoPtr job); + +int qemuMonitorGetJobInfo(qemuMonitorPtr mon, + qemuMonitorJobInfoPtr **jobs, + size_t *njobs); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 0a63cff7e2..101e6ec7cd 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -58,6 +58,12 @@ VIR_LOG_INIT("qemu.qemu_monitor_json"); #define LINE_ENDING "\r\n" +VIR_ENUM_IMPL(qemuMonitorJob, QEMU_MONITOR_JOB_TYPE_LAST, + "", "commit", "stream", "mirror", "backup", "create"); +VIR_ENUM_IMPL(qemuMonitorJobStatus, QEMU_MONITOR_JOB_STATUS_LAST, + "", "created", "running", "paused", "ready", "standby", "waiting", + "pending", "aborting", "concluded", "undefined", "null"); + static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); @@ -8567,3 +8573,83 @@ qemuMonitorJSONGetPRManagerInfo(qemuMonitorPtr mon, return ret; } + + +static qemuMonitorJobInfoPtr +qemuMonitorJSONGetJobInfoOne(virJSONValuePtr data) +{ + const char *id = virJSONValueObjectGetString(data, "id"); + const char *type = virJSONValueObjectGetString(data, "type"); + const char *status = virJSONValueObjectGetString(data, "status"); + const char *errmsg = virJSONValueObjectGetString(data, "error"); + int tmp; + qemuMonitorJobInfoPtr job = NULL; + qemuMonitorJobInfoPtr ret = NULL; + + if (!data) + return NULL; + + if (VIR_ALLOC(job) < 0) + return NULL; + + if ((tmp = qemuMonitorJobTypeFromString(type)) < 0) + tmp = QEMU_MONITOR_JOB_TYPE_UNKNOWN; + + job->type = tmp; + + if ((tmp = qemuMonitorJobStatusTypeFromString(status)) < 0) + tmp = QEMU_MONITOR_JOB_STATUS_UNKNOWN; + + job->status = tmp; + + if (VIR_STRDUP(job->id, id) < 0 || + VIR_STRDUP(job->error, errmsg) < 0) + goto cleanup; + + VIR_STEAL_PTR(ret, job); + + cleanup: + qemuMonitorJobInfoFree(job); + return ret; +} + + +int +qemuMonitorJSONGetJobInfo(qemuMonitorPtr mon, + qemuMonitorJobInfoPtr **jobs, + size_t *njobs) +{ + virJSONValuePtr data; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + size_t i; + int ret = -1; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-jobs", NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) + goto cleanup; + + data = virJSONValueObjectGetArray(reply, "return"); + + for (i = 0; i < virJSONValueArraySize(data); i++) { + qemuMonitorJobInfoPtr job = NULL; + + if (!(job = qemuMonitorJSONGetJobInfoOne(virJSONValueArrayGet(data, i)))) + goto cleanup; + + if (VIR_APPEND_ELEMENT(*jobs, *njobs, job) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 8de1d8d547..7e2ac422dd 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -324,6 +324,12 @@ int qemuMonitorJSONBlockJobCancel(qemuMonitorPtr mon, const char *jobname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int +qemuMonitorJSONGetJobInfo(qemuMonitorPtr mon, + qemuMonitorJobInfoPtr **jobs, + size_t *njobs); + + int qemuMonitorJSONBlockJobSetSpeed(qemuMonitorPtr mon, const char *jobname, unsigned long long speed) diff --git a/tests/qemumonitorjsondata/query-jobs-create.json b/tests/qemumonitorjsondata/query-jobs-create.json new file mode 100644 index 0000000000..fbc7c4b15d --- /dev/null +++ b/tests/qemumonitorjsondata/query-jobs-create.json @@ -0,0 +1,20 @@ +{ + "return": [ + { + "current-progress": 1, + "status": "concluded", + "total-progress": 1, + "type": "create", + "id": "createjob-fail", + "error": "Image size must be a multiple of 512 bytes" + }, + { + "current-progress": 1, + "status": "concluded", + "total-progress": 1, + "type": "create", + "id": "createjob" + } + ], + "id": "libvirt-24" +} diff --git a/tests/qemumonitorjsondata/query-jobs-create.result b/tests/qemumonitorjsondata/query-jobs-create.result new file mode 100644 index 0000000000..a43282fe67 --- /dev/null +++ b/tests/qemumonitorjsondata/query-jobs-create.result @@ -0,0 +1,11 @@ +[job] +id=createjob-fail +type=create +status=concluded +error=Image size must be a multiple of 512 bytes + +[job] +id=createjob +type=create +status=concluded +error=<null> diff --git a/tests/qemumonitorjsondata/query-jobs-empty.json b/tests/qemumonitorjsondata/query-jobs-empty.json new file mode 100644 index 0000000000..c1ede999e5 --- /dev/null +++ b/tests/qemumonitorjsondata/query-jobs-empty.json @@ -0,0 +1 @@ +{ "return": [] } diff --git a/tests/qemumonitorjsondata/query-jobs-empty.result b/tests/qemumonitorjsondata/query-jobs-empty.result new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c index 5284fe60c6..ce3ae2d31b 100644 --- a/tests/qemumonitorjsontest.c +++ b/tests/qemumonitorjsontest.c @@ -2879,6 +2879,83 @@ testQAPISchema(const void *opaque) } +static void +testQueryJobsPrintJob(virBufferPtr buf, + qemuMonitorJobInfoPtr job) +{ + virBufferAddLit(buf, "[job]\n"); + virBufferAsprintf(buf, "id=%s\n", NULLSTR(job->id)); + virBufferAsprintf(buf, "type=%s\n", NULLSTR(qemuMonitorJobTypeToString(job->type))); + virBufferAsprintf(buf, "status=%s\n", NULLSTR(qemuMonitorJobStatusTypeToString(job->status))); + virBufferAsprintf(buf, "error=%s\n", NULLSTR(job->error)); + virBufferAddLit(buf, "\n"); +} + + +struct testQueryJobsData { + const char *name; + virDomainXMLOptionPtr xmlopt; +}; + + +static int +testQueryJobs(const void *opaque) +{ + const struct testQueryJobsData *data = opaque; + qemuMonitorTestPtr test = qemuMonitorTestNewSimple(true, data->xmlopt); + char *filenameJSON = NULL; + char *fileJSON = NULL; + char *filenameResult = NULL; + char *actual = NULL; + qemuMonitorJobInfoPtr *jobs = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + size_t njobs = 0; + size_t i; + int ret = -1; + + if (virAsprintf(&filenameJSON, + abs_srcdir "/qemumonitorjsondata/query-jobs-%s.json", + data->name) < 0 || + virAsprintf(&filenameResult, + abs_srcdir "/qemumonitorjsondata/query-jobs-%s.result", + data->name) < 0) + goto cleanup; + + if (virTestLoadFile(filenameJSON, &fileJSON) < 0) + goto cleanup; + + if (qemuMonitorTestAddItem(test, "query-jobs", fileJSON) < 0) + goto cleanup; + + if (qemuMonitorJSONGetJobInfo(qemuMonitorTestGetMonitor(test), + &jobs, &njobs) < 0) + goto cleanup; + + for (i = 0; i < njobs; i++) + testQueryJobsPrintJob(&buf, jobs[i]); + + virBufferTrim(&buf, "\n", -1); + + if (virBufferCheckError(&buf) < 0) + goto cleanup; + + actual = virBufferContentAndReset(&buf); + + if (virTestCompareToFile(actual, filenameResult) < 0) + goto cleanup; + + ret = 0; + + cleanup: + qemuMonitorTestFree(test); + VIR_FREE(filenameJSON); + VIR_FREE(fileJSON); + VIR_FREE(filenameResult); + VIR_FREE(actual); + return ret; +} + + static int mymain(void) { @@ -3113,6 +3190,18 @@ mymain(void) #undef DO_TEST_QAPI_SCHEMA +#define DO_TEST_QUERY_JOBS(name) \ + do { \ + struct testQueryJobsData data = { name, driver.xmlopt}; \ + if (virTestRun("query-jobs-" name, testQueryJobs, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_QUERY_JOBS("empty"); + DO_TEST_QUERY_JOBS("create"); + +#undef DO_TEST_QUERY_JOBS + cleanup: VIR_FREE(metaschemastr); virJSONValueFree(metaschema); -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:00PM +0100, Peter Krempa wrote:
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 23 +++++ src/qemu/qemu_monitor.h | 49 ++++++++++ src/qemu/qemu_monitor_json.c | 86 ++++++++++++++++++ src/qemu/qemu_monitor_json.h | 6 ++ .../query-jobs-create.json | 20 +++++ .../query-jobs-create.result | 11 +++ .../qemumonitorjsondata/query-jobs-empty.json | 1 + .../query-jobs-empty.result | 0 tests/qemumonitorjsontest.c | 89 +++++++++++++++++++ 9 files changed, 285 insertions(+) create mode 100644 tests/qemumonitorjsondata/query-jobs-create.json create mode 100644 tests/qemumonitorjsondata/query-jobs-create.result create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.json create mode 100644 tests/qemumonitorjsondata/query-jobs-empty.result
+int +qemuMonitorJSONGetJobInfo(qemuMonitorPtr mon, + qemuMonitorJobInfoPtr **jobs, + size_t *njobs) +{ + virJSONValuePtr data; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + size_t i; + int ret = -1; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-jobs", NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + goto cleanup; + + if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) + goto cleanup; + + data = virJSONValueObjectGetArray(reply, "return"); + + for (i = 0; i < virJSONValueArraySize(data); i++) { + qemuMonitorJobInfoPtr job = NULL; + + if (!(job = qemuMonitorJSONGetJobInfoOne(virJSONValueArrayGet(data, i)))) + goto cleanup; + + if (VIR_APPEND_ELEMENT(*jobs, *njobs, job) < 0) + goto cleanup;
On the unlikely event of VIR_APPEND_ELEMENT returning -1, job will be leaked.
+ } + + ret = 0; + + cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +}
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This new state is entered when qemu finished the job but libvirt does not know whether it was successful or not. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 2 +- src/qemu/qemu_blockjob.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 24a0efe36f..ee545fc8de 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -45,7 +45,7 @@ VIR_LOG_INIT("qemu.qemu_blockjob"); /* Note that qemuBlockjobState and qemuBlockjobType values are formatted into * the status XML */ VIR_ENUM_IMPL(qemuBlockjobState, QEMU_BLOCKJOB_STATE_LAST, - "completed", "failed", "cancelled", "ready", "new", "running"); + "completed", "failed", "cancelled", "ready", "new", "running", "concluded"); VIR_ENUM_IMPL(qemuBlockjob, QEMU_BLOCKJOB_TYPE_LAST, "", "pull", "copy", "commit", "active-commit", ""); diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index d1e94dc066..c8a6d8cf4c 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -37,6 +37,8 @@ typedef enum { QEMU_BLOCKJOB_STATE_READY = VIR_DOMAIN_BLOCK_JOB_READY, QEMU_BLOCKJOB_STATE_NEW = VIR_DOMAIN_BLOCK_JOB_LAST, QEMU_BLOCKJOB_STATE_RUNNING, + QEMU_BLOCKJOB_STATE_CONCLUDED, /* job has finished, but it's unknown + whether it has failed or not */ QEMU_BLOCKJOB_STATE_LAST } qemuBlockjobState; verify((int)QEMU_BLOCKJOB_STATE_NEW == VIR_DOMAIN_BLOCK_JOB_LAST); -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:01PM +0100, Peter Krempa wrote:
This new state is entered when qemu finished the job but libvirt does not know whether it was successful or not.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 2 +- src/qemu/qemu_blockjob.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

This new event is a superset of the BLOCK_JOB* events and also covers jobs which don't bind to a VM disk. In this patch the monitor part is implemented. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 13 +++++++++++++ src/qemu/qemu_monitor.h | 9 +++++++++ src/qemu/qemu_monitor_json.c | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 7d061a48ab..367e3265b8 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1526,6 +1526,19 @@ qemuMonitorEmitBlockJob(qemuMonitorPtr mon, } +int +qemuMonitorEmitJobStatusChange(qemuMonitorPtr mon, + const char *jobname, + qemuMonitorJobStatus status) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, jobStatusChange, mon->vm, jobname, status); + return ret; +} + + int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, unsigned long long actual) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index e51177bf44..e7ea8f5b8e 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -222,6 +222,11 @@ typedef int (*qemuMonitorDomainBlockJobCallback)(qemuMonitorPtr mon, int status, const char *error, void *opaque); +typedef int (*qemuMonitorDomainJobStatusChangeCallback)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *jobname, + int status, + void *opaque); typedef int (*qemuMonitorDomainTrayChangeCallback)(qemuMonitorPtr mon, virDomainObjPtr vm, const char *devAlias, @@ -341,6 +346,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainIOErrorCallback domainIOError; qemuMonitorDomainGraphicsCallback domainGraphics; qemuMonitorDomainBlockJobCallback domainBlockJob; + qemuMonitorDomainJobStatusChangeCallback jobStatusChange; qemuMonitorDomainTrayChangeCallback domainTrayChange; qemuMonitorDomainPMWakeupCallback domainPMWakeup; qemuMonitorDomainPMSuspendCallback domainPMSuspend; @@ -452,6 +458,9 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int type, int status, const char *error); +int qemuMonitorEmitJobStatusChange(qemuMonitorPtr mon, + const char *jobname, + qemuMonitorJobStatus status); int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, unsigned long long actual); int qemuMonitorEmitPMSuspendDisk(qemuMonitorPtr mon); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 101e6ec7cd..a8f6320414 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -84,6 +84,7 @@ static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr d static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleJobStatusChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); @@ -115,6 +116,7 @@ static qemuEventHandler eventHandlers[] = { { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, { "DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, + { "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, }, { "MIGRATION", qemuMonitorJSONHandleMigrationStatus, }, { "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, }, { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, }, @@ -1035,6 +1037,30 @@ qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon, qemuMonitorEmitBlockJob(mon, device, type, event, error); } + +static void +qemuMonitorJSONHandleJobStatusChange(qemuMonitorPtr mon, + virJSONValuePtr data) +{ + const char *jobname = virJSONValueObjectGetString(data, "id"); + const char *statusstr = virJSONValueObjectGetString(data, "status"); + int status; + + if (!jobname) { + VIR_WARN("missing job name in JOB_STATUS_CHANGE event"); + return; + } + + if ((status = qemuMonitorJobStatusTypeFromString(statusstr)) < 0) { + VIR_WARN("unknown job status '%s' for job '%s' in JOB_STATUS_CHANGE event", + statusstr, jobname); + return; + } + + qemuMonitorEmitJobStatusChange(mon, jobname, status); +} + + static void qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon, virJSONValuePtr data) -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:02PM +0100, Peter Krempa wrote:
This new event is a superset of the BLOCK_JOB* events and also covers jobs which don't bind to a VM disk.
In this patch the monitor part is implemented.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_monitor.c | 13 +++++++++++++ src/qemu/qemu_monitor.h | 9 +++++++++ src/qemu/qemu_monitor_json.c | 26 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

With blockdev we'll need to use the JOB_STATUS_CHANGE so gate the old events by the blockdev capability. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 4b57411856..255a5acc13 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -923,6 +923,7 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, const char *error, void *opaque) { + qemuDomainObjPrivatePtr priv; virQEMUDriverPtr driver = opaque; struct qemuProcessEvent *processEvent = NULL; virDomainDiskDefPtr disk; @@ -931,6 +932,12 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virObjectLock(vm); + priv = vm->privateData; + + /* with QEMU_CAPS_BLOCKDEV we handle block job events via JOB_STATUS_CHANGE */ + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) + goto cleanup; + VIR_DEBUG("Block job for device %s (domain: %p,%s) type %d status %d", diskAlias, vm, vm->def->name, type, status); -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:03PM +0100, Peter Krempa wrote:
With blockdev we'll need to use the JOB_STATUS_CHANGE so gate the old events by the blockdev capability.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_process.c | 7 +++++++ 1 file changed, 7 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 41 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 3 +++ 2 files changed, 44 insertions(+) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index ee545fc8de..020e7b3994 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -480,3 +480,44 @@ qemuBlockJobGetByDisk(virDomainDiskDefPtr disk) return virObjectRef(job); } + + +/** + * @monitorstatus: Status of the blockjob from qemu monitor (qemuMonitorJobStatus) + * + * Converts the block job status from the monitor to the one used by + * qemuBlockJobData. If the status is unknown or does not require any handling + * QEMU_BLOCKJOB_TYPE_LAST is returned. + */ +qemuBlockjobState +qemuBlockjobConvertMonitorStatus(int monitorstatus) +{ + qemuBlockjobState ret = QEMU_BLOCKJOB_TYPE_LAST; + + switch ((qemuMonitorJobStatus) monitorstatus) { + case QEMU_MONITOR_JOB_STATUS_READY: + ret = QEMU_BLOCKJOB_STATE_READY; + break; + + case QEMU_MONITOR_JOB_STATUS_CONCLUDED: + ret = QEMU_BLOCKJOB_STATE_CONCLUDED; + break; + + case QEMU_MONITOR_JOB_STATUS_UNKNOWN: + case QEMU_MONITOR_JOB_STATUS_CREATED: + case QEMU_MONITOR_JOB_STATUS_RUNNING: + case QEMU_MONITOR_JOB_STATUS_PAUSED: + case QEMU_MONITOR_JOB_STATUS_STANDBY: + case QEMU_MONITOR_JOB_STATUS_WAITING: + case QEMU_MONITOR_JOB_STATUS_PENDING: + case QEMU_MONITOR_JOB_STATUS_ABORTING: + case QEMU_MONITOR_JOB_STATUS_UNDEFINED: + case QEMU_MONITOR_JOB_STATUS_NULL: + case QEMU_MONITOR_JOB_STATUS_LAST: + default: + break; + } + + return ret; + +} diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index c8a6d8cf4c..9a2d047d03 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -118,4 +118,7 @@ qemuBlockJobDataPtr qemuBlockJobGetByDisk(virDomainDiskDefPtr disk) ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; +qemuBlockjobState +qemuBlockjobConvertMonitorStatus(int monitorstatus); + #endif /* __QEMU_BLOCKJOB_H__ */ -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:04PM +0100, Peter Krempa wrote:
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 41 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 3 +++ 2 files changed, 44 insertions(+)
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index ee545fc8de..020e7b3994 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -480,3 +480,44 @@ qemuBlockJobGetByDisk(virDomainDiskDefPtr disk)
return virObjectRef(job); } + + +/** + * @monitorstatus: Status of the blockjob from qemu monitor (qemuMonitorJobStatus) + * + * Converts the block job status from the monitor to the one used by + * qemuBlockJobData. If the status is unknown or does not require any handling + * QEMU_BLOCKJOB_TYPE_LAST is returned. + */ +qemuBlockjobState +qemuBlockjobConvertMonitorStatus(int monitorstatus) +{ + qemuBlockjobState ret = QEMU_BLOCKJOB_TYPE_LAST; + qemu/qemu_blockjob.c:495:29: error: implicit conversion from enumeration type 'qemuBlockjobType' to different enumeration type 'qemuBlockjobState' [-Werror,-Wenum-conversion] qemuBlockjobState ret = QEMU_BLOCKJOB_TYPE_LAST; ~~~ ^~~~~~~~~~~~~~~~~~~~~~~
+ switch ((qemuMonitorJobStatus) monitorstatus) { + case QEMU_MONITOR_JOB_STATUS_READY: + ret = QEMU_BLOCKJOB_STATE_READY; + break; + + case QEMU_MONITOR_JOB_STATUS_CONCLUDED: + ret = QEMU_BLOCKJOB_STATE_CONCLUDED; + break; + + case QEMU_MONITOR_JOB_STATUS_UNKNOWN:
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Add support for handling the event either synchronously or asynchronously using the event thread. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 3 ++ src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 23 ++++++++++++++ src/qemu/qemu_process.c | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7845889dec..9467d0fd32 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -13711,6 +13711,9 @@ qemuProcessEventFree(struct qemuProcessEvent *event) case QEMU_PROCESS_EVENT_MONITOR_EOF: VIR_FREE(event->data); break; + case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE: + virObjectUnref(event->data); + break; case QEMU_PROCESS_EVENT_PR_DISCONNECT: case QEMU_PROCESS_EVENT_LAST: break; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index a243bdc80e..0a2973aba9 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -485,6 +485,7 @@ typedef enum { QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED, QEMU_PROCESS_EVENT_SERIAL_CHANGED, QEMU_PROCESS_EVENT_BLOCK_JOB, + QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE, QEMU_PROCESS_EVENT_MONITOR_EOF, QEMU_PROCESS_EVENT_PR_DISCONNECT, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f62d153c03..0c38a61adc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4747,6 +4747,26 @@ processBlockJobEvent(virQEMUDriverPtr driver, } +static void +processJobStatusChangeEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuBlockJobDataPtr job) +{ + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + return; + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Domain is not running"); + goto endjob; + } + + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); + + endjob: + qemuDomainObjEndJob(driver, vm); +} + + static void processMonitorEOFEvent(virQEMUDriverPtr driver, virDomainObjPtr vm) @@ -4842,6 +4862,9 @@ static void qemuProcessEventHandler(void *data, void *opaque) processEvent->action, processEvent->status); break; + case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE: + processJobStatusChangeEvent(driver, vm, processEvent->data); + break; case QEMU_PROCESS_EVENT_MONITOR_EOF: processMonitorEOFEvent(driver, vm); break; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 255a5acc13..fb687a1fd9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -981,6 +981,71 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuProcessHandleJobStatusChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *jobname, + int status, + void *opaque) +{ + virQEMUDriverPtr driver = opaque; + qemuDomainObjPrivatePtr priv; + struct qemuProcessEvent *processEvent = NULL; + qemuBlockJobDataPtr job = NULL; + qemuBlockjobState jobnewstate; + + virObjectLock(vm); + priv = vm->privateData; + + VIR_DEBUG("job '%s'(domain: %p,%s) state changed to '%s'(%d)", + jobname, vm, vm->def->name, + qemuMonitorJobStatusTypeToString(status), status); + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) { + VIR_DEBUG("job '%s' handled by old blockjob handler", jobname); + goto cleanup; + } + + jobnewstate = qemuBlockjobConvertMonitorStatus(status); + + if (jobnewstate == QEMU_BLOCKJOB_STATE_LAST) + goto cleanup; + + if (!(job = virHashLookup(priv->blockjobs, jobname))) { + VIR_DEBUG("job '%s' not registered", jobname); + goto cleanup; + } + + job->newstate = jobnewstate; + + if (job->synchronous) { + VIR_DEBUG("job '%s' handled synchronously", jobname); + virDomainObjBroadcast(vm); + } else { + VIR_DEBUG("job '%s' handled by event thread", jobname); + if (VIR_ALLOC(processEvent) < 0) + goto cleanup; + + processEvent->eventType = QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE; + processEvent->vm = virObjectRef(vm); + VIR_STEAL_PTR(processEvent->data, job); + + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + ignore_value(virObjectUnref(vm)); + goto cleanup; + } + + processEvent = NULL; + } + + cleanup: + qemuProcessEventFree(processEvent); + virObjectUnref(job); + virObjectUnlock(vm); + return 0; +} + + static int qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm, @@ -1735,6 +1800,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainIOError = qemuProcessHandleIOError, .domainGraphics = qemuProcessHandleGraphics, .domainBlockJob = qemuProcessHandleBlockJob, + .jobStatusChange = qemuProcessHandleJobStatusChange, .domainTrayChange = qemuProcessHandleTrayChange, .domainPMWakeup = qemuProcessHandlePMWakeup, .domainPMSuspend = qemuProcessHandlePMSuspend, -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:05PM +0100, Peter Krempa wrote:
Add support for handling the event either synchronously or asynchronously using the event thread.
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 3 ++ src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 23 ++++++++++++++ src/qemu/qemu_process.c | 66 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano

Add the infrastructure to handle block job events in the -blockdev era. Some complexity is required as qemu does not bother to notify whether the job was concluded successfully or failed. Thus it's necessary to re-query the monitor. To minimize the possibility of stuck jobs save the state into the XML prior to handling everything so that the reconnect code can potentially continue with the cleanup. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 123 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 020e7b3994..6aa8b1df51 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -390,6 +390,124 @@ qemuBlockJobEventProcessLegacy(virQEMUDriverPtr driver, } +static int +qemuBlockJobEventProcessConcluded(qemuBlockJobDataPtr job, + virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob, + virQEMUDriverConfigPtr cfg) +{ + qemuMonitorJobInfoPtr *jobinfo = NULL; + size_t njobinfo = 0; + size_t i; + int ret = -1; + int rc = 0; + bool refreshstate = job->state == QEMU_BLOCKJOB_STATE_CONCLUDED; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + + /* fetch job state */ + if (refreshstate) + rc = qemuMonitorGetJobInfo(qemuDomainGetMonitor(vm), &jobinfo, &njobinfo); + /* dismiss job in qemu */ + if (rc >= 0) + rc = qemuMonitorJobDismiss(qemuDomainGetMonitor(vm), job->name); + + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + goto cleanup; + + /* we need to fetch the error state as the event does not propagate it */ + if (refreshstate) { + for (i = 0; i < njobinfo; i++) { + if (STRNEQ_NULLABLE(job->name, jobinfo[i]->id)) + continue; + + if (VIR_STRDUP(job->errmsg, jobinfo[i]->error) < 0) + goto cleanup; + } + + if (i == njobinfo) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to refresh job '%s'"), + job->name); + goto cleanup; + } + + if (job->errmsg) + job->state = QEMU_BLOCKJOB_STATE_FAILED; + else + job->state = QEMU_BLOCKJOB_STATE_COMPLETED; + + /* write status XML */ + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + VIR_WARN("Unable to save status on vm %s after block job", vm->def->name); + } + + /* TODO: implement handlers for job types */ + switch (job->type) { + case QEMU_BLOCKJOB_TYPE_PULL: + case QEMU_BLOCKJOB_TYPE_COPY: + case QEMU_BLOCKJOB_TYPE_COMMIT: + case QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT: + case QEMU_BLOCKJOB_TYPE_NONE: + case QEMU_BLOCKJOB_TYPE_INTERNAL: + case QEMU_BLOCKJOB_TYPE_LAST: + break; + } + + ret = 0; + + cleanup: + for (i = 0; i < njobinfo; i++) + qemuMonitorJobInfoFree(jobinfo[i]); + VIR_FREE(jobinfo); + + return ret; +} + + +static void +qemuBlockJobEventProcess(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuBlockJobDataPtr job, + qemuDomainAsyncJob asyncJob) + +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + switch (job->newstate) { + case QEMU_BLOCKJOB_STATE_COMPLETED: + case QEMU_BLOCKJOB_STATE_FAILED: + case QEMU_BLOCKJOB_STATE_CANCELLED: + case QEMU_BLOCKJOB_STATE_CONCLUDED: + qemuBlockJobEventProcessConcluded(job, driver, vm, asyncJob, cfg); + break; + + case QEMU_BLOCKJOB_STATE_READY: + if (job->disk && job->disk->mirror) { + job->disk->mirrorState = VIR_DOMAIN_BLOCK_JOB_READY; + qemuBlockJobEmitEvents(driver, vm, job->disk, job->type, job->newstate); + } + job->state = job->newstate; + break; + + case QEMU_BLOCKJOB_STATE_NEW: + case QEMU_BLOCKJOB_STATE_RUNNING: + case QEMU_BLOCKJOB_STATE_LAST: + goto cleanup; + } + + /* write status XML */ + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, driver->caps) < 0) + VIR_WARN("Unable to save status on vm %s after block job", vm->def->name); + + cleanup: + job->newstate = -1; + virObjectUnref(cfg); + return; +} + + /** * qemuBlockJobUpdateDisk: * @vm: domain @@ -411,7 +529,10 @@ qemuBlockJobUpdate(virDomainObjPtr vm, if (job->newstate == -1) return -1; - qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) + qemuBlockJobEventProcess(priv->driver, vm, job, asyncJob); + else + qemuBlockJobEventProcessLegacy(priv->driver, vm, job, asyncJob); return job->state; } -- 2.19.2

Refresh the state of the jobs and process any events that might have happened while libvirt was not running. The job state processing requires some care to figure out if a job needs to be bumped.t Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 57 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 4 +++ src/qemu/qemu_process.c | 7 ++++- 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index 6aa8b1df51..8631db7986 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -222,6 +222,63 @@ qemuBlockJobIsRunning(qemuBlockJobDataPtr job) } +int +qemuBlockJobRefreshJobs(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuMonitorJobInfoPtr *jobinfo = NULL; + size_t njobinfo = 0; + qemuBlockJobDataPtr job = NULL; + qemuBlockjobState newstate; + size_t i; + int ret = -1; + int rc; + + qemuDomainObjEnterMonitor(driver, vm); + + rc = qemuMonitorGetJobInfo(priv->mon, &jobinfo, &njobinfo); + + if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) + goto cleanup; + + for (i = 0; i < njobinfo; i++) { + if (!(job = virHashLookup(priv->blockjobs, jobinfo[i]->id))) { + VIR_DEBUG("ignoring untracked job '%s'", jobinfo[i]->id); + continue; + } + + newstate = qemuBlockjobConvertMonitorStatus(jobinfo[i]->status); + if (newstate == QEMU_BLOCKJOB_STATE_LAST) + continue; + + if (newstate != job->state) { + if ((job->state == QEMU_BLOCKJOB_STATE_FAILED || + job->state == QEMU_BLOCKJOB_STATE_COMPLETED)) { + /* preserve the old state but allow the job to be bumped to + * execute the finishing steps */ + job->newstate = job->state; + } else { + job->newstate = newstate; + } + } + + /* qemuBlockJobUpdate checks whether something is needed */ + qemuBlockJobUpdate(vm, job, QEMU_ASYNC_JOB_NONE); + job = NULL; /* job may have become invalid here */ + } + + ret = 0; + + cleanup: + for (i = 0; i < njobinfo; i++) + qemuMonitorJobInfoFree(jobinfo[i]); + VIR_FREE(jobinfo); + + return ret; +} + + /** * qemuBlockJobEmitEvents: * diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index 9a2d047d03..6e86c78a9b 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -105,6 +105,10 @@ void qemuBlockJobStartupFinalize(virDomainObjPtr vm, qemuBlockJobDataPtr job); +int +qemuBlockJobRefreshJobs(virQEMUDriverPtr driver, + virDomainObjPtr vm); + int qemuBlockJobUpdate(virDomainObjPtr vm, qemuBlockJobDataPtr job, int asyncJob); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index fb687a1fd9..75edced15d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7895,7 +7895,12 @@ static int qemuProcessRefreshBlockjobs(virQEMUDriverPtr driver, virDomainObjPtr vm) { - return qemuProcessRefreshLegacyBlockjobs(driver, vm); + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) + return qemuBlockJobRefreshJobs(driver, vm); + else + return qemuProcessRefreshLegacyBlockjobs(driver, vm); } -- 2.19.2

On Wed, Dec 12, 2018 at 06:09:07PM +0100, Peter Krempa wrote:
Refresh the state of the jobs and process any events that might have happened while libvirt was not running.
The job state processing requires some care to figure out if a job needs to be bumped.t
d/t/
Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_blockjob.c | 57 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_blockjob.h | 4 +++ src/qemu/qemu_process.c | 7 ++++- 3 files changed, 67 insertions(+), 1 deletion(-)
Reviewed-by: Ján Tomko <jtomko@redhat.com> Jano
participants (2)
-
Ján Tomko
-
Peter Krempa