Individual vCPU hotplug requires us to track the state of any vCPU. To allow this add the following XML: <domain> ... <vcpu current='1'>2</vcpu> <vcpus> <vcpu id='0' enabled='no' hotpluggable='yes'/> <vcpu id='1' enabled='yes' hotpluggable='no' order='1'/> </vcpus> ... The 'enabled' attribute allows to control the state of the vcpu. 'hotpluggable' controls whether given vcpu can be hotplugged and 'order' allows to specify the order to add the vcpus. --- docs/formatdomain.html.in | 18 +++ docs/schemas/domaincommon.rng | 25 ++++ src/conf/domain_conf.c | 151 ++++++++++++++++++++- src/conf/domain_conf.h | 6 + .../generic-vcpus-individual.xml | 23 ++++ tests/genericxml2xmltest.c | 2 + tests/testutils.c | 4 +- 7 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 tests/genericxml2xmlindata/generic-vcpus-individual.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 5acb3b9..2d70f56 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -489,6 +489,10 @@ <domain> ... <vcpu placement='static' cpuset="1-4,^3,6" current="1">2</vcpu> + <vcpus> + <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='1' enabled='no' hotpluggable='yes'/> + </vcpus> ... </domain> </pre> @@ -542,6 +546,20 @@ </dd> </dl> </dd> + <dt><code>vcpus</code></dt> + <dd> + The vcpus element allows to specify state of individual vcpu. + + Note that providing state for individual cpus may be necessary to enable + support of addressable vCPU hotplug and this feature may not be + supported by all hypervisors. + <span class="since">Since 2.2.0</span> + + The <code>enabled</code> attribute allows to control the state of the + vcpu. <code>hotpluggable</code> controls whether given vcpu can be + hotplugged and hotunplugged in cases when the cpu is enabled at boot. + <code>order</code> allows to specify the order to add the vcpus. + </dd> </dl> <h3><a name="elementsIOThreadsAllocation">IOThreads Allocation</a></h3> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 052f28c..5b3d652 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -583,6 +583,31 @@ </optional> <optional> + <element name="vcpus"> + <zeroOrMore> + <element name="vcpu"> + <attribute name="id"> + <ref name="unsignedInt"/> + </attribute> + <attribute name="enabled"> + <ref name="virYesNo"/> + </attribute> + <optional> + <attribute name="hotpluggable"> + <ref name="virYesNo"/> + </attribute> + </optional> + <optional> + <attribute name="order"> + <ref name="unsignedInt"/> + </attribute> + </optional> + </element> + </zeroOrMore> + </element> + </optional> + + <optional> <element name="iothreads"> <ref name="unsignedInt"/> </element> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2500058..51b8b78 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4328,6 +4328,13 @@ virDomainDefPostParseCheckFeatures(virDomainDefPtr def, } } + if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS) && + def->individualvcpus) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("individual CPU state configuration is not supported")); + return -1; + } + return 0; } @@ -4402,6 +4409,43 @@ virDomainDefPostParseDeviceIterator(virDomainDefPtr def, static int +virDomainVcpuDefPostParse(virDomainDefPtr def) +{ + virDomainVcpuDefPtr vcpu; + size_t maxvcpus = virDomainDefGetVcpusMax(def); + size_t i; + + for (i = 0; i < maxvcpus; i++) { + vcpu = virDomainDefGetVcpu(def, i); + + switch (vcpu->hotpluggable) { + case VIR_TRISTATE_BOOL_ABSENT: + if (vcpu->online) + vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO; + else + vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; + break; + + case VIR_TRISTATE_BOOL_NO: + if (!vcpu->online) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("vcpu '%zu' is both offline and not " + "hotpluggable"), i); + return -1; + } + break; + + case VIR_TRISTATE_BOOL_YES: + case VIR_TRISTATE_BOOL_LAST: + break; + } + } + + return 0; +} + + +static int virDomainDefPostParseInternal(virDomainDefPtr def, struct virDomainDefPostParseDeviceIteratorData *data) { @@ -4412,6 +4456,9 @@ virDomainDefPostParseInternal(virDomainDefPtr def, return -1; } + if (virDomainVcpuDefPostParse(def) < 0) + return -1; + if (virDomainDefPostParseMemory(def, data->parseFlags) < 0) return -1; @@ -15513,6 +15560,8 @@ virDomainVcpuParse(virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { int n; + xmlNodePtr *nodes = NULL; + size_t i; char *tmp = NULL; unsigned int maxvcpus; unsigned int vcpus; @@ -15541,8 +15590,6 @@ virDomainVcpuParse(virDomainDefPtr def, vcpus = maxvcpus; } - if (virDomainDefSetVcpus(def, vcpus) < 0) - goto cleanup; tmp = virXPathString("string(./vcpu[1]/@placement)", ctxt); if (tmp) { @@ -15574,9 +15621,79 @@ virDomainVcpuParse(virDomainDefPtr def, } } + if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0) + goto cleanup; + + if (n) { + /* if individual vcpu states are provided take them as master */ + def->individualvcpus = true; + + for (i = 0; i < n; i++) { + virDomainVcpuDefPtr vcpu; + int state; + unsigned int id; + unsigned int order; + + if (!(tmp = virXMLPropString(nodes[i], "id")) || + virStrToLong_uip(tmp, NULL, 10, &id) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing or invalid vcpu id")); + goto cleanup; + } + + VIR_FREE(tmp); + + if (id >= def->maxvcpus) { + virReportError(VIR_ERR_XML_ERROR, + _("vcpu id '%u' is out of range of maximum " + "vcpu count"), id); + goto cleanup; + } + + vcpu = virDomainDefGetVcpu(def, id); + + if (!(tmp = virXMLPropString(nodes[i], "enabled"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing vcpu enabled state")); + goto cleanup; + } + + if ((state = virTristateBoolTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid vcpu 'enabled' value '%s'"), tmp); + goto cleanup; + } + VIR_FREE(tmp); + + vcpu->online = state == VIR_TRISTATE_BOOL_YES; + + if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) { + if ((vcpu->hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid vcpu 'hotpluggable' value '%s'"), tmp); + goto cleanup; + } + VIR_FREE(tmp); + } + + if ((tmp = virXMLPropString(nodes[i], "order"))) { + if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid vcpu order")); + goto cleanup; + } + vcpu->order = order; + } + } + } else { + if (virDomainDefSetVcpus(def, vcpus) < 0) + goto cleanup; + } + ret = 0; cleanup: + VIR_FREE(nodes); VIR_FREE(tmp); return ret; @@ -18634,6 +18751,13 @@ virDomainDefVcpuCheckAbiStability(virDomainDefPtr src, "destination definitions"), i); return false; } + + if (svcpu->order != dvcpu->order) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("vcpu enable order of vCPU '%zu' differs between " + "source and destination definitions"), i); + return false; + } } return true; @@ -22938,6 +23062,8 @@ static int virDomainCpuDefFormat(virBufferPtr buf, const virDomainDef *def) { + virDomainVcpuDefPtr vcpu; + size_t i; char *cpumask = NULL; int ret = -1; @@ -22954,6 +23080,27 @@ virDomainCpuDefFormat(virBufferPtr buf, virBufferAsprintf(buf, " current='%u'", virDomainDefGetVcpus(def)); virBufferAsprintf(buf, ">%u</vcpu>\n", virDomainDefGetVcpusMax(def)); + if (def->individualvcpus) { + virBufferAddLit(buf, "<vcpus>\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < def->maxvcpus; i++) { + vcpu = def->vcpus[i]; + + virBufferAsprintf(buf, "<vcpu id='%zu' enabled='%s'", + i, vcpu->online ? "yes" : "no"); + if (vcpu->hotpluggable) + virBufferAsprintf(buf, " hotpluggable='%s'", + virTristateBoolTypeToString(vcpu->hotpluggable)); + + if (vcpu->order != 0) + virBufferAsprintf(buf, " order='%d'", vcpu->order); + + virBufferAddLit(buf, "/>\n"); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</vcpus>\n"); + } + ret = 0; cleanup: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8b26724..b889125 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2046,6 +2046,9 @@ typedef virDomainVcpuDef *virDomainVcpuDefPtr; struct _virDomainVcpuDef { bool online; + virTristateBool hotpluggable; + unsigned int order; + virBitmapPtr cpumask; virDomainThreadSchedParam sched; @@ -2142,6 +2145,8 @@ struct _virDomainDef { virDomainVcpuDefPtr *vcpus; size_t maxvcpus; + /* set if the vcpu definition was specified individually */ + bool individualvcpus; int placement_mode; virBitmapPtr cpumask; @@ -2344,6 +2349,7 @@ typedef enum { VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG = (1 << 1), VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN = (1 << 2), VIR_DOMAIN_DEF_FEATURE_NAME_SLASH = (1 << 3), + VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS = (1 << 4), } virDomainDefFeatures; diff --git a/tests/genericxml2xmlindata/generic-vcpus-individual.xml b/tests/genericxml2xmlindata/generic-vcpus-individual.xml new file mode 100644 index 0000000..cbcf8fd --- /dev/null +++ b/tests/genericxml2xmlindata/generic-vcpus-individual.xml @@ -0,0 +1,23 @@ +<domain type='qemu'> + <name>foobar</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static' current='2'>4</vcpu> + <vcpus> + <vcpu id='0' enabled='no' hotpluggable='yes' order='1'/> + <vcpu id='1' enabled='yes' hotpluggable='no'/> + <vcpu id='2' enabled='no' hotpluggable='yes' order='2'/> + <vcpu id='3' enabled='yes' hotpluggable='no'/> + </vcpus> + <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> + </devices> +</domain> diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index a487727..2ea2396 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -97,6 +97,8 @@ mymain(void) DO_TEST("perf"); + DO_TEST("vcpus-individual"); + virObjectUnref(caps); virObjectUnref(xmlopt); diff --git a/tests/testutils.c b/tests/testutils.c index 8af8707..8ea6ab8 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -1089,7 +1089,9 @@ virCapsPtr virTestGenericCapsInit(void) return NULL; } -static virDomainDefParserConfig virTestGenericDomainDefParserConfig; +static virDomainDefParserConfig virTestGenericDomainDefParserConfig = { + .features = VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS, +}; static virDomainXMLPrivateDataCallbacks virTestGenericPrivateDataCallbacks; virDomainXMLOptionPtr virTestGenericDomainXMLConfInit(void) -- 2.9.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list