qemu: migration_cookie: Add XML handling for setting up bitmap migration

In cases where we are copying the storage we need to ensure that also
bitmaps are copied properly. This patch adds migration cookie XML
infrastructure which will allow the migration sides reach consensus on
which bitmaps to migrate.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
This commit is contained in:
Peter Krempa 2021-02-08 11:12:35 +01:00
parent b0104664c6
commit f5cd60d478
2 changed files with 180 additions and 0 deletions

View File

@ -51,6 +51,7 @@ VIR_ENUM_IMPL(qemuMigrationCookieFlag,
"cpu", "cpu",
"allowReboot", "allowReboot",
"capabilities", "capabilities",
"block-dirty-bitmaps",
); );
@ -116,6 +117,39 @@ qemuMigrationCookieCapsFree(qemuMigrationCookieCapsPtr caps)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieCaps, G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieCaps,
qemuMigrationCookieCapsFree); qemuMigrationCookieCapsFree);
static void
qemuMigrationBlockDirtyBitmapsDiskBitmapFree(qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bmp)
{
if (!bmp)
return;
g_free(bmp->bitmapname);
g_free(bmp->alias);
g_free(bmp->sourcebitmap);
g_free(bmp);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDiskBitmap,
qemuMigrationBlockDirtyBitmapsDiskBitmapFree);
static void
qemuMigrationBlockDirtyBitmapsDiskFree(qemuMigrationBlockDirtyBitmapsDiskPtr dsk)
{
if (!dsk)
return;
g_free(dsk->target);
if (dsk->bitmaps)
g_slist_free_full(dsk->bitmaps,
(GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskBitmapFree);
g_free(dsk);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDisk,
qemuMigrationBlockDirtyBitmapsDiskFree);
void void
qemuMigrationCookieFree(qemuMigrationCookiePtr mig) qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
{ {
@ -135,6 +169,9 @@ qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree); g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree);
virCPUDefFree(mig->cpu); virCPUDefFree(mig->cpu);
qemuMigrationCookieCapsFree(mig->caps); qemuMigrationCookieCapsFree(mig->caps);
if (mig->blockDirtyBitmaps)
g_slist_free_full(mig->blockDirtyBitmaps,
(GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskFree);
g_free(mig); g_free(mig);
} }
@ -758,6 +795,48 @@ qemuMigrationCookieNBDXMLFormat(qemuMigrationCookieNBDPtr nbd,
} }
static void
qemuMigrationCookieBlockDirtyBitmapsFormat(virBufferPtr buf,
GSList *bitmaps)
{
g_auto(virBuffer) disksBuf = VIR_BUFFER_INIT_CHILD(buf);
GSList *nextdisk;
for (nextdisk = bitmaps; nextdisk; nextdisk = nextdisk->next) {
qemuMigrationBlockDirtyBitmapsDiskPtr disk = nextdisk->data;
g_auto(virBuffer) diskAttrBuf = VIR_BUFFER_INITIALIZER;
g_auto(virBuffer) diskChildBuf = VIR_BUFFER_INIT_CHILD(&disksBuf);
bool hasBitmaps = false;
GSList *nextbitmap;
if (disk->skip || !disk->bitmaps)
continue;
for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) {
qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap = nextbitmap->data;
if (bitmap->skip)
continue;
virBufferAsprintf(&diskChildBuf,
"<bitmap name='%s' alias='%s'/>\n",
bitmap->bitmapname, bitmap->alias);
hasBitmaps = true;
}
if (!hasBitmaps)
continue;
virBufferAsprintf(&diskAttrBuf, " target='%s'", disk->target);
virXMLFormatElement(&disksBuf, "disk", &diskAttrBuf, &diskChildBuf);
}
virXMLFormatElement(buf, "blockDirtyBitmaps", NULL, &disksBuf);
}
int int
qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver,
virQEMUCapsPtr qemuCaps, virQEMUCapsPtr qemuCaps,
@ -829,6 +908,9 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver,
if (mig->flags & QEMU_MIGRATION_COOKIE_CAPS) if (mig->flags & QEMU_MIGRATION_COOKIE_CAPS)
qemuMigrationCookieCapsXMLFormat(buf, mig->caps); qemuMigrationCookieCapsXMLFormat(buf, mig->caps);
if (mig->flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS)
qemuMigrationCookieBlockDirtyBitmapsFormat(buf, mig->blockDirtyBitmaps);
virBufferAdjustIndent(buf, -2); virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</qemu-migration>\n"); virBufferAddLit(buf, "</qemu-migration>\n");
return 0; return 0;
@ -1132,6 +1214,65 @@ qemuMigrationCookieXMLParseMandatoryFeatures(xmlXPathContextPtr ctxt,
} }
static int
qemuMigrationCookieBlockDirtyBitmapsParse(xmlXPathContextPtr ctxt,
qemuMigrationCookiePtr mig)
{
g_autoslist(qemuMigrationBlockDirtyBitmapsDisk) disks = NULL;
g_autofree xmlNodePtr *disknodes = NULL;
int ndisknodes;
size_t i;
VIR_XPATH_NODE_AUTORESTORE(ctxt)
if ((ndisknodes = virXPathNodeSet("./blockDirtyBitmaps/disk", ctxt, &disknodes)) < 0)
return -1;
for (i = 0; i < ndisknodes; i++) {
g_autoslist(qemuMigrationBlockDirtyBitmapsDiskBitmap) bitmaps = NULL;
qemuMigrationBlockDirtyBitmapsDiskPtr disk;
g_autofree xmlNodePtr *bitmapnodes = NULL;
int nbitmapnodes;
size_t j;
ctxt->node = disknodes[i];
if ((nbitmapnodes = virXPathNodeSet("./bitmap", ctxt, &bitmapnodes)) < 0)
return -1;
for (j = 0; j < nbitmapnodes; j++) {
qemuMigrationBlockDirtyBitmapsDiskBitmapPtr bitmap;
bitmap = g_new0(qemuMigrationBlockDirtyBitmapsDiskBitmap, 1);
bitmap->bitmapname = virXMLPropString(bitmapnodes[j], "name");
bitmap->alias = virXMLPropString(bitmapnodes[j], "alias");
bitmaps = g_slist_prepend(bitmaps, bitmap);
if (!bitmap->bitmapname || !bitmap->alias) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed <blockDirtyBitmaps> in migration cookie"));
return -1;
}
}
disk = g_new0(qemuMigrationBlockDirtyBitmapsDisk, 1);
disk->target = virXMLPropString(disknodes[i], "target");
disk->bitmaps = g_slist_reverse(g_steal_pointer(&bitmaps));
disks = g_slist_prepend(disks, disk);
if (!disk->target) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed <blockDirtyBitmaps> in migration cookie"));
return -1;
}
}
mig->blockDirtyBitmaps = g_slist_reverse(g_steal_pointer(&disks));
return 0;
}
static int static int
qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
virQEMUDriverPtr driver, virQEMUDriverPtr driver,
@ -1275,6 +1416,11 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
!(mig->caps = qemuMigrationCookieCapsXMLParse(ctxt))) !(mig->caps = qemuMigrationCookieCapsXMLParse(ctxt)))
return -1; return -1;
if (flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS &&
virXPathBoolean("boolean(./blockDirtyBitmaps)", ctxt) &&
qemuMigrationCookieBlockDirtyBitmapsParse(ctxt, mig) < 0)
return -1;
return 0; return 0;
} }

View File

@ -35,6 +35,7 @@ typedef enum {
QEMU_MIGRATION_COOKIE_FLAG_CPU, QEMU_MIGRATION_COOKIE_FLAG_CPU,
QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT, QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT,
QEMU_MIGRATION_COOKIE_FLAG_CAPS, QEMU_MIGRATION_COOKIE_FLAG_CAPS,
QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS,
QEMU_MIGRATION_COOKIE_FLAG_LAST QEMU_MIGRATION_COOKIE_FLAG_LAST
} qemuMigrationCookieFlags; } qemuMigrationCookieFlags;
@ -53,6 +54,7 @@ typedef enum {
QEMU_MIGRATION_COOKIE_CPU = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU), QEMU_MIGRATION_COOKIE_CPU = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU),
QEMU_MIGRATION_COOKIE_ALLOW_REBOOT = (1 << QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT), QEMU_MIGRATION_COOKIE_ALLOW_REBOOT = (1 << QEMU_MIGRATION_COOKIE_FLAG_ALLOW_REBOOT),
QEMU_MIGRATION_COOKIE_CAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_CAPS), QEMU_MIGRATION_COOKIE_CAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_CAPS),
QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS = (1 << QEMU_MIGRATION_COOKIE_FLAG_BLOCK_DIRTY_BITMAPS),
} qemuMigrationCookieFeatures; } qemuMigrationCookieFeatures;
typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics; typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
@ -107,6 +109,35 @@ struct _qemuMigrationCookieCaps {
virBitmapPtr automatic; virBitmapPtr automatic;
}; };
typedef struct _qemuMigrationBlockDirtyBitmapsDiskBitmap qemuMigrationBlockDirtyBitmapsDiskBitmap;
typedef qemuMigrationBlockDirtyBitmapsDiskBitmap *qemuMigrationBlockDirtyBitmapsDiskBitmapPtr;
struct _qemuMigrationBlockDirtyBitmapsDiskBitmap {
/* config */
char *bitmapname;
char *alias;
/* runtime */
virTristateBool persistent; /* force persisting of the bitmap */
char *sourcebitmap; /* optional, actual bitmap to migrate in case we needed
to create a temporary one by merging */
bool skip; /* omit this bitmap */
};
typedef struct _qemuMigrationBlockDirtyBitmapsDisk qemuMigrationBlockDirtyBitmapsDisk;
typedef qemuMigrationBlockDirtyBitmapsDisk *qemuMigrationBlockDirtyBitmapsDiskPtr;
struct _qemuMigrationBlockDirtyBitmapsDisk {
char *target;
GSList *bitmaps;
/* runtime data */
virDomainDiskDefPtr disk; /* disk object corresponding to 'target' */
const char *nodename; /* nodename of the top level source of 'disk' */
bool skip; /* omit this disk */
};
typedef struct _qemuMigrationCookie qemuMigrationCookie; typedef struct _qemuMigrationCookie qemuMigrationCookie;
typedef qemuMigrationCookie *qemuMigrationCookiePtr; typedef qemuMigrationCookie *qemuMigrationCookiePtr;
struct _qemuMigrationCookie { struct _qemuMigrationCookie {
@ -150,6 +181,9 @@ struct _qemuMigrationCookie {
/* If flags & QEMU_MIGRATION_COOKIE_CAPS */ /* If flags & QEMU_MIGRATION_COOKIE_CAPS */
qemuMigrationCookieCapsPtr caps; qemuMigrationCookieCapsPtr caps;
/* If flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS */
GSList *blockDirtyBitmaps;
}; };