Add some monitor commands to be used during backup/checkpoint
operations:
- another facet to query-block for learning bitmap size
- the new bitmap parameter to nbd-server-add-bitmap
- new block-dirty-bitmap-{add,enable,disable,merge} functions
Also add two capabilities for testing that they are supported;
using block-dirty-bitmap-merge as the generic witness of checkpoint
support (since all of the functionalities were added in the same
qemu 4.0 release), and the bitmap parameter to nbd-server-add for
pull-mode backup support. Even though both capabilities are
likely to be present or absent together (that is, it is unlikely
to encounter a qemu that backports only one of the two), it still
makes sense to keep two capabilities as the two uses are
orthogonal (full backups don't require checkpoints, push mode
backups don't require NBD bitmap support, and checkpoints can
be used for more than just incremental backups).
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
src/qemu/qemu_capabilities.h | 2 +
src/qemu/qemu_monitor.h | 20 +-
src/qemu/qemu_monitor_json.h | 18 +-
src/qemu/qemu_capabilities.c | 4 +
src/qemu/qemu_migration.c | 2 +-
src/qemu/qemu_monitor.c | 64 +++++-
src/qemu/qemu_monitor_json.c | 195 +++++++++++++++++-
.../caps_4.0.0.riscv32.xml | 2 +
.../caps_4.0.0.riscv64.xml | 2 +
tests/qemumonitorjsontest.c | 2 +-
10 files changed, 303 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 6d5ed8a3cc..3953e3ad3c 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -504,6 +504,8 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check
*/
/* 325 */
QEMU_CAPS_OBJECT_MEMORY_FILE_PMEM, /* -object memory-backend-file,pmem= */
QEMU_CAPS_DEVICE_NVDIMM_UNARMED, /* -device nvdimm,unarmed= */
+ QEMU_CAPS_BITMAP_MERGE, /* block-dirty-bitmap-merge */
+ QEMU_CAPS_NBD_BITMAP, /* nbd-server-add supports bitmap */
QEMU_CAPS_LAST /* this must always be the last item */
} virQEMUCapsFlags;
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 55acd60380..7eb0d067b9 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -25,6 +25,7 @@
# include "internal.h"
# include "domain_conf.h"
+# include "checkpoint_conf.h"
# include "virbitmap.h"
# include "virhash.h"
# include "virjson.h"
@@ -629,6 +630,9 @@ int qemuMonitorBlockStatsUpdateCapacity(qemuMonitorPtr mon,
int qemuMonitorBlockStatsUpdateCapacityBlockdev(qemuMonitorPtr mon,
virHashTablePtr stats)
ATTRIBUTE_NONNULL(2);
+int qemuMonitorUpdateCheckpointSize(qemuMonitorPtr mon,
+ virDomainCheckpointDefPtr chk)
+ ATTRIBUTE_NONNULL(2);
int qemuMonitorBlockResize(qemuMonitorPtr mon,
const char *device,
@@ -647,6 +651,19 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon,
unsigned long long newmem);
int qemuMonitorSetCPU(qemuMonitorPtr mon, int cpu, bool online);
+int qemuMonitorAddBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap, bool persistent)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int qemuMonitorEnableBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+int qemuMonitorMergeBitmaps(qemuMonitorPtr mon, const char *node,
+ const char *dst, virJSONValuePtr *src)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+int qemuMonitorDeleteBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+ ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
/* XXX should we pass the virDomainDiskDefPtr instead
* and hide dev_name details inside monitor. Reconsider
@@ -1100,7 +1117,8 @@ int qemuMonitorNBDServerStart(qemuMonitorPtr mon,
int qemuMonitorNBDServerAdd(qemuMonitorPtr mon,
const char *deviceID,
const char *export,
- bool writable);
+ bool writable,
+ const char *bitmap);
int qemuMonitorNBDServerStop(qemuMonitorPtr);
int qemuMonitorGetTPMModels(qemuMonitorPtr mon,
char ***tpmmodels);
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index b105964ed6..076f7b6dde 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -98,6 +98,9 @@ int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
const char *nodename,
unsigned long long size);
+int qemuMonitorJSONUpdateCheckpointSize(qemuMonitorPtr mon,
+ virDomainCheckpointDefPtr chk);
+
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password);
int qemuMonitorJSONSetPassword(qemuMonitorPtr mon,
@@ -466,7 +469,8 @@ int qemuMonitorJSONNBDServerStart(qemuMonitorPtr mon,
int qemuMonitorJSONNBDServerAdd(qemuMonitorPtr mon,
const char *deviceID,
const char *export,
- bool writable);
+ bool writable,
+ const char *bitmap);
int qemuMonitorJSONNBDServerStop(qemuMonitorPtr mon);
int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon,
char ***tpmmodels)
@@ -579,4 +583,16 @@ int qemuMonitorJSONGetPRManagerInfo(qemuMonitorPtr mon,
virHashTablePtr info)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuMonitorJSONAddBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap, bool persistent);
+
+int qemuMonitorJSONEnableBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap);
+
+int qemuMonitorJSONMergeBitmaps(qemuMonitorPtr mon, const char *node,
+ const char *dst, virJSONValuePtr *src);
+
+int qemuMonitorJSONDeleteBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap);
+
#endif /* LIBVIRT_QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 81ef0357e7..cc4f197835 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -524,6 +524,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
/* 325 */
"memory-backend-file.pmem",
"nvdimm.unarmed",
+ "bitmap-merge",
+ "nbd-bitmap",
);
@@ -979,6 +981,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = {
{ "query-cpus-fast", QEMU_CAPS_QUERY_CPUS_FAST },
{ "qom-list-properties", QEMU_CAPS_QOM_LIST_PROPERTIES },
{ "blockdev-del", QEMU_CAPS_BLOCKDEV_DEL },
+ { "block-dirty-bitmap-merge", QEMU_CAPS_BITMAP_MERGE },
};
struct virQEMUCapsStringFlags virQEMUCapsMigration[] = {
@@ -1257,6 +1260,7 @@ static struct virQEMUCapsStringFlags virQEMUCapsQMPSchemaQueries[] =
{
{ "block-commit/arg-type/*top", QEMU_CAPS_ACTIVE_COMMIT },
{ "query-iothreads/ret-type/poll-max-ns", QEMU_CAPS_IOTHREAD_POLLING },
{ "query-display-options/ret-type/+egl-headless/rendernode",
QEMU_CAPS_EGL_HEADLESS_RENDERNODE },
+ { "nbd-server-add/arg-type/bitmap", QEMU_CAPS_NBD_BITMAP },
};
typedef struct _virQEMUCapsObjectTypeProps virQEMUCapsObjectTypeProps;
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 39a228d977..1aa89809a3 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -417,7 +417,7 @@ qemuMigrationDstStartNBDServer(virQEMUDriverPtr driver,
goto exit_monitor;
}
- if (qemuMonitorNBDServerAdd(priv->mon, diskAlias, NULL, true) < 0)
+ if (qemuMonitorNBDServerAdd(priv->mon, diskAlias, NULL, true, NULL) < 0)
goto exit_monitor;
if (qemuDomainObjExitMonitor(driver, vm) < 0)
goto cleanup;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 296563ce1c..906ed50466 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2342,6 +2342,17 @@ qemuMonitorBlockStatsUpdateCapacityBlockdev(qemuMonitorPtr mon,
return qemuMonitorJSONBlockStatsUpdateCapacityBlockdev(mon, stats);
}
+/* Updates "chk" to fill in size of the associated bitmap */
+int qemuMonitorUpdateCheckpointSize(qemuMonitorPtr mon,
+ virDomainCheckpointDefPtr chk)
+{
+ VIR_DEBUG("chk=%p", chk);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONUpdateCheckpointSize(mon, chk);
+}
+
int
qemuMonitorBlockResize(qemuMonitorPtr mon,
const char *device,
@@ -3946,13 +3957,16 @@ int
qemuMonitorNBDServerAdd(qemuMonitorPtr mon,
const char *deviceID,
const char *export,
- bool writable)
+ bool writable,
+ const char *bitmap)
{
- VIR_DEBUG("deviceID=%s, export=%s", deviceID, NULLSTR(export));
+ VIR_DEBUG("deviceID=%s, export=%s, bitmap=%s", deviceID, NULLSTR(export),
+ NULLSTR(bitmap));
QEMU_CHECK_MONITOR(mon);
- return qemuMonitorJSONNBDServerAdd(mon, deviceID, export, writable);
+ return qemuMonitorJSONNBDServerAdd(mon, deviceID, export, writable,
+ bitmap);
}
@@ -4479,3 +4493,47 @@ qemuMonitorGetPRManagerInfo(qemuMonitorPtr mon,
virHashFree(info);
return ret;
}
+
+int
+qemuMonitorAddBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap, bool persistent)
+{
+ VIR_DEBUG("node=%s bitmap=%s persistent=%d", node, bitmap, persistent);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONAddBitmap(mon, node, bitmap, persistent);
+}
+
+int
+qemuMonitorEnableBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+{
+ VIR_DEBUG("node=%s bitmap=%s", node, bitmap);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONEnableBitmap(mon, node, bitmap);
+}
+
+int
+qemuMonitorMergeBitmaps(qemuMonitorPtr mon, const char *node,
+ const char *dst, virJSONValuePtr *src)
+{
+ VIR_DEBUG("node=%s dst=%s src=%p", node, dst, *src);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONMergeBitmaps(mon, node, dst, src);
+}
+
+int
+qemuMonitorDeleteBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+{
+ VIR_DEBUG("node=%s bitmap=%s", node, bitmap);
+
+ QEMU_CHECK_MONITOR(mon);
+
+ return qemuMonitorJSONDeleteBitmap(mon, node, bitmap);
+}
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 37626b64ea..06c3945670 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1011,6 +1011,8 @@ qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon,
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
else if (STREQ(type_str, "mirror"))
type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
+ else if (STREQ(type_str, "backup"))
+ type = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP;
switch ((virConnectDomainEventBlockJobStatus) event) {
case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
@@ -2755,6 +2757,82 @@ int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
return ret;
}
+int qemuMonitorJSONUpdateCheckpointSize(qemuMonitorPtr mon,
+ virDomainCheckpointDefPtr chk)
+{
+ int ret = -1;
+ size_t i, j;
+ virJSONValuePtr devices;
+
+ if (!(devices = qemuMonitorJSONQueryBlock(mon)))
+ return -1;
+
+ for (i = 0; i < virJSONValueArraySize(devices); i++) {
+ virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
+ virJSONValuePtr inserted;
+ virJSONValuePtr bitmaps = NULL;
+ const char *node;
+ virDomainCheckpointDiskDefPtr disk;
+
+ if (!(dev = qemuMonitorJSONGetBlockDev(devices, i)))
+ goto cleanup;
+
+ if (!(inserted = virJSONValueObjectGetObject(dev, "inserted")))
+ continue;
+ if (!(node = virJSONValueObjectGetString(inserted, "node-name"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("query-block device entry was not in expected
format"));
+ goto cleanup;
+ }
+
+ for (j = 0; j < chk->ndisks; j++) {
+ disk = &chk->disks[j];
+ if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
+ continue;
+ if (STREQ(chk->dom->disks[disk->idx]->src->nodeformat, node))
+ break;
+ }
+ if (j == chk->ndisks) {
+ VIR_DEBUG("query-block did not find node %s", node);
+ continue;
+ }
+ if (!(bitmaps = virJSONValueObjectGetArray(dev, "dirty-bitmaps"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("disk %s dirty bitmaps missing"), disk->name);
+ goto cleanup;
+ }
+ for (j = 0; j < virJSONValueArraySize(bitmaps); j++) {
+ virJSONValuePtr map = virJSONValueArrayGet(bitmaps, j);
+ const char *name;
+
+ if (!(name = virJSONValueObjectGetString(map, "name"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("dirty bitmaps entry was not in expected
format"));
+ goto cleanup;
+ }
+ if (STRNEQ(name, disk->bitmap))
+ continue;
+ if (virJSONValueObjectGetNumberUlong(map, "count",
&disk->size) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("invalid bitmap count"));
+ goto cleanup;
+ }
+ break;
+ }
+ if (j == virJSONValueArraySize(bitmaps)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("disk %s dirty bitmap info missing"),
disk->name);
+ goto cleanup;
+ }
+ }
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(devices);
+ return ret;
+}
+
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password)
{
@@ -6768,16 +6846,19 @@ int
qemuMonitorJSONNBDServerAdd(qemuMonitorPtr mon,
const char *deviceID,
const char *export,
- bool writable)
+ bool writable,
+ const char *bitmap)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
+ /* Note: bitmap must be NULL if QEMU_CAPS_NBD_BITMAP is lacking */
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-add",
"s:device", deviceID,
"S:name", export,
"b:writable", writable,
+ "S:bitmap", bitmap,
NULL)))
return ret;
@@ -8486,3 +8567,115 @@ qemuMonitorJSONGetPRManagerInfo(qemuMonitorPtr mon,
return ret;
}
+
+int
+qemuMonitorJSONAddBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap, bool persistent)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-add",
+ "s:node", node,
+ "s:name", bitmap,
+ "b:persistent", persistent,
+ NULL)))
+ return -1;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+int
+qemuMonitorJSONEnableBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-enable",
+ "s:node", node,
+ "s:name", bitmap,
+ NULL)))
+ return -1;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+int
+qemuMonitorJSONMergeBitmaps(qemuMonitorPtr mon, const char *node,
+ const char *dst, virJSONValuePtr *src)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-merge",
+ "s:node", node,
+ "s:target", dst,
+ "a:bitmaps", src,
+ NULL)))
+ goto cleanup;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(*src);
+ *src = NULL;
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+int
+qemuMonitorJSONDeleteBitmap(qemuMonitorPtr mon, const char *node,
+ const char *bitmap)
+{
+ int ret = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+
+ if (!(cmd = qemuMonitorJSONMakeCommand("block-dirty-bitmap-remove",
+ "s:node", node,
+ "s:name", bitmap,
+ NULL)))
+ return -1;
+
+ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
+ goto cleanup;
+
+ if (qemuMonitorJSONCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ ret = 0;
+ cleanup:
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml
b/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml
index 15e447742d..195b9132c6 100644
--- a/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml
+++ b/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml
@@ -167,6 +167,8 @@
<flag name='egl-headless.rendernode'/>
<flag name='memory-backend-file.align'/>
<flag name='memory-backend-file.pmem'/>
+ <flag name='bitmap-merge'/>
+ <flag name='nbd-bitmap'/>
<version>3001050</version>
<kvmVersion>0</kvmVersion>
<microcodeVersion>0</microcodeVersion>
diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml
b/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml
index 066c892eaa..4263edbb55 100644
--- a/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml
+++ b/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml
@@ -167,6 +167,8 @@
<flag name='egl-headless.rendernode'/>
<flag name='memory-backend-file.align'/>
<flag name='memory-backend-file.pmem'/>
+ <flag name='bitmap-merge'/>
+ <flag name='nbd-bitmap'/>
<version>3001050</version>
<kvmVersion>0</kvmVersion>
<microcodeVersion>0</microcodeVersion>
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index 1bdef11d15..c5eac06cc1 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -1354,7 +1354,7 @@ GEN_TEST_FUNC(qemuMonitorJSONDrivePivot, "vdb")
GEN_TEST_FUNC(qemuMonitorJSONScreendump, "devicename", 1,
"/foo/bar")
GEN_TEST_FUNC(qemuMonitorJSONOpenGraphics, "spice", "spicefd",
false)
GEN_TEST_FUNC(qemuMonitorJSONNBDServerStart, "localhost", 12345,
"test-alias")
-GEN_TEST_FUNC(qemuMonitorJSONNBDServerAdd, "vda", NULL, true)
+GEN_TEST_FUNC(qemuMonitorJSONNBDServerAdd, "vda", NULL, true, NULL)
GEN_TEST_FUNC(qemuMonitorJSONDetachCharDev, "serial1")
GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayOpen, "foodev", true)
GEN_TEST_FUNC(qemuMonitorJSONBlockdevTrayClose, "foodev")
--
2.20.1