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=HEAD

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>
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
Wang Xin 2020-07-24 11:34:11 +08:00 committed by Martin Kletzander
parent 77296c807b
commit 493d2769f2
15 changed files with 114 additions and 19 deletions

View File

@ -9142,7 +9142,7 @@ qemu-kvm -net nic,model=? /dev/null
<pre>
...
&lt;devices&gt;
&lt;shmem name='my_shmem0'&gt;
&lt;shmem name='my_shmem0' role='peer'&gt;
&lt;model type='ivshmem-plain'/&gt;
&lt;size unit='M'&gt;4&lt;/size&gt;
&lt;/shmem&gt;
@ -9163,6 +9163,17 @@ qemu-kvm -net nic,model=? /dev/null
<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>.
The optional <code>role</code> (<span class="since">since 6.6.0</span>)
attribute specifies the shared memory is migratable or not. The value can
be either "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. If the role not specified, the hypervisor
default is used. This attribute is currently available only for
<code>model</code> type <code>ivshmem-plain</code> and
<code>ivshmem-doorbell</code>.
</dd>
<dt><code>model</code></dt>
<dd>

View File

@ -4422,6 +4422,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">

View File

@ -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,
"",
@ -15366,6 +15373,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;
@ -18585,6 +18605,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;
@ -23771,6 +23794,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 "
@ -27423,8 +27455,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",

View File

@ -1772,10 +1772,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;
@ -3625,6 +3634,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);

View File

@ -597,6 +597,8 @@ virDomainShmemDefInsert;
virDomainShmemDefRemove;
virDomainShmemModelTypeFromString;
virDomainShmemModelTypeToString;
virDomainShmemRoleTypeFromString;
virDomainShmemRoleTypeToString;
virDomainShutdownReasonTypeFromString;
virDomainShutdownReasonTypeToString;
virDomainShutoffReasonTypeFromString;

View File

@ -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) {

View File

@ -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_MASTER) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("shmem device '%s' cannot be migrated, "
"only shmem with role='%s' can be migrated"),
shmem->name,
virDomainShmemRoleTypeToString(VIR_DOMAIN_SHMEM_ROLE_MASTER));
return false;
}
}
for (i = 0; i < vm->def->nnets; i++) {

View File

@ -1,4 +1,4 @@
<shmem name='shmem0'>
<shmem name='shmem0' role='peer'>
<model type='ivshmem-plain'/>
<size unit='M'>4</size>
<alias name='shmem0'/>

View File

@ -1,3 +1,3 @@
<shmem name='shmem0'>
<shmem name='shmem0' role='peer'>
<model type='ivshmem-plain'/>
</shmem>

View File

@ -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'/>

View File

@ -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'/>

View File

@ -30,10 +30,12 @@ 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 \

View File

@ -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'/>

View File

@ -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'/>

View File

@ -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);