qemuBlockJobSyncBegin and qemuBlockJobSyncEnd delimit a region of code
where block job events are processed "synchronously".
qemuBlockJobSyncWait and qemuBlockJobSyncWaitWithTimeout wait for an
event generated by a block job.
The Wait* functions may be called multiple times while the synchronous
block job is active. Any pending block job event will be processed by
only when Wait* or End is called. disk->blockJobStatus is reset by
these functions, so if it is needed a pointer to a
virConnectDomainEventBlockJobStatus variable should be passed as the
last argument. It is safe to pass NULL if you do not care about the
block job status.
All functions assume the VM object is locked. The Wait* functions will
unlock the object for as long as they are waiting. They will return -1
and report an error if the domain exits before an event is received.
Typical use is as follows:
virQEMUDriverPtr driver;
virDomainObjPtr vm; /* locked */
virDomainDiskDefPtr disk;
virConnectDomainEventBlockJobStatus status;
qemuBlockJobSyncBegin(disk);
... start block job ...
if (qemuBlockJobSyncWait(driver, vm, disk, &status) < 0) {
/* domain died while waiting for event */
ret = -1;
goto error;
}
... possibly start other block jobs
or wait for further events ...
qemuBlockJobSyncEnd(driver, vm, disk, NULL);
To perform other tasks periodically while waiting for an event:
virQEMUDriverPtr driver;
virDomainObjPtr vm; /* locked */
virDomainDiskDefPtr disk;
virConnectDomainEventBlockJobStatus status;
unsigned long long timeout = 500 * 1000ull; /* milliseconds */
qemuBlockJobSyncBegin(disk);
... start block job ...
do {
... do other task ...
if (qemuBlockJobSyncWaitWithTimeout(driver, vm, disk,
timeout, &status) < 0) {
/* domain died while waiting for event */
ret = -1;
goto error;
}
} while (status == -1);
qemuBlockJobSyncEnd(driver, vm, disk, NULL);
Signed-off-by: Michael Chapman <mike(a)very.puzzling.org>
---
po/POTFILES.in | 1 +
src/qemu/qemu_blockjob.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_blockjob.h | 16 +++++
3 files changed, 181 insertions(+)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dd06ab3..bb0f6e1 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -112,6 +112,7 @@ src/parallels/parallels_utils.h
src/parallels/parallels_storage.c
src/phyp/phyp_driver.c
src/qemu/qemu_agent.c
+src/qemu/qemu_blockjob.c
src/qemu/qemu_capabilities.c
src/qemu/qemu_cgroup.c
src/qemu/qemu_command.c
diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c
index e61ad8c..729928a 100644
--- a/src/qemu/qemu_blockjob.c
+++ b/src/qemu/qemu_blockjob.c
@@ -31,6 +31,8 @@
#include "virlog.h"
#include "virstoragefile.h"
+#include "virthread.h"
+#include "virtime.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -165,3 +167,165 @@ qemuBlockJobEventProcess(virQEMUDriverPtr driver,
virObjectUnref(cfg);
}
+
+
+/**
+ * qemuBlockJobSyncBegin:
+ * @disk: domain disk
+ *
+ * Begin a new synchronous block job for @disk. The synchronous
+ * 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 qemuBlockJobSyncWait* or
+ * qemuBlockJobSyncEnd is called.
+ */
+void
+qemuBlockJobSyncBegin(virDomainDiskDefPtr disk)
+{
+ if (disk->blockJobSync)
+ VIR_WARN("Disk %s already has synchronous block job",
+ disk->dst);
+
+ disk->blockJobSync = true;
+}
+
+
+/**
+ * qemuBlockJobSyncEnd:
+ * @driver: qemu driver
+ * @vm: domain
+ * @disk: domain disk
+ * @ret_status: pointer to virConnectDomainEventBlockJobStatus
+ *
+ * End a synchronous block job for @disk. Any pending block job event
+ * for the disk is processed, and its status is recorded in the
+ * virConnectDomainEventBlockJobStatus field pointed to by
+ * @ret_status.
+ */
+void
+qemuBlockJobSyncEnd(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virConnectDomainEventBlockJobStatus *ret_status)
+{
+ if (disk->blockJobSync && disk->blockJobStatus != -1) {
+ if (ret_status)
+ *ret_status = disk->blockJobStatus;
+ qemuBlockJobEventProcess(driver, vm, disk,
+ disk->blockJobType,
+ disk->blockJobStatus);
+ disk->blockJobStatus = -1;
+ }
+ disk->blockJobSync = false;
+}
+
+
+/**
+ * qemuBlockJobSyncWaitWithTimeout:
+ * @driver: qemu driver
+ * @vm: domain
+ * @disk: domain disk
+ * @timeout: timeout in milliseconds
+ * @ret_status: pointer to virConnectDomainEventBlockJobStatus
+ *
+ * Wait up to @timeout milliseconds for a block job event for @disk.
+ * If an event is received it is processed, and its status is recorded
+ * in the virConnectDomainEventBlockJobStatus field pointed to by
+ * @ret_status.
+ *
+ * If @timeout is not 0, @vm will be unlocked while waiting for the event.
+ *
+ * Returns 0 if an event was received or the timeout expired,
+ * -1 otherwise.
+ */
+int
+qemuBlockJobSyncWaitWithTimeout(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long timeout,
+ virConnectDomainEventBlockJobStatus *ret_status)
+{
+ if (!disk->blockJobSync) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("No current synchronous block job"));
+ return -1;
+ }
+
+ while (disk->blockJobSync && disk->blockJobStatus == -1) {
+ int r;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ disk->blockJobSync = false;
+ return -1;
+ }
+
+ if (timeout == (unsigned long long)-1) {
+ r = virCondWait(&disk->blockJobSyncCond, &vm->parent.lock);
+ } else if (timeout) {
+ unsigned long long now;
+ if (virTimeMillisNow(&now) < 0) {
+ virReportSystemError(errno, "%s",
+ _("Unable to get current time"));
+ return -1;
+ }
+ r = virCondWaitUntil(&disk->blockJobSyncCond,
&vm->parent.lock,
+ now + timeout);
+ if (r < 0 && errno == ETIMEDOUT)
+ return 0;
+ } else {
+ errno = ETIMEDOUT;
+ return 0;
+ }
+
+ if (r < 0) {
+ disk->blockJobSync = false;
+ virReportSystemError(errno, "%s",
+ _("Unable to wait on block job sync "
+ "condition"));
+ return -1;
+ }
+ }
+
+ if (ret_status)
+ *ret_status = disk->blockJobStatus;
+ qemuBlockJobEventProcess(driver, vm, disk,
+ disk->blockJobType,
+ disk->blockJobStatus);
+ disk->blockJobStatus = -1;
+
+ return 0;
+}
+
+
+/**
+ * qemuBlockJobSyncWait:
+ * @driver: qemu driver
+ * @vm: domain
+ * @disk: domain disk
+ * @ret_status: pointer to virConnectDomainEventBlockJobStatus
+ *
+ * Wait for a block job event for @disk. If an event is received it
+ * is processed, and its status is recorded in the
+ * virConnectDomainEventBlockJobStatus field pointed to by
+ * @ret_status.
+ *
+ * @vm will be unlocked while waiting for the event.
+ *
+ * Returns 0 if an event was received,
+ * -1 otherwise.
+ */
+int
+qemuBlockJobSyncWait(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virConnectDomainEventBlockJobStatus *ret_status)
+{
+ return qemuBlockJobSyncWaitWithTimeout(driver, vm, disk,
+ (unsigned long long)-1,
+ ret_status);
+}
diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h
index abe1aa5..ba372a2 100644
--- a/src/qemu/qemu_blockjob.h
+++ b/src/qemu/qemu_blockjob.h
@@ -22,6 +22,7 @@
#ifndef __QEMU_BLOCKJOB_H__
# define __QEMU_BLOCKJOB_H__
+# include "internal.h"
# include "qemu_conf.h"
void qemuBlockJobEventProcess(virQEMUDriverPtr driver,
@@ -30,4 +31,19 @@ void qemuBlockJobEventProcess(virQEMUDriverPtr driver,
int type,
int status);
+void qemuBlockJobSyncBegin(virDomainDiskDefPtr disk);
+void qemuBlockJobSyncEnd(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virConnectDomainEventBlockJobStatus *ret_status);
+int qemuBlockJobSyncWaitWithTimeout(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long timeout,
+ virConnectDomainEventBlockJobStatus *ret_status);
+int qemuBlockJobSyncWait(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virConnectDomainEventBlockJobStatus *ret_status);
+
#endif /* __QEMU_BLOCKJOB_H__ */
--
2.1.0