[PATCH v2 1/2] qemu: add support for shmem-{plain, doorbell} role

Role(master or peer) controls how the domain behaves on migration. For more details about migration with ivshmem, see https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/system/ivshmem.rst;hb=H... It's a optional attribute in libvirt, and qemu will choose default role for ivshmem device if the user is not specified. With device property 'role', the value can be 'master' or 'peer'. - 'master' (means 'master=on' in qemu), the guest will copy the shared memory on migration to the destination host. - 'peer' (means 'master=off' in qemu), the migration is disabled. Signed-off-by: Martin Kletzander <mkletzan@redhat.com> Signed-off-by: Yang Hang <yanghang44@huawei.com> Signed-off-by: Wang Xin <wangxinxin.wang@huawei.com> diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f5ee97de81..d4d90ad44d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -9141,7 +9141,7 @@ qemu-kvm -net nic,model=? /dev/null <pre> ... <devices> - <shmem name='my_shmem0'> + <shmem name='my_shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> </shmem> @@ -9159,9 +9159,15 @@ qemu-kvm -net nic,model=? /dev/null <dt><code>shmem</code></dt> <dd> The <code>shmem</code> element has one mandatory attribute, - <code>name</code> to identify the shared memory. This attribute cannot - be directory specific to <code>.</code> or <code>..</code> as well as - it cannot involve path separator <code>/</code>. + <code>name</code> to identify the shared memory. Optional attribute + <code>role</code> can be set to values "master" or "peer". The former + will mean that upon migration, the data in the shared memory is migrated + with the domain. There should be only one "master" per shared memory object. + Migration with "peer" role is disabled. If migration of such domain is required, + the shmem device needs to be unplugged before migration and plugged in at the + destination upon successful migration. This attribute cannot be directory + specific to <code>.</code> or <code>..</code> as well as it cannot involve path + separator <code>/</code>. </dd> <dt><code>model</code></dt> <dd> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..09d4ad3e96 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4417,6 +4417,14 @@ <param name="pattern">[^/]*</param> </data> </attribute> + <optional> + <attribute name="role"> + <choice> + <value>master</value> + <value>peer</value> + </choice> + </attribute> + </optional> <interleave> <optional> <element name="model"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..6e67c7ebe0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1325,6 +1325,13 @@ VIR_ENUM_IMPL(virDomainShmemModel, "ivshmem-doorbell", ); +VIR_ENUM_IMPL(virDomainShmemRole, + VIR_DOMAIN_SHMEM_ROLE_LAST, + "default", + "master", + "peer", +); + VIR_ENUM_IMPL(virDomainLaunchSecurity, VIR_DOMAIN_LAUNCH_SECURITY_LAST, "", @@ -15357,6 +15364,19 @@ virDomainShmemDefParseXML(virDomainXMLOptionPtr xmlopt, goto cleanup; } + if (def->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + tmp = virXMLPropString(node, "role"); + if (tmp) { + if ((def->role = virDomainShmemRoleTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown shmem role type '%s'"), tmp); + goto cleanup; + } + + VIR_FREE(tmp); + } + } + if (virParseScaledValue("./size[1]", NULL, ctxt, &def->size, 1, ULLONG_MAX, false) < 0) goto cleanup; @@ -18579,6 +18599,9 @@ virDomainShmemDefEquals(virDomainShmemDefPtr src, if (src->model != dst->model) return false; + if (src->role != dst->role) + return false; + if (src->server.enabled != dst->server.enabled) return false; @@ -23758,6 +23781,15 @@ virDomainShmemDefCheckABIStability(virDomainShmemDefPtr src, return false; } + if (src->role != dst->role) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target shared memory role '%s' does not match " + "source role '%s'"), + virDomainShmemRoleTypeToString(dst->role), + virDomainShmemRoleTypeToString(src->role)); + return false; + } + if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory model '%s' does not match " @@ -27408,8 +27440,12 @@ virDomainShmemDefFormat(virBufferPtr buf, virDomainShmemDefPtr def, unsigned int flags) { - virBufferEscapeString(buf, "<shmem name='%s'>\n", def->name); + virBufferEscapeString(buf, "<shmem name='%s'", def->name); + if (def->role) + virBufferEscapeString(buf, " role='%s'", + virDomainShmemRoleTypeToString(def->role)); + virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "<model type='%s'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..855c144ddb 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1771,10 +1771,19 @@ typedef enum { VIR_DOMAIN_SHMEM_MODEL_LAST } virDomainShmemModel; +typedef enum { + VIR_DOMAIN_SHMEM_ROLE_DEFAULT, + VIR_DOMAIN_SHMEM_ROLE_MASTER, + VIR_DOMAIN_SHMEM_ROLE_PEER, + + VIR_DOMAIN_SHMEM_ROLE_LAST +} virDomainShmemRole; + struct _virDomainShmemDef { char *name; unsigned long long size; int model; /* enum virDomainShmemModel */ + int role; /* enum virDomainShmemRole */ struct { bool enabled; virDomainChrSourceDef chr; @@ -3624,6 +3633,7 @@ VIR_ENUM_DECL(virDomainMemoryAllocation); VIR_ENUM_DECL(virDomainIOMMUModel); VIR_ENUM_DECL(virDomainVsockModel); VIR_ENUM_DECL(virDomainShmemModel); +VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73b72c9e10..3f28943575 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -597,6 +597,8 @@ virDomainShmemDefInsert; virDomainShmemDefRemove; virDomainShmemModelTypeFromString; virDomainShmemModelTypeToString; +virDomainShmemRoleTypeFromString; +virDomainShmemRoleTypeToString; virDomainShutdownReasonTypeFromString; virDomainShutdownReasonTypeToString; virDomainShutoffReasonTypeFromString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 839c93bfb4..0655d8359d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8539,11 +8539,24 @@ qemuBuildShmemDevStr(virDomainDefPtr def, virBufferAdd(&buf, virDomainShmemModelTypeToString(shmem->model), -1); virBufferAsprintf(&buf, ",id=%s", shmem->info.alias); - if (shmem->server.enabled) + if (shmem->server.enabled) { virBufferAsprintf(&buf, ",chardev=char%s", shmem->info.alias); - else + } else { virBufferAsprintf(&buf, ",memdev=shmmem-%s", shmem->info.alias); + switch ((virDomainShmemRole) shmem->role) { + case VIR_DOMAIN_SHMEM_ROLE_MASTER: + virBufferAddLit(&buf, ",master=on"); + break; + case VIR_DOMAIN_SHMEM_ROLE_PEER: + virBufferAddLit(&buf, ",master=off"); + break; + case VIR_DOMAIN_SHMEM_ROLE_DEFAULT: + case VIR_DOMAIN_SHMEM_ROLE_LAST: + break; + } + } + if (shmem->msi.vectors) virBufferAsprintf(&buf, ",vectors=%u", shmem->msi.vectors); if (shmem->msi.ioeventfd) { diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 78e64344f6..5ae85c7ae7 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1261,10 +1261,22 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, } } - if (vm->def->nshmems) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("migration with shmem device is not supported")); - return false; + for (i = 0; i < vm->def->nshmems; i++) { + virDomainShmemDefPtr shmem = vm->def->shmems[i]; + + if (shmem->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("migration with legacy shmem device is not supported")); + return false; + } + if (shmem->role == VIR_DOMAIN_SHMEM_ROLE_PEER) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("domain's shmem device '%s' has role='%s', " + "try unplugging it first"), + shmem->name, + virDomainShmemRoleTypeToString(shmem->role)); + return false; + } } for (i = 0; i < vm->def->nnets; i++) { diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml index 68f592fb21..338017aa28 100644 --- a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml +++ b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml @@ -1,4 +1,4 @@ -<shmem name='shmem0'> +<shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> <alias name='shmem0'/> diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml index 6bd96ff167..780e49de23 100644 --- a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml +++ b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml @@ -1,3 +1,3 @@ -<shmem name='shmem0'> +<shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> </shmem> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml index a6930bfa69..8013264989 100644 --- a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml @@ -45,7 +45,7 @@ <alias name='input1'/> </input> <memballoon model='none'/> - <shmem name='shmem0'> + <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> <alias name='shmem0'/> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml index 757b6b0980..0490310760 100644 --- a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml @@ -45,7 +45,7 @@ <alias name='input1'/> </input> <memballoon model='none'/> - <shmem name='shmem0'> + <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> <alias name='shmem0'/> diff --git a/tests/qemuxml2argvdata/shmem-plain-doorbell.args b/tests/qemuxml2argvdata/shmem-plain-doorbell.args index 6ed86b7448..aa41d5391a 100644 --- a/tests/qemuxml2argvdata/shmem-plain-doorbell.args +++ b/tests/qemuxml2argvdata/shmem-plain-doorbell.args @@ -30,10 +30,10 @@ size=4194304,share=yes \ -device ivshmem-plain,id=shmem0,memdev=shmmem-shmem0,bus=pci.0,addr=0x3 \ -object memory-backend-file,id=shmmem-shmem1,mem-path=/dev/shm/shmem1,\ size=134217728,share=yes \ --device ivshmem-plain,id=shmem1,memdev=shmmem-shmem1,bus=pci.0,addr=0x5 \ +-device ivshmem-plain,id=shmem1,memdev=shmmem-shmem1,master=off,bus=pci.0,addr=0x5 \ -object memory-backend-file,id=shmmem-shmem2,mem-path=/dev/shm/shmem2,\ size=268435456,share=yes \ --device ivshmem-plain,id=shmem2,memdev=shmmem-shmem2,bus=pci.0,addr=0x4 \ +-device ivshmem-plain,id=shmem2,memdev=shmmem-shmem2,master=on,bus=pci.0,addr=0x4 \ -device ivshmem-doorbell,id=shmem3,chardev=charshmem3,ioeventfd=on,bus=pci.0,\ addr=0x6 \ -chardev socket,id=charshmem3,path=/var/lib/libvirt/shmem-shmem3-sock \ diff --git a/tests/qemuxml2argvdata/shmem-plain-doorbell.xml b/tests/qemuxml2argvdata/shmem-plain-doorbell.xml index e248d637a5..7c76e0fbba 100644 --- a/tests/qemuxml2argvdata/shmem-plain-doorbell.xml +++ b/tests/qemuxml2argvdata/shmem-plain-doorbell.xml @@ -22,11 +22,11 @@ <shmem name='shmem0'> <model type='ivshmem-plain'/> </shmem> - <shmem name='shmem1'> + <shmem name='shmem1' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>128</size> </shmem> - <shmem name='shmem2'> + <shmem name='shmem2' role='master'> <model type='ivshmem-plain'/> <size unit='M'>256</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> diff --git a/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml b/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml index c5dd0f4598..64c0a7d753 100644 --- a/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml +++ b/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml @@ -26,12 +26,12 @@ <size unit='M'>4</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </shmem> - <shmem name='shmem1'> + <shmem name='shmem1' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>128</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> </shmem> - <shmem name='shmem2'> + <shmem name='shmem2' role='master'> <model type='ivshmem-plain'/> <size unit='M'>256</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index c17e3303b0..6cb2718767 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -1135,7 +1135,8 @@ mymain(void) DO_TEST("tap-vhost", NONE); DO_TEST("tap-vhost-incorrect", NONE); DO_TEST("shmem", NONE); - DO_TEST("shmem-plain-doorbell", NONE); + DO_TEST("shmem-plain-doorbell", + QEMU_CAPS_DEVICE_IVSHMEM_PLAIN, QEMU_CAPS_DEVICE_IVSHMEM_DOORBELL); DO_TEST("smbios", NONE); DO_TEST("smbios-multiple-type2", NONE); DO_TEST("smbios-type-fwcfg", NONE); -- 2.26.0.windows.1

The shared memory path is generated by shmem name as default, however, we may need to change it to avoid filename conflict when VM migrate to other host. Signed-off-by: Yang Hang <yanghang44@huawei.com> Signed-off-by: Wang Xin <wangxinxin.wang@huawei.com> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 09d4ad3e96..27fa6306df 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4442,6 +4442,13 @@ <ref name="scaledInteger"/> </element> </optional> + <optional> + <element name="mem"> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + </element> + </optional> <optional> <element name="server"> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6e67c7ebe0..41018b12d8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2906,6 +2906,7 @@ void virDomainShmemDefFree(virDomainShmemDefPtr def) virDomainDeviceInfoClear(&def->info); virDomainChrSourceDefClear(&def->server.chr); + VIR_FREE(def->memPath); VIR_FREE(def->name); VIR_FREE(def); } @@ -15381,6 +15382,12 @@ virDomainShmemDefParseXML(virDomainXMLOptionPtr xmlopt, &def->size, 1, ULLONG_MAX, false) < 0) goto cleanup; + tmp = virXPathString("string(./mem/@path)", ctxt); + if (tmp) { + def->memPath = virFileSanitizePath(tmp); + VIR_FREE(tmp); + } + if ((server = virXPathNode("./server[1]", ctxt))) { def->server.enabled = true; @@ -27451,6 +27458,12 @@ virDomainShmemDefFormat(virBufferPtr buf, virBufferAsprintf(buf, "<model type='%s'/>\n", virDomainShmemModelTypeToString(def->model)); + if (def->memPath) { + virBufferAddLit(buf, "<mem"); + virBufferEscapeString(buf, " path='%s'", def->memPath); + virBufferAddLit(buf, "/>\n"); + } + if (def->size) virBufferAsprintf(buf, "<size unit='M'>%llu</size>\n", def->size >> 20); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 855c144ddb..58fd4f2122 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1782,6 +1782,7 @@ typedef enum { struct _virDomainShmemDef { char *name; unsigned long long size; + char *memPath; int model; /* enum virDomainShmemModel */ int role; /* enum virDomainShmemRole */ struct { diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 0655d8359d..db82f66974 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8578,7 +8578,10 @@ qemuBuildShmemBackendMemProps(virDomainShmemDefPtr shmem) g_autofree char *mem_path = NULL; virJSONValuePtr ret = NULL; - mem_path = g_strdup_printf("/dev/shm/%s", shmem->name); + if (!shmem->memPath) + mem_path = g_strdup_printf("/dev/shm/%s", shmem->name); + else + mem_path = g_strdup_printf("%s", shmem->memPath); mem_alias = g_strdup_printf("shmmem-%s", shmem->info.alias); diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml index 338017aa28..3970193a80 100644 --- a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml +++ b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain-detach.xml @@ -1,5 +1,6 @@ <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem0'/> <size unit='M'>4</size> <alias name='shmem0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml index 780e49de23..e5c5f61d66 100644 --- a/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml +++ b/tests/qemuhotplugtestdevices/qemuhotplug-ivshmem-plain.xml @@ -1,3 +1,4 @@ <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem0'/> </shmem> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml index 8013264989..0ef2c5027a 100644 --- a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-doorbell.xml @@ -47,6 +47,7 @@ <memballoon model='none'/> <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem0'/> <size unit='M'>4</size> <alias name='shmem0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml index 0490310760..cb9c1238d4 100644 --- a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+ivshmem-plain.xml @@ -47,6 +47,7 @@ <memballoon model='none'/> <shmem name='shmem0' role='peer'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem0'/> <size unit='M'>4</size> <alias name='shmem0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> diff --git a/tests/qemuxml2argvdata/shmem-plain-doorbell.xml b/tests/qemuxml2argvdata/shmem-plain-doorbell.xml index 7c76e0fbba..ccad19efe2 100644 --- a/tests/qemuxml2argvdata/shmem-plain-doorbell.xml +++ b/tests/qemuxml2argvdata/shmem-plain-doorbell.xml @@ -28,6 +28,7 @@ </shmem> <shmem name='shmem2' role='master'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem2'/> <size unit='M'>256</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </shmem> diff --git a/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml b/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml index 64c0a7d753..aa1b59a50b 100644 --- a/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml +++ b/tests/qemuxml2xmloutdata/shmem-plain-doorbell.xml @@ -33,6 +33,7 @@ </shmem> <shmem name='shmem2' role='master'> <model type='ivshmem-plain'/> + <mem path='/dev/shm/shmem2'/> <size unit='M'>256</size> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </shmem> -- 2.26.0.windows.1

On Fri, Jul 17, 2020 at 09:04:51PM +0800, Wang Xin wrote:
The shared memory path is generated by shmem name as default, however, we may need to change it to avoid filename conflict when VM migrate to other host.
At which point there is no need for the name at all. I agree that having the 'name' was an unfortunate decision, but adding more attributes does not seem like a proper fix. If it needs to be changed then we should allow changing the name in the process. You can also unplug the old name and plug in the new one, just like you'd have to do with role='peer'. Did I miss any other reason for this?

On Fri, Jul 17, 2020 at 09:04:51PM +0800, Wang Xin wrote:
The shared memory path is generated by shmem name as default, however, we may need to change it to avoid filename conflict when VM migrate to other host.
At which point there is no need for the name at all. I agree that having the 'name' was an unfortunate decision, but adding more attributes does not seem like a proper fix. If it needs to be changed then we should allow changing the name in the process. You can also unplug the old name and plug in the new one, just like you'd have to do with role='peer'. Did I miss any other reason for this?
e.g. Ivshmem config: <shmem name='abcdefg'> <model type='ivshmem-plain'/> <size unit='M'>4</size> <alias name='shmem0'/> <!--auto generated in runtime--> <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </shmem> qemu args: -object memory-backend-file,id=shmmem-shmem0,mem-path=/dev/shm/abcdefg,size=4194304,share=yes -device ivshmem-plain,id=shmem0,memdev=shmmem-shmem0,bus=pci.0,addr=0x3 The shmem 'name' usually mean the ivshmem device name/id, I think we shouldn't change it in migration. However, here we use the auto generated 'alias name' as device name/id instead. Add a new optional attribute for easier understanding what we changed, no other reason. Update the shmem 'name' will also work, I can support it in V3.

On Fri, Jul 17, 2020 at 09:04:50PM +0800, Wang Xin wrote:
Role(master or peer) controls how the domain behaves on migration. For more details about migration with ivshmem, see https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/system/ivshmem.rst;hb=H...
It's a optional attribute in libvirt, and qemu will choose default role for ivshmem device if the user is not specified.
With device property 'role', the value can be 'master' or 'peer'. - 'master' (means 'master=on' in qemu), the guest will copy the shared memory on migration to the destination host. - 'peer' (means 'master=off' in qemu), the migration is disabled.
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
I do not remember signing off this patch. Is this a re-spin of some old series? I see with this v2 you fixed the `make check`. We tend to use the cover letters for that, it's nicely recognisable what are the changes there. However make syntax-check still fails after this patch, even though that's just a minor whitespace change that can be done automatically. Not only make syntax-check outputs the diff for you (which you can apply), but it also suggests the script we have there that does all of that for you.
Signed-off-by: Yang Hang <yanghang44@huawei.com> Signed-off-by: Wang Xin <wangxinxin.wang@huawei.com>
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f5ee97de81..d4d90ad44d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -9141,7 +9141,7 @@ qemu-kvm -net nic,model=? /dev/null <pre> ... <devices> - <shmem name='my_shmem0'> + <shmem name='my_shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> </shmem> @@ -9159,9 +9159,15 @@ qemu-kvm -net nic,model=? /dev/null <dt><code>shmem</code></dt> <dd> The <code>shmem</code> element has one mandatory attribute, - <code>name</code> to identify the shared memory. This attribute cannot - be directory specific to <code>.</code> or <code>..</code> as well as - it cannot involve path separator <code>/</code>. + <code>name</code> to identify the shared memory. Optional attribute + <code>role</code> can be set to values "master" or "peer". The former + will mean that upon migration, the data in the shared memory is migrated + with the domain. There should be only one "master" per shared memory object. + Migration with "peer" role is disabled. If migration of such domain is required, + the shmem device needs to be unplugged before migration and plugged in at the + destination upon successful migration. This attribute cannot be directory + specific to <code>.</code> or <code>..</code> as well as it cannot involve path + separator <code>/</code>.
You wrote your text in the middle of the explanation of another attribute. Now it looks like the optional attribute "role" can be set to values "master" or "peer", cannot be a directory specific to "." or ".." and it cannot use a path separator "/". It's a bit confusing, even though it is still true =) You should also describe what leaving out that attribute does. We usually leave it to the hypervisor to decide although this might be enough of a difference that we might fill in the default ourselves. But I'll leave the decision up to you.
</dd> <dt><code>model</code></dt> <dd> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..09d4ad3e96 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4417,6 +4417,14 @@ <param name="pattern">[^/]*</param> </data> </attribute> + <optional> + <attribute name="role"> + <choice> + <value>master</value> + <value>peer</value> + </choice> + </attribute> + </optional> <interleave> <optional> <element name="model"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..6e67c7ebe0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1325,6 +1325,13 @@ VIR_ENUM_IMPL(virDomainShmemModel, "ivshmem-doorbell", );
+VIR_ENUM_IMPL(virDomainShmemRole, + VIR_DOMAIN_SHMEM_ROLE_LAST, + "default", + "master", + "peer", +); + VIR_ENUM_IMPL(virDomainLaunchSecurity, VIR_DOMAIN_LAUNCH_SECURITY_LAST, "", @@ -15357,6 +15364,19 @@ virDomainShmemDefParseXML(virDomainXMLOptionPtr xmlopt, goto cleanup; }
+ if (def->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + tmp = virXMLPropString(node, "role"); + if (tmp) { + if ((def->role = virDomainShmemRoleTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown shmem role type '%s'"), tmp); + goto cleanup; + } + + VIR_FREE(tmp); + } + } + if (virParseScaledValue("./size[1]", NULL, ctxt, &def->size, 1, ULLONG_MAX, false) < 0) goto cleanup; @@ -18579,6 +18599,9 @@ virDomainShmemDefEquals(virDomainShmemDefPtr src, if (src->model != dst->model) return false;
+ if (src->role != dst->role) + return false; + if (src->server.enabled != dst->server.enabled) return false;
@@ -23758,6 +23781,15 @@ virDomainShmemDefCheckABIStability(virDomainShmemDefPtr src, return false; }
+ if (src->role != dst->role) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target shared memory role '%s' does not match " + "source role '%s'"), + virDomainShmemRoleTypeToString(dst->role), + virDomainShmemRoleTypeToString(src->role)); + return false; + } + if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory model '%s' does not match " @@ -27408,8 +27440,12 @@ virDomainShmemDefFormat(virBufferPtr buf, virDomainShmemDefPtr def, unsigned int flags) { - virBufferEscapeString(buf, "<shmem name='%s'>\n", def->name); + virBufferEscapeString(buf, "<shmem name='%s'", def->name); + if (def->role) + virBufferEscapeString(buf, " role='%s'", + virDomainShmemRoleTypeToString(def->role));
+ virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2);
virBufferAsprintf(buf, "<model type='%s'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..855c144ddb 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1771,10 +1771,19 @@ typedef enum { VIR_DOMAIN_SHMEM_MODEL_LAST } virDomainShmemModel;
+typedef enum { + VIR_DOMAIN_SHMEM_ROLE_DEFAULT, + VIR_DOMAIN_SHMEM_ROLE_MASTER, + VIR_DOMAIN_SHMEM_ROLE_PEER, + + VIR_DOMAIN_SHMEM_ROLE_LAST +} virDomainShmemRole; + struct _virDomainShmemDef { char *name; unsigned long long size; int model; /* enum virDomainShmemModel */ + int role; /* enum virDomainShmemRole */ struct { bool enabled; virDomainChrSourceDef chr; @@ -3624,6 +3633,7 @@ VIR_ENUM_DECL(virDomainMemoryAllocation); VIR_ENUM_DECL(virDomainIOMMUModel); VIR_ENUM_DECL(virDomainVsockModel); VIR_ENUM_DECL(virDomainShmemModel); +VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73b72c9e10..3f28943575 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -597,6 +597,8 @@ virDomainShmemDefInsert; virDomainShmemDefRemove; virDomainShmemModelTypeFromString; virDomainShmemModelTypeToString; +virDomainShmemRoleTypeFromString; +virDomainShmemRoleTypeToString; virDomainShutdownReasonTypeFromString; virDomainShutdownReasonTypeToString; virDomainShutoffReasonTypeFromString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 839c93bfb4..0655d8359d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8539,11 +8539,24 @@ qemuBuildShmemDevStr(virDomainDefPtr def, virBufferAdd(&buf, virDomainShmemModelTypeToString(shmem->model), -1); virBufferAsprintf(&buf, ",id=%s", shmem->info.alias);
- if (shmem->server.enabled) + if (shmem->server.enabled) { virBufferAsprintf(&buf, ",chardev=char%s", shmem->info.alias); - else + } else { virBufferAsprintf(&buf, ",memdev=shmmem-%s", shmem->info.alias);
+ switch ((virDomainShmemRole) shmem->role) { + case VIR_DOMAIN_SHMEM_ROLE_MASTER: + virBufferAddLit(&buf, ",master=on"); + break; + case VIR_DOMAIN_SHMEM_ROLE_PEER: + virBufferAddLit(&buf, ",master=off"); + break; + case VIR_DOMAIN_SHMEM_ROLE_DEFAULT: + case VIR_DOMAIN_SHMEM_ROLE_LAST: + break; + } + } + if (shmem->msi.vectors) virBufferAsprintf(&buf, ",vectors=%u", shmem->msi.vectors); if (shmem->msi.ioeventfd) { diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 78e64344f6..5ae85c7ae7 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1261,10 +1261,22 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, } }
- if (vm->def->nshmems) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("migration with shmem device is not supported")); - return false; + for (i = 0; i < vm->def->nshmems; i++) { + virDomainShmemDefPtr shmem = vm->def->shmems[i]; + + if (shmem->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("migration with legacy shmem device is not supported")); + return false; + } + if (shmem->role == VIR_DOMAIN_SHMEM_ROLE_PEER) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("domain's shmem device '%s' has role='%s', " + "try unplugging it first"),
I, for one, like similar useful suggestions here, but in this case it is true that it is nothing special compared to other devices that cannot be migrated. So this one probably should not be suggesting it, having it in the documentation is enough. And it's still missing the reason for failure, the fact that the migration is not supported with such device/role combination. The rest looks fine to me, so you can keep the S-o-b with the appropriate changes.

On Fri, Jul 17, 2020 at 09:04:50PM +0800, Wang Xin wrote:
Role(master or peer) controls how the domain behaves on migration. For more details about migration with ivshmem, see https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/system/ivshmem.rst;hb=H...
It's a optional attribute in libvirt, and qemu will choose default role for ivshmem device if the user is not specified.
With device property 'role', the value can be 'master' or 'peer'. - 'master' (means 'master=on' in qemu), the guest will copy the shared memory on migration to the destination host. - 'peer' (means 'master=off' in qemu), the migration is disabled.
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
I do not remember signing off this patch. Is this a re-spin of some old series?
It's about 4 years ago, https://www.redhat.com/archives/libvir-list/2016-September/msg00536.html And as you mentioned in msg(see link), you may had a plan to do it, but I didn't find the patch. https://www.redhat.com/archives/libvir-list/2016-August/msg00591.html So, I do some tests and re-send your first patch.
I see with this v2 you fixed the `make check`. We tend to use the cover letters for that, it's nicely recognisable what are the changes there. However make syntax-check still fails after this patch, even though that's just a minor whitespace change that can be done automatically. Not only make syntax-check outputs the diff for you (which you can apply), but it also suggests the script we have there that does all of that for you.
Sorry for missing the check, I will fix it in V3, and list the changes in cover letter.
Signed-off-by: Yang Hang <yanghang44@huawei.com> Signed-off-by: Wang Xin <wangxinxin.wang@huawei.com>
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index f5ee97de81..d4d90ad44d 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -9141,7 +9141,7 @@ qemu-kvm -net nic,model=? /dev/null <pre> ... <devices> - <shmem name='my_shmem0'> + <shmem name='my_shmem0' role='peer'> <model type='ivshmem-plain'/> <size unit='M'>4</size> </shmem> @@ -9159,9 +9159,15 @@ qemu-kvm -net nic,model=? /dev/null <dt><code>shmem</code></dt> <dd> The <code>shmem</code> element has one mandatory attribute, - <code>name</code> to identify the shared memory. This attribute cannot - be directory specific to <code>.</code> or <code>..</code> as well as - it cannot involve path separator <code>/</code>. + <code>name</code> to identify the shared memory. Optional attribute + <code>role</code> can be set to values "master" or "peer". The former + will mean that upon migration, the data in the shared memory is migrated + with the domain. There should be only one "master" per shared memory object. + Migration with "peer" role is disabled. If migration of such domain is required, + the shmem device needs to be unplugged before migration and plugged in at the + destination upon successful migration. This attribute cannot be directory + specific to <code>.</code> or <code>..</code> as well as it cannot involve path + separator <code>/</code>.
You wrote your text in the middle of the explanation of another attribute. Now it looks like the optional attribute "role" can be set to values "master" or "peer", cannot be a directory specific to "." or ".." and it cannot use a path separator "/". It's a bit confusing, even though it is still true =)
You should also describe what leaving out that attribute does. We usually leave it to the hypervisor to decide although this might be enough of a difference that we might fill in the default ourselves. But I'll leave the decision up to you.
Good suggestion, accept.
</dd> <dt><code>model</code></dt> <dd> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a810f569c6..09d4ad3e96 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4417,6 +4417,14 @@ <param name="pattern">[^/]*</param> </data> </attribute> + <optional> + <attribute name="role"> + <choice> + <value>master</value> + <value>peer</value> + </choice> + </attribute> + </optional> <interleave> <optional> <element name="model"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7ecd2818b9..6e67c7ebe0 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -1325,6 +1325,13 @@ VIR_ENUM_IMPL(virDomainShmemModel, "ivshmem-doorbell", );
+VIR_ENUM_IMPL(virDomainShmemRole, + VIR_DOMAIN_SHMEM_ROLE_LAST, + "default", + "master", + "peer", +); + VIR_ENUM_IMPL(virDomainLaunchSecurity, VIR_DOMAIN_LAUNCH_SECURITY_LAST, "", @@ -15357,6 +15364,19 @@ virDomainShmemDefParseXML(virDomainXMLOptionPtr xmlopt, goto cleanup; }
+ if (def->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + tmp = virXMLPropString(node, "role"); + if (tmp) { + if ((def->role = virDomainShmemRoleTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown shmem role type '%s'"), tmp); + goto cleanup; + } + + VIR_FREE(tmp); + } + } + if (virParseScaledValue("./size[1]", NULL, ctxt, &def->size, 1, ULLONG_MAX, false) < 0) goto cleanup; @@ -18579,6 +18599,9 @@ virDomainShmemDefEquals(virDomainShmemDefPtr src, if (src->model != dst->model) return false;
+ if (src->role != dst->role) + return false; + if (src->server.enabled != dst->server.enabled) return false;
@@ -23758,6 +23781,15 @@ virDomainShmemDefCheckABIStability(virDomainShmemDefPtr src, return false; }
+ if (src->role != dst->role) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target shared memory role '%s' does not match " + "source role '%s'"), + virDomainShmemRoleTypeToString(dst->role), + virDomainShmemRoleTypeToString(src->role)); + return false; + } + if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory model '%s' does not match " @@ -27408,8 +27440,12 @@ virDomainShmemDefFormat(virBufferPtr buf, virDomainShmemDefPtr def, unsigned int flags) { - virBufferEscapeString(buf, "<shmem name='%s'>\n", def->name); + virBufferEscapeString(buf, "<shmem name='%s'", def->name); + if (def->role) + virBufferEscapeString(buf, " role='%s'", + virDomainShmemRoleTypeToString(def->role));
+ virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2);
virBufferAsprintf(buf, "<model type='%s'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 241149af24..855c144ddb 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1771,10 +1771,19 @@ typedef enum { VIR_DOMAIN_SHMEM_MODEL_LAST } virDomainShmemModel;
+typedef enum { + VIR_DOMAIN_SHMEM_ROLE_DEFAULT, + VIR_DOMAIN_SHMEM_ROLE_MASTER, + VIR_DOMAIN_SHMEM_ROLE_PEER, + + VIR_DOMAIN_SHMEM_ROLE_LAST +} virDomainShmemRole; + struct _virDomainShmemDef { char *name; unsigned long long size; int model; /* enum virDomainShmemModel */ + int role; /* enum virDomainShmemRole */ struct { bool enabled; virDomainChrSourceDef chr; @@ -3624,6 +3633,7 @@ VIR_ENUM_DECL(virDomainMemoryAllocation); VIR_ENUM_DECL(virDomainIOMMUModel); VIR_ENUM_DECL(virDomainVsockModel); VIR_ENUM_DECL(virDomainShmemModel); +VIR_ENUM_DECL(virDomainShmemRole); VIR_ENUM_DECL(virDomainLaunchSecurity); /* from libvirt.h */ VIR_ENUM_DECL(virDomainState); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 73b72c9e10..3f28943575 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -597,6 +597,8 @@ virDomainShmemDefInsert; virDomainShmemDefRemove; virDomainShmemModelTypeFromString; virDomainShmemModelTypeToString; +virDomainShmemRoleTypeFromString; +virDomainShmemRoleTypeToString; virDomainShutdownReasonTypeFromString; virDomainShutdownReasonTypeToString; virDomainShutoffReasonTypeFromString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 839c93bfb4..0655d8359d 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -8539,11 +8539,24 @@ qemuBuildShmemDevStr(virDomainDefPtr def, virBufferAdd(&buf, virDomainShmemModelTypeToString(shmem->model), -1); virBufferAsprintf(&buf, ",id=%s", shmem->info.alias);
- if (shmem->server.enabled) + if (shmem->server.enabled) { virBufferAsprintf(&buf, ",chardev=char%s", shmem->info.alias); - else + } else { virBufferAsprintf(&buf, ",memdev=shmmem-%s", shmem->info.alias);
+ switch ((virDomainShmemRole) shmem->role) { + case VIR_DOMAIN_SHMEM_ROLE_MASTER: + virBufferAddLit(&buf, ",master=on"); + break; + case VIR_DOMAIN_SHMEM_ROLE_PEER: + virBufferAddLit(&buf, ",master=off"); + break; + case VIR_DOMAIN_SHMEM_ROLE_DEFAULT: + case VIR_DOMAIN_SHMEM_ROLE_LAST: + break; + } + } + if (shmem->msi.vectors) virBufferAsprintf(&buf, ",vectors=%u", shmem->msi.vectors); if (shmem->msi.ioeventfd) { diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 78e64344f6..5ae85c7ae7 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1261,10 +1261,22 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, } }
- if (vm->def->nshmems) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("migration with shmem device is not supported")); - return false; + for (i = 0; i < vm->def->nshmems; i++) { + virDomainShmemDefPtr shmem = vm->def->shmems[i]; + + if (shmem->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("migration with legacy shmem device is not supported")); + return false; + } + if (shmem->role == VIR_DOMAIN_SHMEM_ROLE_PEER) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("domain's shmem device '%s' has role='%s', " + "try unplugging it first"),
I, for one, like similar useful suggestions here, but in this case it is true that it is nothing special compared to other devices that cannot be migrated. So this one probably should not be suggesting it, having it in the documentation is enough. And it's still missing the reason for failure, the fact that the migration is not supported with such device/role combination.
I will make the error msg more clear.
The rest looks fine to me, so you can keep the S-o-b with the appropriate changes.
Thanks.
participants (3)
-
Martin Kletzander
-
Wang Xin
-
Wangxin (Alexander)