diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index e7b585cfed..8e50b8caac 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -5582,6 +5582,58 @@ qemu-kvm -net nic,model=? /dev/null </dd> </dl> + <h4><a name="elementsShmem">Shared memory device</a></h4> + + <p> + A shared memory device allows to share a memory region between + different virtual machines and the host. + <span class="since">Since 1.2.9, QEMU and KVM only</span> + </p> + +<pre> + ... + <devices> + <shmem name='my_shmem0'> + <size unit='M'>4</size> + </shmem> + <shmem name='shmem_server'> + <size unit='M'>2</size> + <server path='/tmp/socket-shmem'/> + <msi vectors='32' ioeventfd='on'/> + </shmem> + </devices> + ... +</pre> + + <dl> + <dt><code>shmem</code></dt> + <dd> + The <code>shmem</code> element has one mandatory attribute, + <code>name</code> to identify the shared memory. + </dd> + <dt><code>size</code></dt> + <dd> + The optional <code>size</code> element specifies the size of the shared + memory. This must be power of 2 and greater than or equal to 1 MiB. + </dd> + <dt><code>server</code></dt> + <dd> + The optional <code>server</code> element can be used to configure a server + socket the device is supposed to connect to. The optional + <code>path</code> attribute specifies the path to the unix socket and + defaults to <code>/var/lib/libvirt/shmem/$shmem-$name-sock</code>. + </dd> + <dt><code>msi</code></dt> + <dd> + The optional <code>msi</code> element enables/disables (values "on"/"off", + respectively) MSI interrupts. This option can currently be used only + together with the <code>server</code> element. The <code>vectors</code> + attribute can be used to specify the number of interrupt + vectors. The <code>ioeventd</code> attribute enables/disables (values + "on"/"off", respectively) ioeventfd. + </dd> + </dl> + <h3><a name="seclabel">Security label</a></h3> <p> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 1a266e571c..6b69fd1bac 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3278,6 +3278,44 @@ </optional> </element> </define> + + <define name="shmem"> + <element name="shmem"> + <attribute name="name"/> + <interleave> + <optional> + <element name="size"> + <ref name="scaledInteger"/> + </element> + </optional> + <optional> + <element name="server"> + <optional> + <attribute name="path"> + <ref name="absFilePath"/> + </attribute> + </optional> + </element> + </optional> + <optional> + <element name="msi"> + <optional> + <ref name="ioeventfd"/> + </optional> + <optional> + <attribute name="vectors"> + <ref name="unsignedInt"/> + </attribute> + </optional> + </element> + </optional> + <optional> + <ref name="address"/> + </optional> + </interleave> + </element> + </define> + <define name="memballoon"> <element name="memballoon"> <attribute name="model"> @@ -3864,6 +3902,7 @@ <ref name="redirfilter"/> <ref name="rng"/> <ref name="tpm"/> + <ref name="shmem"/> </choice> </zeroOrMore> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6ea25df401..b74782f42f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -255,7 +255,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST, "chr", "memballoon", "nvram", - "rng") + "rng", + "shmem") VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "none", @@ -1720,6 +1721,17 @@ void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def) VIR_FREE(def); } +void virDomainShmemDefFree(virDomainShmemDefPtr def) +{ + if (!def) + return; + + virDomainDeviceInfoClear(&def->info); + VIR_FREE(def->server.path); + VIR_FREE(def->name); + VIR_FREE(def); +} + void virDomainVideoDefFree(virDomainVideoDefPtr def) { if (!def) @@ -1921,6 +1933,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_NVRAM: virDomainNVRAMDefFree(def->data.nvram); break; + case VIR_DOMAIN_DEVICE_SHMEM: + virDomainShmemDefFree(def->data.shmem); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -2181,6 +2196,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainRedirFilterDefFree(def->redirfilter); + for (i = 0; i < def->nshmems; i++) + virDomainShmemDefFree(def->shmems[i]); + VIR_FREE(def->shmems); + if (def->namespaceData && def->ns.free) (def->ns.free)(def->namespaceData); @@ -2613,6 +2632,8 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device) return &device->data.memballoon->info; case VIR_DOMAIN_DEVICE_NVRAM: return &device->data.nvram->info; + case VIR_DOMAIN_DEVICE_SHMEM: + return &device->data.shmem->info; case VIR_DOMAIN_DEVICE_RNG: return &device->data.rng->info; @@ -2828,6 +2849,12 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, if (cb(def, &device, &def->hubs[i]->info, opaque) < 0) return -1; } + device.type = VIR_DOMAIN_DEVICE_SHMEM; + for (i = 0; i < def->nshmems; i++) { + device.data.shmem = def->shmems[i]; + if (cb(def, &device, &def->shmems[i]->info, opaque) < 0) + return -1; + } /* Coverity is not very happy with this - all dead_error_condition */ #if !STATIC_ANALYSIS @@ -2856,6 +2883,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, case VIR_DOMAIN_DEVICE_CHR: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_RNG: break; @@ -9702,6 +9730,84 @@ virDomainNVRAMDefParseXML(xmlNodePtr node, return NULL; } +static virDomainShmemDefPtr +virDomainShmemDefParseXML(xmlNodePtr node, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + char *tmp = NULL; + virDomainShmemDefPtr def = NULL; + virDomainShmemDefPtr ret = NULL; + xmlNodePtr msi = NULL; + xmlNodePtr save = ctxt->node; + xmlNodePtr server = NULL; + + + if (VIR_ALLOC(def) < 0) + return NULL; + + ctxt->node = node; + + if (!(def->name = virXMLPropString(node, "name"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("shmem element must contain 'name' attribute")); + goto cleanup; + } + + if (virDomainParseScaledValue("./size[1]", ctxt, &def->size, + 1, ULLONG_MAX, false) < 0) + goto cleanup; + + if ((server = virXPathNode("./server", ctxt))) { + def->server.enabled = true; + + if ((tmp = virXMLPropString(server, "path"))) + def->server.path = virFileSanitizePath(tmp); + VIR_FREE(tmp); + } + + if ((msi = virXPathNode("./msi", ctxt))) { + def->msi.enabled = true; + + if ((tmp = virXMLPropString(msi, "vectors")) && + virStrToLong_uip(tmp, NULL, 0, &def->msi.vectors) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid number of vectors for shmem: '%s'"), + tmp); + goto cleanup; + } + VIR_FREE(tmp); + + if ((tmp = virXMLPropString(msi, "ioeventfd")) && + (def->msi.ioeventfd = virTristateSwitchTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid msi ioeventfd setting for shmem: '%s'"), + tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + + /* msi option is only relevant with a server */ + if (def->msi.enabled && !def->server.enabled) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("msi option is only supported with a server")); + goto cleanup; + } + + if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) + goto cleanup; + + + ret = def; + def = NULL; + cleanup: + ctxt->node = save; + VIR_FREE(tmp); + virDomainShmemDefFree(def); + return ret; +} + static virSysinfoDefPtr virSysinfoParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, @@ -10558,6 +10664,10 @@ virDomainDeviceDefParse(const char *xmlStr, if (!(dev->data.nvram = virDomainNVRAMDefParseXML(node, flags))) goto error; break; + case VIR_DOMAIN_DEVICE_SHMEM: + if (!(dev->data.shmem = virDomainShmemDefParseXML(node, ctxt, flags))) + goto error; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -13686,6 +13796,25 @@ virDomainDefParseXML(xmlDocPtr xml, VIR_FREE(nodes); } + /* analysis of the shmem devices */ + if ((n = virXPathNodeSet("./devices/shmem", ctxt, &nodes)) < 0) { + goto error; + } + if (n && VIR_ALLOC_N(def->shmems, n) < 0) + goto error; + + node = ctxt->node; + for (i = 0; i < n; i++) { + virDomainShmemDefPtr shmem; + ctxt->node = nodes[i]; + shmem = virDomainShmemDefParseXML(nodes[i], ctxt, flags); + if (!shmem) + goto error; + + def->shmems[def->nshmems++] = shmem; + } + ctxt->node = node; + VIR_FREE(nodes); /* analysis of the user namespace mapping */ if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0) @@ -17469,6 +17598,54 @@ static int virDomainPanicDefFormat(virBufferPtr buf, return 0; } +static int +virDomainShmemDefFormat(virBufferPtr buf, + virDomainShmemDefPtr def, + unsigned int flags) +{ + virBufferAsprintf(buf, "<shmem name='%s'", def->name); + + if (!def->size && + !def->server.enabled && + !def->msi.enabled && + !virDomainDeviceInfoIsSet(&def->info, flags)) { + virBufferAddLit(buf, "/>\n"); + return 0; + } else { + virBufferAddLit(buf, ">\n"); + } + + virBufferAdjustIndent(buf, 2); + + if (def->size) + virBufferAsprintf(buf, "<size unit='M'>%llu</size>\n", + VIR_DIV_UP(def->size, 1024 * 1024)); + + if (def->server.enabled) { + virBufferAddLit(buf, "<server"); + virBufferEscapeString(buf, " path='%s'", def->server.path); + virBufferAddLit(buf, "/>\n"); + } + + if (def->msi.enabled) { + virBufferAddLit(buf, "<msi"); + if (def->msi.vectors) + virBufferAsprintf(buf, " vectors='%u'", def->msi.vectors); + if (def->msi.ioeventfd) + virBufferAsprintf(buf, " ioeventfd='%s'", + virTristateSwitchTypeToString(def->msi.ioeventfd)); + virBufferAddLit(buf, "/>\n"); + } + + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</shmem>\n"); + + return 0; +} + static int virDomainRNGDefFormat(virBufferPtr buf, virDomainRNGDefPtr def, @@ -19089,6 +19266,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, virDomainPanicDefFormat(buf, def->panic) < 0) goto error; + for (n = 0; n < def->nshmems; n++) + if (virDomainShmemDefFormat(buf, def->shmems[n], flags) < 0) + goto error; + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</devices>\n"); @@ -20454,6 +20635,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("Copying definition of '%d' type " diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index ea201b3a33..d73d654282 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -136,6 +136,9 @@ typedef virDomainPanicDef *virDomainPanicDefPtr; typedef struct _virDomainChrSourceDef virDomainChrSourceDef; typedef virDomainChrSourceDef *virDomainChrSourceDefPtr; +typedef struct _virDomainShmemDef virDomainShmemDef; +typedef virDomainShmemDef *virDomainShmemDefPtr; + /* Flags for the 'type' field in virDomainDeviceDef */ typedef enum { VIR_DOMAIN_DEVICE_NONE = 0, @@ -157,6 +160,7 @@ typedef enum { VIR_DOMAIN_DEVICE_MEMBALLOON, VIR_DOMAIN_DEVICE_NVRAM, VIR_DOMAIN_DEVICE_RNG, + VIR_DOMAIN_DEVICE_SHMEM, VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -184,6 +188,7 @@ struct _virDomainDeviceDef { virDomainMemballoonDefPtr memballoon; virDomainNVRAMDefPtr nvram; virDomainRNGDefPtr rng; + virDomainShmemDefPtr shmem; } data; }; @@ -1506,6 +1511,21 @@ struct _virDomainNVRAMDef { virDomainDeviceInfo info; }; +struct _virDomainShmemDef { + char *name; + unsigned long long size; + struct { + bool enabled; + char *path; + } server; + struct { + bool enabled; + unsigned vectors; + virTristateSwitch ioeventfd; + } msi; + virDomainDeviceInfo info; +}; + typedef enum { VIR_DOMAIN_SMBIOS_NONE = 0, VIR_DOMAIN_SMBIOS_EMULATE, @@ -2082,6 +2102,9 @@ struct _virDomainDef { size_t nrngs; virDomainRNGDefPtr *rngs; + size_t nshmems; + virDomainShmemDefPtr *shmems; + /* Only 1 */ virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; @@ -2279,6 +2302,7 @@ void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainHubDefFree(virDomainHubDefPtr def); void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def); void virDomainRedirFilterDefFree(virDomainRedirFilterDefPtr def); +void virDomainShmemDefFree(virDomainShmemDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); virDomainDeviceDefPtr virDomainDeviceDefCopy(virDomainDeviceDefPtr src, const virDomainDef *def, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index d63188749c..3a654e436f 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2859,6 +2859,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_RNG: + case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("don't know how to remove a %s device"), diff --git a/tests/qemuxml2argvdata/qemuxml2argv-shmem-msi-only.xml b/tests/qemuxml2argvdata/qemuxml2argv-shmem-msi-only.xml new file mode 100644 index 0000000000..d70279c21f --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-shmem-msi-only.xml @@ -0,0 +1,24 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='none'/> + <shmem name='shmem0'> + <msi/> + </shmem> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-shmem.xml b/tests/qemuxml2argvdata/qemuxml2argv-shmem.xml new file mode 100644 index 0000000000..fd79c89c1a --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-shmem.xml @@ -0,0 +1,51 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <controller type='pci' index='0' model='pci-root'/> + <memballoon model='none'/> + <shmem name='shmem0'/> + <shmem name='shmem1'> + <size unit='M'>128</size> + </shmem> + <shmem name='shmem2'> + <size unit='M'>256</size> + </shmem> + <shmem name='shmem3'> + <size unit='M'>512</size> + <server/> + </shmem> + <shmem name='shmem4'> + <size unit='M'>1024</size> + <server path='/tmp/shmem4-sock'/> + </shmem> + <shmem name='shmem5'> + <size unit='M'>2048</size> + <server path='/tmp/shmem5-sock'/> + <msi ioeventfd='off'/> + </shmem> + <shmem name='shmem6'> + <size unit='M'>4096</size> + <server path='/tmp/shmem6-sock'/> + <msi vectors='16'/> + </shmem> + <shmem name='shmem7'> + <size unit='M'>8192</size> + <server path='/tmp/shmem7-sock'/> + <msi vectors='32' ioeventfd='on'/> + </shmem> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 483ca90324..58153db0df 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1466,6 +1466,8 @@ mymain(void) DO_TEST("fips-enabled", QEMU_CAPS_ENABLE_FIPS); + DO_TEST_PARSE_ERROR("shmem-msi-only", NONE); + virObjectUnref(driver.config); virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 9a568806d7..978e5f1c6a 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -404,6 +404,7 @@ mymain(void) DO_TEST("bios-nvram"); DO_TEST("tap-vhost"); + DO_TEST("shmem"); virObjectUnref(driver.caps); virObjectUnref(driver.xmlopt);