diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9324de065c..cd6ee22e57 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1289,9 +1289,22 @@ virDomainDiskDefNew(void) if (VIR_ALLOC(ret) < 0) return NULL; + if (VIR_ALLOC(ret->src) < 0) - VIR_FREE(ret); + goto error; + + if (virCondInit(&ret->blockJobSyncCond) < 0) { + virReportSystemError(errno, "%s", _("Failed to initialize condition")); + goto error; + } + return ret; + + error: + virStorageSourceFree(ret->src); + VIR_FREE(ret); + + return NULL; } @@ -1310,6 +1323,7 @@ virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->product); VIR_FREE(def->domain_name); virDomainDeviceInfoClear(&def->info); + virCondDestroy(&def->blockJobSyncCond); VIR_FREE(def); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 608f61f0e3..84e880a6d7 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -685,6 +685,12 @@ struct _virDomainDiskDef { int mirrorState; /* enum virDomainDiskMirrorState */ int mirrorJob; /* virDomainBlockJobType */ + /* for some synchronous block jobs, we need to notify the owner */ + virCond blockJobSyncCond; + int blockJobType; /* type of the block job from the event */ + int blockJobStatus; /* status of the finished block job */ + bool blockJobSync; /* the block job needs synchronized termination */ + struct { unsigned int cylinders; unsigned int heads; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5a90dcde29..becf415b8b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16276,6 +16276,12 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm, goto endjob; if (mode == BLOCK_JOB_ABORT) { + if (async && !(flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)) { + /* prepare state for event delivery */ + disk->blockJobStatus = -1; + disk->blockJobSync = true; + } + if ((flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT) && !(async && disk->mirror)) { virReportError(VIR_ERR_OPERATION_INVALID, @@ -16389,37 +16395,24 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm, status); event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type, status); - } else if (!(flags & VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC)) { + } else if (disk->blockJobSync) { /* XXX If the event reports failure, we should reflect * that back into the return status of this API call. */ - while (1) { - /* Poll every 50ms */ - static struct timespec ts = { - .tv_sec = 0, - .tv_nsec = 50 * 1000 * 1000ull }; - virDomainBlockJobInfo dummy; - qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorBlockJobInfo(priv->mon, device, &dummy, NULL); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - ret = -1; - - if (ret <= 0) - break; - - virObjectUnlock(vm); - - nanosleep(&ts, NULL); - - virObjectLock(vm); - - if (!virDomainObjIsActive(vm)) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("domain is not running")); - ret = -1; - break; + while (disk->blockJobStatus == -1 && disk->blockJobSync) { + if (virCondWait(&disk->blockJobSyncCond, &vm->parent.lock) < 0) { + virReportSystemError(errno, "%s", + _("Unable to wait on block job sync " + "condition")); + disk->blockJobSync = false; + goto endjob; } } + + qemuBlockJobEventProcess(driver, vm, disk, + disk->blockJobType, + disk->blockJobStatus); + disk->blockJobSync = false; } } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 79f763e324..2d86eb8411 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1020,28 +1020,40 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, { virQEMUDriverPtr driver = opaque; struct qemuProcessEvent *processEvent = NULL; - char *data; + virDomainDiskDefPtr disk; + char *data = NULL; virObjectLock(vm); VIR_DEBUG("Block job for device %s (domain: %p,%s) type %d status %d", diskAlias, vm, vm->def->name, type, status); - if (VIR_ALLOC(processEvent) < 0) + if (!(disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias))) goto error; - processEvent->eventType = QEMU_PROCESS_EVENT_BLOCK_JOB; - if (VIR_STRDUP(data, diskAlias) < 0) - goto error; - processEvent->data = data; - processEvent->vm = vm; - processEvent->action = type; - processEvent->status = status; + if (disk->blockJobSync) { + disk->blockJobType = type; + disk->blockJobStatus = status; + /* We have an SYNC API waiting for this event, dispatch it back */ + virCondSignal(&disk->blockJobSyncCond); + } else { + /* there is no waiting SYNC API, dispatch the update to a thread */ + if (VIR_ALLOC(processEvent) < 0) + goto error; - virObjectRef(vm); - if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { - ignore_value(virObjectUnref(vm)); - goto error; + processEvent->eventType = QEMU_PROCESS_EVENT_BLOCK_JOB; + if (VIR_STRDUP(data, diskAlias) < 0) + goto error; + processEvent->data = data; + processEvent->vm = vm; + processEvent->action = type; + processEvent->status = status; + + virObjectRef(vm); + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + ignore_value(virObjectUnref(vm)); + goto error; + } } cleanup: