With this patch, block write threshold events delivered by qemu
are converted into libvirt events.
FIXME:
Qemu reports stats based on node name, but my plan is to rely on
a patch where qemu auto-assigns node names rather than libvirt
having to create and track it all. However, there's still the
matter that libvirt needs to map a node name back to the right
host resource. I tested this patch using a qemu hack patch:
https://lists.gnu.org/archive/html/qemu-devel/2015-06/msg02858.html
which reported the device name for the active layer node, but
the active layer node is the guest view, not the host view.
* src/qemu/qemu_monitor_json.c
(qemuMonitorJSONHandleBlockWriteThreshold): New function.
* src/qemu/qemu_monitor.c (qemuMonitorEmitBlockThreshold):
Likewise.
* src/qemu/qemu_monitor.h (qemuMonitorEmitBlockThreshold):
Likewise.
* src/qemu/qemu_process.c (qemuProcessHandleBlockThreshold):
Likewise.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/qemu/qemu_monitor.c | 14 ++++++++++++++
src/qemu/qemu_monitor.h | 11 +++++++++++
src/qemu/qemu_monitor_json.c | 33 +++++++++++++++++++++++++++++++++
src/qemu/qemu_process.c | 34 ++++++++++++++++++++++++++++++++++
4 files changed, 92 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 33600f0..27e5a32 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1426,6 +1426,20 @@ qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
}
+int qemuMonitorEmitBlockThreshold(qemuMonitorPtr mon,
+ const char *diskAlias,
+ unsigned long long threshold,
+ unsigned long long length)
+{
+ int ret = -1;
+ VIR_DEBUG("mon=%p", mon);
+
+ QEMU_MONITOR_CALLBACK(mon, ret, domainBlockThreshold, mon->vm,
+ diskAlias, threshold, length);
+ 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 d30b514..381c0a6 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -146,6 +146,12 @@ typedef int (*qemuMonitorDomainBlockJobCallback)(qemuMonitorPtr mon,
int type,
int status,
void *opaque);
+typedef int (*qemuMonitorDomainBlockThresholdCallback)(qemuMonitorPtr mon,
+ virDomainObjPtr vm,
+ const char *diskAlias,
+ unsigned long long threshold,
+ unsigned long long length,
+ void *opaque);
typedef int (*qemuMonitorDomainTrayChangeCallback)(qemuMonitorPtr mon,
virDomainObjPtr vm,
const char *devAlias,
@@ -200,6 +206,7 @@ struct _qemuMonitorCallbacks {
qemuMonitorDomainIOErrorCallback domainIOError;
qemuMonitorDomainGraphicsCallback domainGraphics;
qemuMonitorDomainBlockJobCallback domainBlockJob;
+ qemuMonitorDomainBlockThresholdCallback domainBlockThreshold;
qemuMonitorDomainTrayChangeCallback domainTrayChange;
qemuMonitorDomainPMWakeupCallback domainPMWakeup;
qemuMonitorDomainPMSuspendCallback domainPMSuspend;
@@ -296,6 +303,10 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
const char *diskAlias,
int type,
int status);
+int qemuMonitorEmitBlockThreshold(qemuMonitorPtr mon,
+ const char *diskAlias,
+ unsigned long long threshold,
+ unsigned long long length);
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 13c57d2..8dd1279 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -77,6 +77,8 @@ 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 qemuMonitorJSONHandleBlockWriteThreshold(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);
@@ -95,6 +97,7 @@ static qemuEventHandler eventHandlers[] = {
{ "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
{ "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, },
+ { "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockWriteThreshold, },
{ "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, },
{ "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
{ "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
@@ -845,6 +848,36 @@ qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon,
VIR_DOMAIN_BLOCK_JOB_READY);
}
+
+static void
+qemuMonitorJSONHandleBlockWriteThreshold(qemuMonitorPtr mon,
+ virJSONValuePtr data)
+{
+ const char *device;
+ unsigned long long offset = 0, len = 0;
+
+ /* TODO: Once we support node names for more than just the active
+ * layer, we will need to map this into 'vda[1]' notation. */
+ if ((device = virJSONValueObjectGetString(data, "node-name")) == NULL) {
+ VIR_WARN("missing device in block threshold event");
+ goto out;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(data, "write-threshold",
+ &offset) < 0) {
+ VIR_WARN("missing threshold in block threshold event");
+ goto out;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &len)
< 0) {
+ VIR_WARN("missing len in block threshold event");
+ goto out;
+ }
+
+ out:
+ qemuMonitorEmitBlockThreshold(mon, device, offset, len);
+}
+
static void
qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon,
virJSONValuePtr data)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 64ee049..3109c80 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1037,6 +1037,39 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
static int
+qemuProcessHandleBlockThreshold(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+ const char *diskAlias,
+ unsigned long long threshold,
+ unsigned long long length,
+ void *opaque)
+{
+ virQEMUDriverPtr driver = opaque;
+ virDomainDiskDefPtr disk;
+ virObjectEventPtr event = NULL;
+
+ virObjectLock(vm);
+
+ VIR_DEBUG("Block threshold for device %s (domain: %p,%s) threshold %llu "
+ "length %llu", diskAlias, vm, vm->def->name, threshold,
length);
+
+ if (!(disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias)))
+ goto cleanup;
+
+ /* TODO: Once we support node names for more than just the active
+ * layer, we will need to map this into 'vda[1]' notation. */
+ event = virDomainEventWriteThresholdNewFromObj(vm, disk->dst, threshold,
+ length);
+
+ cleanup:
+ virObjectUnlock(vm);
+ if (event)
+ qemuDomainEventQueue(driver, event);
+ return 0;
+}
+
+
+static int
qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDomainObjPtr vm,
int phase,
@@ -1495,6 +1528,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
.domainIOError = qemuProcessHandleIOError,
.domainGraphics = qemuProcessHandleGraphics,
.domainBlockJob = qemuProcessHandleBlockJob,
+ .domainBlockThreshold = qemuProcessHandleBlockThreshold,
.domainTrayChange = qemuProcessHandleTrayChange,
.domainPMWakeup = qemuProcessHandlePMWakeup,
.domainPMSuspend = qemuProcessHandlePMSuspend,
--
2.4.2