From c740ae69c382be18070282085d90b74ff0ef5fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Tomko?= Date: Tue, 21 Apr 2020 18:35:59 +0200 Subject: [PATCH] conf: split out virDomainFeaturesDefParse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virDomainDefParseXML function has grown so large it broke the build: ../../src/conf/domain_conf.c:20362:1: error: stack frame size of 4168 bytes in function 'virDomainDefParseXML' [-Werror,-Wframe-larger-than=] Signed-off-by: Ján Tomko Reviewed-by: Peter Krempa --- src/conf/domain_conf.c | 3373 ++++++++++++++++++++-------------------- 1 file changed, 1697 insertions(+), 1676 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9f3362c934..d9454575f2 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -19181,400 +19181,577 @@ virDomainResourceDefParse(xmlNodePtr node, return NULL; } + static int -virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def) +virDomainFeaturesDefParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt) { - /* Look for any hostdev scsi dev */ + g_autofree xmlNodePtr *nodes = NULL; + g_autofree char *tmp = NULL; + xmlNodePtr node = NULL; + int gic_version; size_t i; - int maxController = -1; - virDomainHostdevDefPtr hostdev; - int newModel = -1; + int n; - for (i = 0; i < def->nhostdevs; i++) { - hostdev = def->hostdevs[i]; - if (virHostdevIsSCSIDevice(hostdev) && - (int)hostdev->info->addr.drive.controller > maxController) { - virDomainControllerDefPtr cont; + if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) < 0) + goto error; - maxController = hostdev->info->addr.drive.controller; - /* We may be creating a new controller because this one is full. - * So let's grab the model from it and update the model we're - * going to add as long as this one isn't undefined. The premise - * being keeping the same controller model for all SCSI hostdevs. */ - cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive); - if (cont && cont->model != -1) - newModel = cont->model; + for (i = 0; i < n; i++) { + int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name); + if (val < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unexpected feature '%s'"), nodes[i]->name); + goto error; } - } - if (maxController == -1) - return 0; + switch ((virDomainFeature) val) { + case VIR_DOMAIN_FEATURE_APIC: + if ((tmp = virXPathString("string(./features/apic/@eoi)", ctxt))) { + int eoi; + if ((eoi = virTristateSwitchTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown value for attribute eoi: '%s'"), + tmp); + goto error; + } + def->apic_eoi = eoi; + VIR_FREE(tmp); + } + G_GNUC_FALLTHROUGH; + case VIR_DOMAIN_FEATURE_ACPI: + case VIR_DOMAIN_FEATURE_PAE: + case VIR_DOMAIN_FEATURE_VIRIDIAN: + case VIR_DOMAIN_FEATURE_PRIVNET: + case VIR_DOMAIN_FEATURE_HYPERV: + case VIR_DOMAIN_FEATURE_KVM: + case VIR_DOMAIN_FEATURE_MSRS: + case VIR_DOMAIN_FEATURE_XEN: + def->features[val] = VIR_TRISTATE_SWITCH_ON; + break; - for (i = 0; i <= maxController; i++) { - if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, - i, newModel) < 0) - return -1; - } + case VIR_DOMAIN_FEATURE_CAPABILITIES: + if ((tmp = virXMLPropString(nodes[i], "policy"))) { + if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown policy attribute '%s' of feature '%s'"), + tmp, virDomainFeatureTypeToString(val)); + goto error; + } + VIR_FREE(tmp); + } else { + def->features[val] = VIR_TRISTATE_SWITCH_ABSENT; + } + break; - return 0; -} + case VIR_DOMAIN_FEATURE_VMCOREINFO: + case VIR_DOMAIN_FEATURE_HAP: + case VIR_DOMAIN_FEATURE_PMU: + case VIR_DOMAIN_FEATURE_PVSPINLOCK: + case VIR_DOMAIN_FEATURE_VMPORT: + case VIR_DOMAIN_FEATURE_SMM: + if ((tmp = virXMLPropString(nodes[i], "state"))) { + if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown state attribute '%s' of feature '%s'"), + tmp, virDomainFeatureTypeToString(val)); + goto error; + } + VIR_FREE(tmp); + } else { + def->features[val] = VIR_TRISTATE_SWITCH_ON; + } + break; -static int -virDomainLoaderDefParseXML(xmlNodePtr node, - virDomainLoaderDefPtr loader, - bool fwAutoSelect) -{ - g_autofree char *readonly_str = NULL; - g_autofree char *secure_str = NULL; - g_autofree char *type_str = NULL; + case VIR_DOMAIN_FEATURE_GIC: + if ((tmp = virXMLPropString(nodes[i], "version"))) { + gic_version = virGICVersionTypeFromString(tmp); + if (gic_version < 0 || gic_version == VIR_GIC_VERSION_NONE) { + virReportError(VIR_ERR_XML_ERROR, + _("malformed gic version: %s"), tmp); + goto error; + } + def->gic_version = gic_version; + VIR_FREE(tmp); + } + def->features[val] = VIR_TRISTATE_SWITCH_ON; + break; - secure_str = virXMLPropString(node, "secure"); + case VIR_DOMAIN_FEATURE_IOAPIC: + tmp = virXMLPropString(nodes[i], "driver"); + if (tmp) { + int value = virDomainIOAPICTypeFromString(tmp); + if (value < 0 || value == VIR_DOMAIN_IOAPIC_NONE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unknown driver mode: %s"), + tmp); + goto error; + } + def->features[val] = value; + VIR_FREE(tmp); + } + break; - if (!fwAutoSelect) { - readonly_str = virXMLPropString(node, "readonly"); - type_str = virXMLPropString(node, "type"); - loader->path = (char *) xmlNodeGetContent(node); - if (STREQ_NULLABLE(loader->path, "")) - VIR_FREE(loader->path); - } + case VIR_DOMAIN_FEATURE_HPT: + tmp = virXMLPropString(nodes[i], "resizing"); + if (tmp) { + int value = virDomainHPTResizingTypeFromString(tmp); + if (value < 0 || value == VIR_DOMAIN_HPT_RESIZING_NONE) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unknown HPT resizing setting: %s"), + tmp); + goto error; + } + def->hpt_resizing = (virDomainHPTResizing) value; + VIR_FREE(tmp); + } - if (readonly_str && - (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("unknown readonly value: %s"), readonly_str); - return -1; - } + if (virDomainParseScaledValue("./features/hpt/maxpagesize", + NULL, + ctxt, + &def->hpt_maxpagesize, + 1024, + ULLONG_MAX, + false) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", + _("Unable to parse HPT maxpagesize setting")); + goto error; + } + def->hpt_maxpagesize = VIR_DIV_UP(def->hpt_maxpagesize, 1024); - if (secure_str && - (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("unknown secure value: %s"), secure_str); - return -1; + if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE || + def->hpt_maxpagesize > 0) { + def->features[val] = VIR_TRISTATE_SWITCH_ON; + } + break; + + case VIR_DOMAIN_FEATURE_HTM: + case VIR_DOMAIN_FEATURE_NESTED_HV: + case VIR_DOMAIN_FEATURE_CCF_ASSIST: + if (!(tmp = virXMLPropString(nodes[i], "state"))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing state attribute '%s' of feature '%s'"), + tmp, virDomainFeatureTypeToString(val)); + goto error; + } + if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown state attribute '%s' of feature '%s'"), + tmp, virDomainFeatureTypeToString(val)); + goto error; + } + VIR_FREE(tmp); + break; + + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_FEATURE_LAST: + break; + } } + VIR_FREE(nodes); + + if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { + int feature; + int value; + node = ctxt->node; + if ((n = virXPathNodeSet("./features/hyperv/*", ctxt, &nodes)) < 0) + goto error; + + for (i = 0; i < n; i++) { + feature = virDomainHypervTypeFromString((const char *)nodes[i]->name); + if (feature < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported HyperV Enlightenment feature: %s"), + nodes[i]->name); + goto error; + } + + ctxt->node = nodes[i]; + + if (!(tmp = virXMLPropString(nodes[i], "state"))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing 'state' attribute for " + "HyperV Enlightenment feature '%s'"), + nodes[i]->name); + goto error; + } + + if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid value of state argument " + "for HyperV Enlightenment feature '%s'"), + nodes[i]->name); + goto error; + } + + VIR_FREE(tmp); + def->hyperv_features[feature] = value; + + switch ((virDomainHyperv) feature) { + case VIR_DOMAIN_HYPERV_RELAXED: + case VIR_DOMAIN_HYPERV_VAPIC: + case VIR_DOMAIN_HYPERV_VPINDEX: + case VIR_DOMAIN_HYPERV_RUNTIME: + case VIR_DOMAIN_HYPERV_SYNIC: + case VIR_DOMAIN_HYPERV_STIMER: + case VIR_DOMAIN_HYPERV_RESET: + case VIR_DOMAIN_HYPERV_FREQUENCIES: + case VIR_DOMAIN_HYPERV_REENLIGHTENMENT: + case VIR_DOMAIN_HYPERV_TLBFLUSH: + case VIR_DOMAIN_HYPERV_IPI: + case VIR_DOMAIN_HYPERV_EVMCS: + break; + + case VIR_DOMAIN_HYPERV_SPINLOCKS: + if (value != VIR_TRISTATE_SWITCH_ON) + break; + + if (virXPathUInt("string(./@retries)", ctxt, + &def->hyperv_spinlocks) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid HyperV spinlock retry count")); + goto error; + } + + if (def->hyperv_spinlocks < 0xFFF) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("HyperV spinlock retry count must be " + "at least 4095")); + goto error; + } + break; + + case VIR_DOMAIN_HYPERV_VENDOR_ID: + if (value != VIR_TRISTATE_SWITCH_ON) + break; + + if (!(def->hyperv_vendor_id = virXMLPropString(nodes[i], + "value"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing 'value' attribute for " + "HyperV feature 'vendor_id'")); + goto error; + } + + if (strlen(def->hyperv_vendor_id) > VIR_DOMAIN_HYPERV_VENDOR_ID_MAX) { + virReportError(VIR_ERR_XML_ERROR, + _("HyperV vendor_id value must not be more " + "than %d characters."), + VIR_DOMAIN_HYPERV_VENDOR_ID_MAX); + goto error; + } - if (type_str) { - int type; - if ((type = virDomainLoaderTypeFromString(type_str)) <= 0) { - virReportError(VIR_ERR_XML_DETAIL, - _("unknown type value: %s"), type_str); - return -1; + /* ensure that the string can be passed to qemu */ + if (strchr(def->hyperv_vendor_id, ',')) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("HyperV vendor_id value is invalid")); + goto error; + } + + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_HYPERV_LAST: + break; + } } - loader->type = type; + VIR_FREE(nodes); + ctxt->node = node; } - return 0; -} + if (def->features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) { + int value; + if ((n = virXPathNodeSet("./features/hyperv/stimer/*", ctxt, &nodes)) < 0) + goto error; + for (i = 0; i < n; i++) { + if (STRNEQ((const char *)nodes[i]->name, "direct")) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported Hyper-V stimer feature: %s"), + nodes[i]->name); + goto error; + } -static int -virDomainSchedulerParseCommonAttrs(xmlNodePtr node, - virProcessSchedPolicy *policy, - int *priority) -{ - int pol = 0; - g_autofree char *tmp = NULL; + if (!(tmp = virXMLPropString(nodes[i], "state"))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing 'state' attribute for " + "Hyper-V stimer '%s' feature"), "direct"); + goto error; + } - if (!(tmp = virXMLPropString(node, "scheduler"))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Missing scheduler attribute")); - return -1; - } + if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid value of state argument " + "for Hyper-V stimer '%s' feature"), "direct"); + goto error; + } - if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Invalid scheduler attribute: '%s'"), tmp); - return -1; + VIR_FREE(tmp); + def->hyperv_stimer_direct = value; + } + VIR_FREE(nodes); } - *policy = pol; - VIR_FREE(tmp); + if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { + int feature; + int value; + if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) < 0) + goto error; - if (pol == VIR_PROC_POLICY_FIFO || - pol == VIR_PROC_POLICY_RR) { - if (!(tmp = virXMLPropString(node, "priority"))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing scheduler priority")); - return -1; - } + for (i = 0; i < n; i++) { + feature = virDomainKVMTypeFromString((const char *)nodes[i]->name); + if (feature < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported KVM feature: %s"), + nodes[i]->name); + goto error; + } - if (virStrToLong_i(tmp, NULL, 10, priority) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Invalid value for element priority")); - return -1; + switch ((virDomainKVM) feature) { + case VIR_DOMAIN_KVM_HIDDEN: + case VIR_DOMAIN_KVM_DEDICATED: + if (!(tmp = virXMLPropString(nodes[i], "state"))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing 'state' attribute for " + "KVM feature '%s'"), + nodes[i]->name); + goto error; + } + + if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid value of state argument " + "for KVM feature '%s'"), + nodes[i]->name); + goto error; + } + + VIR_FREE(tmp); + def->kvm_features[feature] = value; + break; + + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_KVM_LAST: + break; + } } + VIR_FREE(nodes); } - return 0; -} - + if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) { + int feature; + int value; + g_autofree char *ptval = NULL; -static int -virDomainEmulatorSchedParse(xmlNodePtr node, - virDomainDefPtr def) -{ - g_autofree virDomainThreadSchedParamPtr sched = NULL; + if ((n = virXPathNodeSet("./features/xen/*", ctxt, &nodes)) < 0) + goto error; - if (VIR_ALLOC(sched) < 0) - return -1; + for (i = 0; i < n; i++) { + feature = virDomainXenTypeFromString((const char *)nodes[i]->name); + if (feature < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported Xen feature: %s"), + nodes[i]->name); + goto error; + } - if (virDomainSchedulerParseCommonAttrs(node, - &sched->policy, - &sched->priority) < 0) - return -1; + if (!(tmp = virXMLPropString(nodes[i], "state"))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing 'state' attribute for " + "Xen feature '%s'"), + nodes[i]->name); + goto error; + } - def->cputune.emulatorsched = g_steal_pointer(&sched); - return 0; -} + if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("invalid value of state argument " + "for Xen feature '%s'"), + nodes[i]->name); + goto error; + } + VIR_FREE(tmp); + def->xen_features[feature] = value; -static virBitmapPtr -virDomainSchedulerParse(xmlNodePtr node, - const char *name, - virProcessSchedPolicy *policy, - int *priority) -{ - virBitmapPtr ret = NULL; - g_autofree char *tmp = NULL; + switch ((virDomainXen) feature) { + case VIR_DOMAIN_XEN_E820_HOST: + break; - if (!(tmp = virXMLPropString(node, name))) { - virReportError(VIR_ERR_XML_ERROR, - _("Missing attribute '%s' in element '%sched'"), - name, name); - goto error; - } + case VIR_DOMAIN_XEN_PASSTHROUGH: + if (value != VIR_TRISTATE_SWITCH_ON) + break; - if (virBitmapParse(tmp, &ret, VIR_DOMAIN_CPUMASK_LEN) < 0) - goto error; + if ((ptval = virXMLPropString(nodes[i], "mode"))) { + int mode = virDomainXenPassthroughModeTypeFromString(ptval); - if (virBitmapIsAllClear(ret)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("'%s' scheduler bitmap '%s' is empty"), - name, tmp); - goto error; - } + if (mode < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported mode '%s' for Xen passthrough feature"), + ptval); + goto error; + } - if (virDomainSchedulerParseCommonAttrs(node, policy, priority) < 0) - goto error; + if (mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SYNC_PT && + mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SHARE_PT) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("'mode' attribute for Xen feature " + "'passthrough' must be 'sync_pt' or 'share_pt'")); + goto error; + } + def->xen_passthrough_mode = mode; + } + break; - return ret; + /* coverity[dead_error_begin] */ + case VIR_DOMAIN_XEN_LAST: + break; + } + } + VIR_FREE(nodes); + } - error: - virBitmapFree(ret); - return NULL; -} + if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) { + int rv = virDomainParseScaledValue("string(./features/smm/tseg)", + "string(./features/smm/tseg/@unit)", + ctxt, + &def->tseg_size, + 1024 * 1024, /* Defaults to mebibytes */ + ULLONG_MAX, + false); + if (rv < 0) + goto error; + def->tseg_specified = rv; + } + if (def->features[VIR_DOMAIN_FEATURE_MSRS] == VIR_TRISTATE_SWITCH_ON) { + if ((node = virXPathNode("./features/msrs", ctxt)) == NULL) + goto error; -static int -virDomainThreadSchedParseHelper(xmlNodePtr node, - const char *name, - virDomainThreadSchedParamPtr (*func)(virDomainDefPtr, unsigned int), - virDomainDefPtr def) -{ - ssize_t next = -1; - virDomainThreadSchedParamPtr sched = NULL; - virProcessSchedPolicy policy = 0; - int priority = 0; - g_autoptr(virBitmap) map = NULL; + if (!(tmp = virXMLPropString(node, "unknown"))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing 'unknown' attribute for feature '%s'"), + virDomainFeatureTypeToString(VIR_DOMAIN_FEATURE_MSRS)); + goto error; + } - if (!(map = virDomainSchedulerParse(node, name, &policy, &priority))) - return -1; + if ((def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN] = virDomainMsrsUnknownTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown 'unknown' value '%s'"), + tmp); + goto error; + } + VIR_FREE(tmp); + } - while ((next = virBitmapNextSetBit(map, next)) > -1) { - if (!(sched = func(def, next))) - return -1; + if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes)) < 0) + goto error; - if (sched->policy != VIR_PROC_POLICY_NONE) { - virReportError(VIR_ERR_XML_DETAIL, - _("%ssched attributes 'vcpus' must not overlap"), - name); - return -1; + for (i = 0; i < n; i++) { + int val = virDomainProcessCapsFeatureTypeFromString((const char *)nodes[i]->name); + if (val < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unexpected capability feature '%s'"), nodes[i]->name); + goto error; } - sched->policy = policy; - sched->priority = priority; + if ((tmp = virXMLPropString(nodes[i], "state"))) { + if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown state attribute '%s' of feature capability '%s'"), + tmp, virDomainProcessCapsFeatureTypeToString(val)); + goto error; + } + VIR_FREE(tmp); + } else { + def->caps_features[val] = VIR_TRISTATE_SWITCH_ON; + } } - + VIR_FREE(nodes); return 0; + + error: + return -1; } static int -virDomainVcpuThreadSchedParse(xmlNodePtr node, - virDomainDefPtr def) +virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def) { - return virDomainThreadSchedParseHelper(node, "vcpus", - virDomainDefGetVcpuSched, - def); -} - + /* Look for any hostdev scsi dev */ + size_t i; + int maxController = -1; + virDomainHostdevDefPtr hostdev; + int newModel = -1; -static virDomainThreadSchedParamPtr -virDomainDefGetIOThreadSched(virDomainDefPtr def, - unsigned int iothread) -{ - virDomainIOThreadIDDefPtr iothrinfo; + for (i = 0; i < def->nhostdevs; i++) { + hostdev = def->hostdevs[i]; + if (virHostdevIsSCSIDevice(hostdev) && + (int)hostdev->info->addr.drive.controller > maxController) { + virDomainControllerDefPtr cont; - if (!(iothrinfo = virDomainIOThreadIDFind(def, iothread))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Cannot find 'iothread' : %u"), - iothread); - return NULL; + maxController = hostdev->info->addr.drive.controller; + /* We may be creating a new controller because this one is full. + * So let's grab the model from it and update the model we're + * going to add as long as this one isn't undefined. The premise + * being keeping the same controller model for all SCSI hostdevs. */ + cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive); + if (cont && cont->model != -1) + newModel = cont->model; + } } - return &iothrinfo->sched; -} + if (maxController == -1) + return 0; + for (i = 0; i <= maxController; i++) { + if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, + i, newModel) < 0) + return -1; + } -static int -virDomainIOThreadSchedParse(xmlNodePtr node, - virDomainDefPtr def) -{ - return virDomainThreadSchedParseHelper(node, "iothreads", - virDomainDefGetIOThreadSched, - def); + return 0; } - static int -virDomainVcpuParse(virDomainDefPtr def, - xmlXPathContextPtr ctxt, - virDomainXMLOptionPtr xmlopt) +virDomainLoaderDefParseXML(xmlNodePtr node, + virDomainLoaderDefPtr loader, + bool fwAutoSelect) { - int n; - xmlNodePtr vcpuNode; - size_t i; - unsigned int maxvcpus; - unsigned int vcpus; - g_autofree char *tmp = NULL; - g_autofree xmlNodePtr *nodes = NULL; - - vcpus = maxvcpus = 1; - - if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) { - if ((tmp = virXMLNodeContentString(vcpuNode))) { - if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("maximum vcpus count must be an integer")); - return -1; - } - VIR_FREE(tmp); - } - - if ((tmp = virXMLPropString(vcpuNode, "current"))) { - if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("current vcpus count must be an integer")); - return -1; - } - VIR_FREE(tmp); - } else { - vcpus = maxvcpus; - } - - tmp = virXMLPropString(vcpuNode, "placement"); - if (tmp) { - if ((def->placement_mode = - virDomainCpuPlacementModeTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported CPU placement mode '%s'"), - tmp); - return -1; - } - VIR_FREE(tmp); - } else { - def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC; - } - - if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) { - tmp = virXMLPropString(vcpuNode, "cpuset"); - if (tmp) { - if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) - return -1; + g_autofree char *readonly_str = NULL; + g_autofree char *secure_str = NULL; + g_autofree char *type_str = NULL; - if (virBitmapIsAllClear(def->cpumask)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid value of 'cpuset': %s"), tmp); - return -1; - } + secure_str = virXMLPropString(node, "secure"); - VIR_FREE(tmp); - } - } + if (!fwAutoSelect) { + readonly_str = virXMLPropString(node, "readonly"); + type_str = virXMLPropString(node, "type"); + loader->path = (char *) xmlNodeGetContent(node); + if (STREQ_NULLABLE(loader->path, "")) + VIR_FREE(loader->path); } - if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0) + if (readonly_str && + (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("unknown readonly value: %s"), readonly_str); return -1; + } - if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0) + if (secure_str && + (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("unknown secure value: %s"), secure_str); return -1; + } - 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")); - return -1; - } - - VIR_FREE(tmp); - - if (id >= def->maxvcpus) { - virReportError(VIR_ERR_XML_ERROR, - _("vcpu id '%u' is out of range of maximum " - "vcpu count"), id); - return -1; - } - - vcpu = virDomainDefGetVcpu(def, id); - - if (!(tmp = virXMLPropString(nodes[i], "enabled"))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing vcpu enabled state")); - return -1; - } - - if ((state = virTristateBoolTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid vcpu 'enabled' value '%s'"), tmp); - return -1; - } - VIR_FREE(tmp); - - vcpu->online = state == VIR_TRISTATE_BOOL_YES; - - if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) { - int hotpluggable; - if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("invalid vcpu 'hotpluggable' value '%s'"), tmp); - return -1; - } - vcpu->hotpluggable = hotpluggable; - 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")); - return -1; - } - vcpu->order = order; - VIR_FREE(tmp); - } - } - } else { - if (virDomainDefSetVcpus(def, vcpus) < 0) + if (type_str) { + int type; + if ((type = virDomainLoaderTypeFromString(type_str)) <= 0) { + virReportError(VIR_ERR_XML_DETAIL, + _("unknown type value: %s"), type_str); return -1; + } + loader->type = type; } return 0; @@ -19582,155 +19759,131 @@ virDomainVcpuParse(virDomainDefPtr def, static int -virDomainDefParseBootOptions(virDomainDefPtr def, - xmlXPathContextPtr ctxt) +virDomainSchedulerParseCommonAttrs(xmlNodePtr node, + virProcessSchedPolicy *policy, + int *priority) { - char *name = NULL; - size_t i; - int n; - g_autofree xmlNodePtr *nodes = NULL; + int pol = 0; g_autofree char *tmp = NULL; - /* - * Booting options for different OS types.... - * - * - A bootloader (and optional kernel+initrd) (xen) - * - A kernel + initrd (xen) - * - A boot device (and optional kernel+initrd) (hvm) - * - An init script (exe) - */ + if (!(tmp = virXMLPropString(node, "scheduler"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing scheduler attribute")); + return -1; + } - if (def->os.type == VIR_DOMAIN_OSTYPE_EXE) { - def->os.init = virXPathString("string(./os/init[1])", ctxt); - def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); - def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt); - def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt); - def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt); + if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Invalid scheduler attribute: '%s'"), tmp); + return -1; + } + *policy = pol; - if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0) - return -1; + VIR_FREE(tmp); - if (VIR_ALLOC_N(def->os.initargv, n+1) < 0) + if (pol == VIR_PROC_POLICY_FIFO || + pol == VIR_PROC_POLICY_RR) { + if (!(tmp = virXMLPropString(node, "priority"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing scheduler priority")); return -1; - for (i = 0; i < n; i++) { - if (!nodes[i]->children || - !nodes[i]->children->content) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("No data supplied for element")); - return -1; - } - def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content); } - def->os.initargv[n] = NULL; - VIR_FREE(nodes); - if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0) + if (virStrToLong_i(tmp, NULL, 10, priority) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid value for element priority")); return -1; + } + } - if (VIR_ALLOC_N(def->os.initenv, n+1) < 0) - return -1; - for (i = 0; i < n; i++) { - if (!(name = virXMLPropString(nodes[i], "name"))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("No name supplied for element")); - return -1; - } + return 0; +} - if (!nodes[i]->children || - !nodes[i]->children->content) { - virReportError(VIR_ERR_XML_ERROR, - _("No value supplied for element"), - name); - return -1; - } - if (VIR_ALLOC(def->os.initenv[i]) < 0) - return -1; +static int +virDomainEmulatorSchedParse(xmlNodePtr node, + virDomainDefPtr def) +{ + g_autofree virDomainThreadSchedParamPtr sched = NULL; - def->os.initenv[i]->name = name; - def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content); - } - def->os.initenv[n] = NULL; - VIR_FREE(nodes); - } + if (VIR_ALLOC(sched) < 0) + return -1; - if (def->os.type == VIR_DOMAIN_OSTYPE_XEN || - def->os.type == VIR_DOMAIN_OSTYPE_XENPVH || - def->os.type == VIR_DOMAIN_OSTYPE_HVM || - def->os.type == VIR_DOMAIN_OSTYPE_UML) { - g_autofree char *firmware = NULL; - xmlNodePtr loader_node; + if (virDomainSchedulerParseCommonAttrs(node, + &sched->policy, + &sched->priority) < 0) + return -1; - def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt); - def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt); - def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); - def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt); - def->os.root = virXPathString("string(./os/root[1])", ctxt); + def->cputune.emulatorsched = g_steal_pointer(&sched); + return 0; +} - if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && - (firmware = virXPathString("string(./os/@firmware)", ctxt))) { - int fw = virDomainOsDefFirmwareTypeFromString(firmware); - if (fw <= 0) { - virReportError(VIR_ERR_XML_ERROR, - _("unknown firmware value %s"), - firmware); - return -1; - } +static virBitmapPtr +virDomainSchedulerParse(xmlNodePtr node, + const char *name, + virProcessSchedPolicy *policy, + int *priority) +{ + virBitmapPtr ret = NULL; + g_autofree char *tmp = NULL; - def->os.firmware = fw; - } + if (!(tmp = virXMLPropString(node, name))) { + virReportError(VIR_ERR_XML_ERROR, + _("Missing attribute '%s' in element '%sched'"), + name, name); + goto error; + } - if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) { - const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE; + if (virBitmapParse(tmp, &ret, VIR_DOMAIN_CPUMASK_LEN) < 0) + goto error; - if (VIR_ALLOC(def->os.loader) < 0) - return -1; + if (virBitmapIsAllClear(ret)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("'%s' scheduler bitmap '%s' is empty"), + name, tmp); + goto error; + } - if (virDomainLoaderDefParseXML(loader_node, - def->os.loader, - fwAutoSelect) < 0) - return -1; + if (virDomainSchedulerParseCommonAttrs(node, policy, priority) < 0) + goto error; - def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt); - if (!fwAutoSelect) - def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt); - } - } + return ret; - if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) { - if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0) - return -1; + error: + virBitmapFree(ret); + return NULL; +} - if (n > 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Only one acpi table is supported")); - return -1; - } - if (n == 1) { - tmp = virXMLPropString(nodes[0], "type"); +static int +virDomainThreadSchedParseHelper(xmlNodePtr node, + const char *name, + virDomainThreadSchedParamPtr (*func)(virDomainDefPtr, unsigned int), + virDomainDefPtr def) +{ + ssize_t next = -1; + virDomainThreadSchedParamPtr sched = NULL; + virProcessSchedPolicy policy = 0; + int priority = 0; + g_autoptr(virBitmap) map = NULL; - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing acpi table type")); - return -1; - } + if (!(map = virDomainSchedulerParse(node, name, &policy, &priority))) + return -1; - if (STREQ_NULLABLE(tmp, "slic")) { - VIR_FREE(tmp); - tmp = virXMLNodeContentString(nodes[0]); - def->os.slic_table = virFileSanitizePath(tmp); - } else { - virReportError(VIR_ERR_XML_ERROR, - _("Unknown acpi table type: %s"), - tmp); - return -1; - } - } + while ((next = virBitmapNextSetBit(map, next)) > -1) { + if (!(sched = func(def, next))) + return -1; - if (virDomainDefParseBootXML(ctxt, def) < 0) + if (sched->policy != VIR_PROC_POLICY_NONE) { + virReportError(VIR_ERR_XML_DETAIL, + _("%ssched attributes 'vcpus' must not overlap"), + name); return -1; + } + + sched->policy = policy; + sched->priority = priority; } return 0; @@ -19738,1586 +19891,1454 @@ virDomainDefParseBootOptions(virDomainDefPtr def, static int -virDomainResctrlParseVcpus(virDomainDefPtr def, - xmlNodePtr node, - virBitmapPtr *vcpus) +virDomainVcpuThreadSchedParse(xmlNodePtr node, + virDomainDefPtr def) { - g_autofree char *vcpus_str = NULL; + return virDomainThreadSchedParseHelper(node, "vcpus", + virDomainDefGetVcpuSched, + def); +} - vcpus_str = virXMLPropString(node, "vcpus"); - if (!vcpus_str) { - virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"), - node->name); - return -1; - } - if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid %s attribute 'vcpus' value '%s'"), - node->name, vcpus_str); - return -1; - } - /* We need to limit the bitmap to number of vCPUs. If there's nothing left, - * then we can just clean up and return 0 immediately */ - virBitmapShrink(*vcpus, def->maxvcpus); +static virDomainThreadSchedParamPtr +virDomainDefGetIOThreadSched(virDomainDefPtr def, + unsigned int iothread) +{ + virDomainIOThreadIDDefPtr iothrinfo; - return 0; + if (!(iothrinfo = virDomainIOThreadIDFind(def, iothread))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Cannot find 'iothread' : %u"), + iothread); + return NULL; + } + + return &iothrinfo->sched; } static int -virDomainResctrlVcpuMatch(virDomainDefPtr def, - virBitmapPtr vcpus, - virDomainResctrlDefPtr *resctrl) +virDomainIOThreadSchedParse(xmlNodePtr node, + virDomainDefPtr def) { - ssize_t i = 0; - - for (i = 0; i < def->nresctrls; i++) { - /* vcpus group has been created, directly use the existing one. - * Just updating memory allocation information of that group - */ - if (virBitmapEqual(def->resctrls[i]->vcpus, vcpus)) { - *resctrl = def->resctrls[i]; - break; - } - if (virBitmapOverlaps(def->resctrls[i]->vcpus, vcpus)) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Overlapping vcpus in resctrls")); - return -1; - } - } - return 0; + return virDomainThreadSchedParseHelper(node, "iothreads", + virDomainDefGetIOThreadSched, + def); } static int -virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt, - xmlNodePtr node, - virResctrlAllocPtr alloc) +virDomainVcpuParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + virDomainXMLOptionPtr xmlopt) { - VIR_XPATH_NODE_AUTORESTORE(ctxt); - unsigned int level; - unsigned int cache; - int type; - unsigned long long size; + int n; + xmlNodePtr vcpuNode; + size_t i; + unsigned int maxvcpus; + unsigned int vcpus; g_autofree char *tmp = NULL; + g_autofree xmlNodePtr *nodes = NULL; - ctxt->node = node; + vcpus = maxvcpus = 1; - tmp = virXMLPropString(node, "id"); - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing cachetune attribute 'id'")); - return -1; - } - if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid cachetune attribute 'id' value '%s'"), - tmp); - return -1; - } - VIR_FREE(tmp); + if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) { + if ((tmp = virXMLNodeContentString(vcpuNode))) { + if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("maximum vcpus count must be an integer")); + return -1; + } + VIR_FREE(tmp); + } - tmp = virXMLPropString(node, "level"); - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing cachetune attribute 'level'")); - return -1; - } - if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid cachetune attribute 'level' value '%s'"), - tmp); - return -1; - } - VIR_FREE(tmp); + if ((tmp = virXMLPropString(vcpuNode, "current"))) { + if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("current vcpus count must be an integer")); + return -1; + } + VIR_FREE(tmp); + } else { + vcpus = maxvcpus; + } - tmp = virXMLPropString(node, "type"); - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing cachetune attribute 'type'")); - return -1; - } - type = virCacheTypeFromString(tmp); - if (type < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid cachetune attribute 'type' value '%s'"), - tmp); - return -1; + tmp = virXMLPropString(vcpuNode, "placement"); + if (tmp) { + if ((def->placement_mode = + virDomainCpuPlacementModeTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported CPU placement mode '%s'"), + tmp); + return -1; + } + VIR_FREE(tmp); + } else { + def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC; + } + + if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) { + tmp = virXMLPropString(vcpuNode, "cpuset"); + if (tmp) { + if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) + return -1; + + if (virBitmapIsAllClear(def->cpumask)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid value of 'cpuset': %s"), tmp); + return -1; + } + + VIR_FREE(tmp); + } + } } - if (virDomainParseScaledValue("./@size", "./@unit", - ctxt, &size, 1024, - ULLONG_MAX, true) < 0) + if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0) return -1; - if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0) + if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0) return -1; - return 0; -} + 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; -/* Checking if the monitor's vcpus and tag is conflicted with existing - * allocation and monitors. - * - * Returns 1 if @monitor->vcpus equals to @resctrl->vcpus, then the monitor - * will share the underlying resctrl group with @resctrl->alloc. Returns -1 - * if any conflict found. Returns 0 if no conflict and @monitor->vcpus is - * not equal to @resctrl->vcpus. - */ -static int -virDomainResctrlValidateMonitor(virDomainResctrlDefPtr resctrl, - virDomainResctrlMonDefPtr monitor) -{ - size_t i = 0; - int vcpu = -1; - bool vcpus_overlap_any = false; - bool vcpus_equal_to_resctrl = false; - bool vcpus_overlap_no_resctrl = false; - bool default_alloc_monitor = virResctrlAllocIsEmpty(resctrl->alloc); + if (!(tmp = virXMLPropString(nodes[i], "id")) || + virStrToLong_uip(tmp, NULL, 10, &id) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing or invalid vcpu id")); + return -1; + } - if (virBitmapIsAllClear(monitor->vcpus)) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("vcpus is empty")); - return -1; - } + VIR_FREE(tmp); - while ((vcpu = virBitmapNextSetBit(monitor->vcpus, vcpu)) >= 0) { - if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("Monitor vcpus conflicts with allocation")); - return -1; - } - } + if (id >= def->maxvcpus) { + virReportError(VIR_ERR_XML_ERROR, + _("vcpu id '%u' is out of range of maximum " + "vcpu count"), id); + return -1; + } - vcpus_equal_to_resctrl = virBitmapEqual(monitor->vcpus, resctrl->vcpus); + vcpu = virDomainDefGetVcpu(def, id); - for (i = 0; i < resctrl->nmonitors; i++) { - if (virBitmapEqual(monitor->vcpus, resctrl->monitors[i]->vcpus)) { - if (monitor->tag != resctrl->monitors[i]->tag) { - continue; - } else { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("Identical vcpus found in same type monitors")); + if (!(tmp = virXMLPropString(nodes[i], "enabled"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing vcpu enabled state")); return -1; } - } - if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) - vcpus_overlap_any = true; + if ((state = virTristateBoolTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid vcpu 'enabled' value '%s'"), tmp); + return -1; + } + VIR_FREE(tmp); - if (vcpus_equal_to_resctrl || - virBitmapEqual(resctrl->monitors[i]->vcpus, resctrl->vcpus)) - continue; + vcpu->online = state == VIR_TRISTATE_BOOL_YES; - if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) - vcpus_overlap_no_resctrl = true; - } + if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) { + int hotpluggable; + if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid vcpu 'hotpluggable' value '%s'"), tmp); + return -1; + } + vcpu->hotpluggable = hotpluggable; + VIR_FREE(tmp); + } - if (vcpus_overlap_no_resctrl || - (default_alloc_monitor && vcpus_overlap_any)) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("vcpus overlaps in resctrl groups")); - return -1; + if ((tmp = virXMLPropString(nodes[i], "order"))) { + if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("invalid vcpu order")); + return -1; + } + vcpu->order = order; + VIR_FREE(tmp); + } + } + } else { + if (virDomainDefSetVcpus(def, vcpus) < 0) + return -1; } - if (vcpus_equal_to_resctrl && !default_alloc_monitor) - return 1; - return 0; } -#define VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL 3 - static int -virDomainResctrlMonDefParse(virDomainDefPtr def, - xmlXPathContextPtr ctxt, - xmlNodePtr node, - virResctrlMonitorType tag, - virDomainResctrlDefPtr resctrl) +virDomainDefParseBootOptions(virDomainDefPtr def, + xmlXPathContextPtr ctxt) { - virDomainResctrlMonDefPtr domresmon = NULL; - VIR_XPATH_NODE_AUTORESTORE(ctxt); - unsigned int level = 0; - size_t i = 0; - int n = 0; - int rv = -1; - int ret = -1; + char *name = NULL; + size_t i; + int n; g_autofree xmlNodePtr *nodes = NULL; g_autofree char *tmp = NULL; - g_autofree char *id = NULL; - - ctxt->node = node; - if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot extract monitor nodes")); - goto cleanup; - } + /* + * Booting options for different OS types.... + * + * - A bootloader (and optional kernel+initrd) (xen) + * - A kernel + initrd (xen) + * - A boot device (and optional kernel+initrd) (hvm) + * - An init script (exe) + */ - for (i = 0; i < n; i++) { - if (VIR_ALLOC(domresmon) < 0) - goto cleanup; + if (def->os.type == VIR_DOMAIN_OSTYPE_EXE) { + def->os.init = virXPathString("string(./os/init[1])", ctxt); + def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); + def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt); + def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt); + def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt); - domresmon->tag = tag; + if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0) + return -1; - domresmon->instance = virResctrlMonitorNew(); - if (!domresmon->instance) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Could not create monitor")); - goto cleanup; + if (VIR_ALLOC_N(def->os.initargv, n+1) < 0) + return -1; + for (i = 0; i < n; i++) { + if (!nodes[i]->children || + !nodes[i]->children->content) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("No data supplied for element")); + return -1; + } + def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content); } + def->os.initargv[n] = NULL; + VIR_FREE(nodes); - if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) { - tmp = virXMLPropString(nodes[i], "level"); - if (!tmp) { + if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0) + return -1; + + if (VIR_ALLOC_N(def->os.initenv, n+1) < 0) + return -1; + for (i = 0; i < n; i++) { + if (!(name = virXMLPropString(nodes[i], "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing monitor attribute 'level'")); - goto cleanup; + _("No name supplied for element")); + return -1; } - if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { + if (!nodes[i]->children || + !nodes[i]->children->content) { virReportError(VIR_ERR_XML_ERROR, - _("Invalid monitor attribute 'level' value '%s'"), - tmp); - goto cleanup; + _("No value supplied for element"), + name); + return -1; } - if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) { + if (VIR_ALLOC(def->os.initenv[i]) < 0) + return -1; + + def->os.initenv[i]->name = name; + def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content); + } + def->os.initenv[n] = NULL; + VIR_FREE(nodes); + } + + if (def->os.type == VIR_DOMAIN_OSTYPE_XEN || + def->os.type == VIR_DOMAIN_OSTYPE_XENPVH || + def->os.type == VIR_DOMAIN_OSTYPE_HVM || + def->os.type == VIR_DOMAIN_OSTYPE_UML) { + g_autofree char *firmware = NULL; + xmlNodePtr loader_node; + + def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt); + def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt); + def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); + def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt); + def->os.root = virXPathString("string(./os/root[1])", ctxt); + + if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && + (firmware = virXPathString("string(./os/@firmware)", ctxt))) { + int fw = virDomainOsDefFirmwareTypeFromString(firmware); + + if (fw <= 0) { virReportError(VIR_ERR_XML_ERROR, - _("Invalid monitor cache level '%d'"), - level); - goto cleanup; + _("unknown firmware value %s"), + firmware); + return -1; } - VIR_FREE(tmp); + def->os.firmware = fw; } - if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0) - goto cleanup; + if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) { + const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE; - rv = virDomainResctrlValidateMonitor(resctrl, domresmon); - if (rv < 0) - goto cleanup; + if (VIR_ALLOC(def->os.loader) < 0) + return -1; - /* If monitor's vcpu list is identical to the vcpu list of the - * associated allocation, set monitor's id to the same value - * as the allocation. */ - if (rv == 1) { - const char *alloc_id = virResctrlAllocGetID(resctrl->alloc); + if (virDomainLoaderDefParseXML(loader_node, + def->os.loader, + fwAutoSelect) < 0) + return -1; - id = g_strdup(alloc_id); - } else { - if (!(tmp = virBitmapFormat(domresmon->vcpus))) - goto cleanup; + def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt); + if (!fwAutoSelect) + def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt); + } + } - id = g_strdup_printf("vcpus_%s", tmp); + if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) { + if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0) + return -1; + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Only one acpi table is supported")); + return -1; } - virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc); + if (n == 1) { + tmp = virXMLPropString(nodes[0], "type"); - if (virResctrlMonitorSetID(domresmon->instance, id) < 0) - goto cleanup; + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing acpi table type")); + return -1; + } - if (VIR_APPEND_ELEMENT(resctrl->monitors, - resctrl->nmonitors, - domresmon) < 0) - goto cleanup; + if (STREQ_NULLABLE(tmp, "slic")) { + VIR_FREE(tmp); + tmp = virXMLNodeContentString(nodes[0]); + def->os.slic_table = virFileSanitizePath(tmp); + } else { + virReportError(VIR_ERR_XML_ERROR, + _("Unknown acpi table type: %s"), + tmp); + return -1; + } + } - VIR_FREE(id); - VIR_FREE(tmp); + if (virDomainDefParseBootXML(ctxt, def) < 0) + return -1; } - ret = 0; - cleanup: - virDomainResctrlMonDefFree(domresmon); - return ret; + return 0; } -static virDomainResctrlDefPtr -virDomainResctrlNew(xmlNodePtr node, - virResctrlAllocPtr alloc, - virBitmapPtr vcpus, - unsigned int flags) +static int +virDomainResctrlParseVcpus(virDomainDefPtr def, + xmlNodePtr node, + virBitmapPtr *vcpus) { - virDomainResctrlDefPtr resctrl = NULL; - virDomainResctrlDefPtr ret = NULL; g_autofree char *vcpus_str = NULL; - g_autofree char *alloc_id = NULL; - /* We need to format it back because we need to be consistent in the naming - * even when users specify some "sub-optimal" string there. */ - vcpus_str = virBitmapFormat(vcpus); - if (!vcpus_str) - return NULL; + vcpus_str = virXMLPropString(node, "vcpus"); + if (!vcpus_str) { + virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"), + node->name); + return -1; + } + if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid %s attribute 'vcpus' value '%s'"), + node->name, vcpus_str); + return -1; + } - if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) - alloc_id = virXMLPropString(node, "id"); + /* We need to limit the bitmap to number of vCPUs. If there's nothing left, + * then we can just clean up and return 0 immediately */ + virBitmapShrink(*vcpus, def->maxvcpus); - if (!alloc_id) { - /* The number of allocations is limited and the directory structure is flat, - * not hierarchical, so we need to have all same allocations in one - * directory, so it's nice to have it named appropriately. For now it's - * 'vcpus_...' but it's designed in order for it to be changeable in the - * future (it's part of the status XML). */ - alloc_id = g_strdup_printf("vcpus_%s", vcpus_str); - } + return 0; +} - if (virResctrlAllocSetID(alloc, alloc_id) < 0) - goto cleanup; - if (VIR_ALLOC(resctrl) < 0) - goto cleanup; +static int +virDomainResctrlVcpuMatch(virDomainDefPtr def, + virBitmapPtr vcpus, + virDomainResctrlDefPtr *resctrl) +{ + ssize_t i = 0; - if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to copy 'vcpus'")); - goto cleanup; + for (i = 0; i < def->nresctrls; i++) { + /* vcpus group has been created, directly use the existing one. + * Just updating memory allocation information of that group + */ + if (virBitmapEqual(def->resctrls[i]->vcpus, vcpus)) { + *resctrl = def->resctrls[i]; + break; + } + if (virBitmapOverlaps(def->resctrls[i]->vcpus, vcpus)) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Overlapping vcpus in resctrls")); + return -1; + } } - - resctrl->alloc = virObjectRef(alloc); - - ret = g_steal_pointer(&resctrl); - cleanup: - virDomainResctrlDefFree(resctrl); - return ret; + return 0; } static int -virDomainCachetuneDefParse(virDomainDefPtr def, - xmlXPathContextPtr ctxt, - xmlNodePtr node, - unsigned int flags) +virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virResctrlAllocPtr alloc) { VIR_XPATH_NODE_AUTORESTORE(ctxt); - virDomainResctrlDefPtr resctrl = NULL; - ssize_t i = 0; - int n; - int ret = -1; - g_autoptr(virBitmap) vcpus = NULL; - g_autofree xmlNodePtr *nodes = NULL; - g_autoptr(virResctrlAlloc) alloc = NULL; + unsigned int level; + unsigned int cache; + int type; + unsigned long long size; + g_autofree char *tmp = NULL; ctxt->node = node; - if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) + tmp = virXMLPropString(node, "id"); + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing cachetune attribute 'id'")); return -1; - - if (virBitmapIsAllClear(vcpus)) - return 0; - - if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot extract cache nodes under cachetune")); + } + if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid cachetune attribute 'id' value '%s'"), + tmp); return -1; } + VIR_FREE(tmp); - if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) + tmp = virXMLPropString(node, "level"); + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing cachetune attribute 'level'")); + return -1; + } + if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid cachetune attribute 'level' value '%s'"), + tmp); return -1; + } + VIR_FREE(tmp); - if (resctrl) { + tmp = virXMLPropString(node, "type"); + if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("Identical vcpus in cachetunes found")); + _("Missing cachetune attribute 'type'")); return -1; } - - if (!(alloc = virResctrlAllocNew())) + type = virCacheTypeFromString(tmp); + if (type < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid cachetune attribute 'type' value '%s'"), + tmp); return -1; - - for (i = 0; i < n; i++) { - if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0) - return -1; } - if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) + if (virDomainParseScaledValue("./@size", "./@unit", + ctxt, &size, 1024, + ULLONG_MAX, true) < 0) return -1; - if (virDomainResctrlMonDefParse(def, ctxt, node, - VIR_RESCTRL_MONITOR_TYPE_CACHE, - resctrl) < 0) - goto cleanup; - - /* If no element or element in , do not - * append any resctrl element */ - if (!resctrl->nmonitors && n == 0) { - ret = 0; - goto cleanup; - } - - if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0) - goto cleanup; + if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0) + return -1; - ret = 0; - cleanup: - virDomainResctrlDefFree(resctrl); - return ret; + return 0; } +/* Checking if the monitor's vcpus and tag is conflicted with existing + * allocation and monitors. + * + * Returns 1 if @monitor->vcpus equals to @resctrl->vcpus, then the monitor + * will share the underlying resctrl group with @resctrl->alloc. Returns -1 + * if any conflict found. Returns 0 if no conflict and @monitor->vcpus is + * not equal to @resctrl->vcpus. + */ static int -virDomainDefParseCaps(virDomainDefPtr def, - xmlXPathContextPtr ctxt, - virDomainXMLOptionPtr xmlopt) +virDomainResctrlValidateMonitor(virDomainResctrlDefPtr resctrl, + virDomainResctrlMonDefPtr monitor) { - g_autofree char *virttype = NULL; - g_autofree char *arch = NULL; - g_autofree char *ostype = NULL; - - virttype = virXPathString("string(./@type)", ctxt); - ostype = virXPathString("string(./os/type[1])", ctxt); - arch = virXPathString("string(./os/type[1]/@arch)", ctxt); - - def->os.bootloader = virXPathString("string(./bootloader)", ctxt); - def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); - def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt); - def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); + size_t i = 0; + int vcpu = -1; + bool vcpus_overlap_any = false; + bool vcpus_equal_to_resctrl = false; + bool vcpus_overlap_no_resctrl = false; + bool default_alloc_monitor = virResctrlAllocIsEmpty(resctrl->alloc); - if (!virttype) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("missing domain type attribute")); - return -1; - } - if ((def->virtType = virDomainVirtTypeFromString(virttype)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid domain type %s"), virttype); + if (virBitmapIsAllClear(monitor->vcpus)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("vcpus is empty")); return -1; } - if (!ostype) { - if (def->os.bootloader) { - def->os.type = VIR_DOMAIN_OSTYPE_XEN; - } else { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("an os must be specified")); - return -1; - } - } else { - if ((def->os.type = virDomainOSTypeFromString(ostype)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown OS type '%s'"), ostype); + while ((vcpu = virBitmapNextSetBit(monitor->vcpus, vcpu)) >= 0) { + if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Monitor vcpus conflicts with allocation")); return -1; } } - /* - * HACK: For xen driver we previously used bogus 'linux' as the - * os type for paravirt, whereas capabilities declare it to - * be 'xen'. So we accept the former and convert - */ - if (def->os.type == VIR_DOMAIN_OSTYPE_LINUX && - def->virtType == VIR_DOMAIN_VIRT_XEN) { - def->os.type = VIR_DOMAIN_OSTYPE_XEN; + vcpus_equal_to_resctrl = virBitmapEqual(monitor->vcpus, resctrl->vcpus); + + for (i = 0; i < resctrl->nmonitors; i++) { + if (virBitmapEqual(monitor->vcpus, resctrl->monitors[i]->vcpus)) { + if (monitor->tag != resctrl->monitors[i]->tag) { + continue; + } else { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Identical vcpus found in same type monitors")); + return -1; + } + } + + if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) + vcpus_overlap_any = true; + + if (vcpus_equal_to_resctrl || + virBitmapEqual(resctrl->monitors[i]->vcpus, resctrl->vcpus)) + continue; + + if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) + vcpus_overlap_no_resctrl = true; } - if (arch && !(def->os.arch = virArchFromString(arch))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unknown architecture %s"), arch); + if (vcpus_overlap_no_resctrl || + (default_alloc_monitor && vcpus_overlap_any)) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("vcpus overlaps in resctrl groups")); return -1; } - if (def->os.arch == VIR_ARCH_NONE) { - if (xmlopt && xmlopt->config.defArch != VIR_ARCH_NONE) - def->os.arch = xmlopt->config.defArch; - else - def->os.arch = virArchFromHost(); - } + if (vcpus_equal_to_resctrl && !default_alloc_monitor) + return 1; return 0; } +#define VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL 3 + static int -virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt, - xmlNodePtr node, - virResctrlAllocPtr alloc) +virDomainResctrlMonDefParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + xmlNodePtr node, + virResctrlMonitorType tag, + virDomainResctrlDefPtr resctrl) { + virDomainResctrlMonDefPtr domresmon = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); - unsigned int id; - unsigned int bandwidth; + unsigned int level = 0; + size_t i = 0; + int n = 0; + int rv = -1; + int ret = -1; + g_autofree xmlNodePtr *nodes = NULL; g_autofree char *tmp = NULL; + g_autofree char *id = NULL; ctxt->node = node; - tmp = virXMLPropString(node, "id"); - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing memorytune attribute 'id'")); - return -1; - } - if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid memorytune attribute 'id' value '%s'"), - tmp); - return -1; - } - VIR_FREE(tmp); - - tmp = virXMLPropString(node, "bandwidth"); - if (!tmp) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Missing memorytune attribute 'bandwidth'")); - return -1; - } - if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) { - virReportError(VIR_ERR_XML_ERROR, - _("Invalid memorytune attribute 'bandwidth' value '%s'"), - tmp); - return -1; - } - if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0) - return -1; + if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot extract monitor nodes")); + goto cleanup; + } - return 0; -} + for (i = 0; i < n; i++) { + if (VIR_ALLOC(domresmon) < 0) + goto cleanup; + domresmon->tag = tag; -static int -virDomainMemorytuneDefParse(virDomainDefPtr def, - xmlXPathContextPtr ctxt, - xmlNodePtr node, - unsigned int flags) -{ - VIR_XPATH_NODE_AUTORESTORE(ctxt); - virDomainResctrlDefPtr resctrl = NULL; - virDomainResctrlDefPtr newresctrl = NULL; - g_autoptr(virBitmap) vcpus = NULL; - g_autofree xmlNodePtr *nodes = NULL; - g_autoptr(virResctrlAlloc) alloc = NULL; - ssize_t i = 0; - size_t nmons = 0; - size_t ret = -1; + domresmon->instance = virResctrlMonitorNew(); + if (!domresmon->instance) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not create monitor")); + goto cleanup; + } - int n; + if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) { + tmp = virXMLPropString(nodes[i], "level"); + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing monitor attribute 'level'")); + goto cleanup; + } - ctxt->node = node; + if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid monitor attribute 'level' value '%s'"), + tmp); + goto cleanup; + } - if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) - return -1; + if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid monitor cache level '%d'"), + level); + goto cleanup; + } - if (virBitmapIsAllClear(vcpus)) - return 0; + VIR_FREE(tmp); + } - if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot extract memory nodes under memorytune")); - return -1; - } + if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0) + goto cleanup; - if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) - return -1; + rv = virDomainResctrlValidateMonitor(resctrl, domresmon); + if (rv < 0) + goto cleanup; - if (resctrl) { - alloc = virObjectRef(resctrl->alloc); - } else { - if (!(alloc = virResctrlAllocNew())) - return -1; - } + /* If monitor's vcpu list is identical to the vcpu list of the + * associated allocation, set monitor's id to the same value + * as the allocation. */ + if (rv == 1) { + const char *alloc_id = virResctrlAllocGetID(resctrl->alloc); - /* First, parse element if any element exists */ - for (i = 0; i < n; i++) { - if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0) - return -1; - } + id = g_strdup(alloc_id); + } else { + if (!(tmp = virBitmapFormat(domresmon->vcpus))) + goto cleanup; - /* - * If this is a new allocation, format ID and append to resctrl, otherwise - * just update the existing alloc information, which is done in above - * virDomainMemorytuneDefParseMemory */ - if (!resctrl) { - if (!(newresctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) - return -1; + id = g_strdup_printf("vcpus_%s", tmp); + } - resctrl = newresctrl; - } + virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc); - /* Next, parse element */ - nmons = resctrl->nmonitors; - if (virDomainResctrlMonDefParse(def, ctxt, node, - VIR_RESCTRL_MONITOR_TYPE_MEMBW, - resctrl) < 0) - goto cleanup; + if (virResctrlMonitorSetID(domresmon->instance, id) < 0) + goto cleanup; - nmons = resctrl->nmonitors - nmons; - /* Now @nmons contains the new element number found in current - * element, and @n holds the number of new element, - * only append the new @newresctrl object to domain if any of them is - * not zero. */ - if (newresctrl && (nmons || n)) { - if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, newresctrl) < 0) + if (VIR_APPEND_ELEMENT(resctrl->monitors, + resctrl->nmonitors, + domresmon) < 0) goto cleanup; + + VIR_FREE(id); + VIR_FREE(tmp); } ret = 0; cleanup: - virDomainResctrlDefFree(newresctrl); + virDomainResctrlMonDefFree(domresmon); return ret; } -static virDomainDefPtr -virDomainDefParseXML(xmlDocPtr xml, - xmlXPathContextPtr ctxt, - virDomainXMLOptionPtr xmlopt, - unsigned int flags) +static virDomainResctrlDefPtr +virDomainResctrlNew(xmlNodePtr node, + virResctrlAllocPtr alloc, + virBitmapPtr vcpus, + unsigned int flags) { - xmlNodePtr node = NULL; - size_t i, j; - int n, gic_version; - long id = -1; - virDomainDefPtr def; - bool uuid_generated = false; - bool usb_none = false; - bool usb_other = false; - bool usb_master = false; - g_autofree xmlNodePtr *nodes = NULL; - g_autofree char *tmp = NULL; - - if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA) { - g_autofree char *schema = NULL; - - schema = virFileFindResource("domain.rng", - abs_top_srcdir "/docs/schemas", - PKGDATADIR "/schemas"); - if (!schema) - return NULL; - if (virXMLValidateAgainstSchema(schema, xml) < 0) - return NULL; - } + virDomainResctrlDefPtr resctrl = NULL; + virDomainResctrlDefPtr ret = NULL; + g_autofree char *vcpus_str = NULL; + g_autofree char *alloc_id = NULL; - if (!(def = virDomainDefNew())) + /* We need to format it back because we need to be consistent in the naming + * even when users specify some "sub-optimal" string there. */ + vcpus_str = virBitmapFormat(vcpus); + if (!vcpus_str) return NULL; if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) - if (virXPathLong("string(./@id)", ctxt, &id) < 0) - id = -1; - def->id = (int)id; - - if (virDomainDefParseCaps(def, ctxt, xmlopt) < 0) - goto error; + alloc_id = virXMLPropString(node, "id"); - /* Extract domain name */ - if (!(def->name = virXPathString("string(./name[1])", ctxt))) { - virReportError(VIR_ERR_NO_NAME, NULL); - goto error; + if (!alloc_id) { + /* The number of allocations is limited and the directory structure is flat, + * not hierarchical, so we need to have all same allocations in one + * directory, so it's nice to have it named appropriately. For now it's + * 'vcpus_...' but it's designed in order for it to be changeable in the + * future (it's part of the status XML). */ + alloc_id = g_strdup_printf("vcpus_%s", vcpus_str); } - /* Extract domain uuid. If both uuid and sysinfo/system/entry/uuid - * exist, they must match; and if only the latter exists, it can - * also serve as the uuid. */ - tmp = virXPathString("string(./uuid[1])", ctxt); - if (!tmp) { - if (virUUIDGenerate(def->uuid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to generate UUID")); - goto error; - } - uuid_generated = true; - } else { - if (virUUIDParse(tmp, def->uuid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed uuid element")); - goto error; - } - VIR_FREE(tmp); + if (virResctrlAllocSetID(alloc, alloc_id) < 0) + goto cleanup; + + if (VIR_ALLOC(resctrl) < 0) + goto cleanup; + + if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to copy 'vcpus'")); + goto cleanup; } - /* Extract domain genid - a genid can either be provided or generated */ - if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0) - goto error; + resctrl->alloc = virObjectRef(alloc); - if (n > 0) { - if (n != 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("element 'genid' can only appear once")); - goto error; - } - def->genidRequested = true; - if (!(tmp = virXPathString("string(./genid)", ctxt))) { - if (virUUIDGenerate(def->genid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Failed to generate genid")); - goto error; - } - def->genidGenerated = true; - } else { - if (virUUIDParse(tmp, def->genid) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("malformed genid element")); - goto error; - } - VIR_FREE(tmp); - } + ret = g_steal_pointer(&resctrl); + cleanup: + virDomainResctrlDefFree(resctrl); + return ret; +} + + +static int +virDomainCachetuneDefParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + xmlNodePtr node, + unsigned int flags) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt); + virDomainResctrlDefPtr resctrl = NULL; + ssize_t i = 0; + int n; + int ret = -1; + g_autoptr(virBitmap) vcpus = NULL; + g_autofree xmlNodePtr *nodes = NULL; + g_autoptr(virResctrlAlloc) alloc = NULL; + + ctxt->node = node; + + if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) + return -1; + + if (virBitmapIsAllClear(vcpus)) + return 0; + + if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot extract cache nodes under cachetune")); + return -1; } - VIR_FREE(nodes); - /* Extract short description of domain (title) */ - def->title = virXPathString("string(./title[1])", ctxt); - if (def->title && strchr(def->title, '\n')) { + if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) + return -1; + + if (resctrl) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("Domain title can't contain newlines")); - goto error; + _("Identical vcpus in cachetunes found")); + return -1; } - /* Extract documentation if present */ - def->description = virXPathString("string(./description[1])", ctxt); + if (!(alloc = virResctrlAllocNew())) + return -1; - /* analysis of security label, done early even though we format it - * late, so devices can refer to this for defaults */ - if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL)) { - if (virSecurityLabelDefsParseXML(def, ctxt, xmlopt, flags) == -1) - goto error; + for (i = 0; i < n; i++) { + if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0) + return -1; } - /* Extract domain memory */ - if (virDomainParseMemory("./memory[1]", NULL, ctxt, - &def->mem.total_memory, false, true) < 0) - goto error; - - if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt, - &def->mem.cur_balloon, false, true) < 0) - goto error; + if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) + return -1; - if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt, - &def->mem.max_memory, false, false) < 0) - goto error; + if (virDomainResctrlMonDefParse(def, ctxt, node, + VIR_RESCTRL_MONITOR_TYPE_CACHE, + resctrl) < 0) + goto cleanup; - if (virXPathUInt("string(./maxMemory[1]/@slots)", ctxt, &def->mem.memory_slots) == -2) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Failed to parse memory slot count")); - goto error; + /* If no element or element in , do not + * append any resctrl element */ + if (!resctrl->nmonitors && n == 0) { + ret = 0; + goto cleanup; } - /* and info about it */ - if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) && - (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) { + if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0) + goto cleanup; + + ret = 0; + cleanup: + virDomainResctrlDefFree(resctrl); + return ret; +} + + +static int +virDomainDefParseCaps(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + virDomainXMLOptionPtr xmlopt) +{ + g_autofree char *virttype = NULL; + g_autofree char *arch = NULL; + g_autofree char *ostype = NULL; + + virttype = virXPathString("string(./@type)", ctxt); + ostype = virXPathString("string(./os/type[1])", ctxt); + arch = virXPathString("string(./os/type[1]/@arch)", ctxt); + + def->os.bootloader = virXPathString("string(./bootloader)", ctxt); + def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); + def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt); + def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); + + if (!virttype) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing domain type attribute")); + return -1; + } + if ((def->virtType = virDomainVirtTypeFromString(virttype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Invalid memory core dump attribute value '%s'"), tmp); - goto error; + _("invalid domain type %s"), virttype); + return -1; } - VIR_FREE(tmp); - tmp = virXPathString("string(./memoryBacking/source/@type)", ctxt); - if (tmp) { - if ((def->mem.source = virDomainMemorySourceTypeFromString(tmp)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown memoryBacking/source/type '%s'"), tmp); - goto error; + if (!ostype) { + if (def->os.bootloader) { + def->os.type = VIR_DOMAIN_OSTYPE_XEN; + } else { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("an os must be specified")); + return -1; } - VIR_FREE(tmp); - } - - tmp = virXPathString("string(./memoryBacking/access/@mode)", ctxt); - if (tmp) { - if ((def->mem.access = virDomainMemoryAccessTypeFromString(tmp)) <= 0) { + } else { + if ((def->os.type = virDomainOSTypeFromString(ostype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown memoryBacking/access/mode '%s'"), tmp); - goto error; + _("unknown OS type '%s'"), ostype); + return -1; } - VIR_FREE(tmp); } - tmp = virXPathString("string(./memoryBacking/allocation/@mode)", ctxt); - if (tmp) { - if ((def->mem.allocation = virDomainMemoryAllocationTypeFromString(tmp)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown memoryBacking/allocation/mode '%s'"), tmp); - goto error; - } - VIR_FREE(tmp); + /* + * HACK: For xen driver we previously used bogus 'linux' as the + * os type for paravirt, whereas capabilities declare it to + * be 'xen'. So we accept the former and convert + */ + if (def->os.type == VIR_DOMAIN_OSTYPE_LINUX && + def->virtType == VIR_DOMAIN_VIRT_XEN) { + def->os.type = VIR_DOMAIN_OSTYPE_XEN; } - if (virXPathNode("./memoryBacking/hugepages", ctxt)) { - /* hugepages will be used */ - if ((n = virXPathNodeSet("./memoryBacking/hugepages/page", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract hugepages nodes")); - goto error; - } - - if (n) { - if (VIR_ALLOC_N(def->mem.hugepages, n) < 0) - goto error; - - for (i = 0; i < n; i++) { - if (virDomainHugepagesParseXML(nodes[i], ctxt, - &def->mem.hugepages[i]) < 0) - goto error; - def->mem.nhugepages++; - } - - VIR_FREE(nodes); - } else { - /* no hugepage pages */ - if (VIR_ALLOC(def->mem.hugepages) < 0) - goto error; + if (arch && !(def->os.arch = virArchFromString(arch))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unknown architecture %s"), arch); + return -1; + } - def->mem.nhugepages = 1; - } + if (def->os.arch == VIR_ARCH_NONE) { + if (xmlopt && xmlopt->config.defArch != VIR_ARCH_NONE) + def->os.arch = xmlopt->config.defArch; + else + def->os.arch = virArchFromHost(); } - if ((node = virXPathNode("./memoryBacking/nosharepages", ctxt))) - def->mem.nosharepages = true; + return 0; +} - if (virXPathBoolean("boolean(./memoryBacking/locked)", ctxt)) - def->mem.locked = true; - if (virXPathBoolean("boolean(./memoryBacking/discard)", ctxt)) - def->mem.discard = VIR_TRISTATE_BOOL_YES; +static int +virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virResctrlAllocPtr alloc) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt); + unsigned int id; + unsigned int bandwidth; + g_autofree char *tmp = NULL; - /* Extract blkio cgroup tunables */ - if (virXPathUInt("string(./blkiotune/weight)", ctxt, - &def->blkio.weight) < 0) - def->blkio.weight = 0; + ctxt->node = node; - if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot extract blkiotune nodes")); - goto error; + tmp = virXMLPropString(node, "id"); + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing memorytune attribute 'id'")); + return -1; } - if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0) - goto error; - - for (i = 0; i < n; i++) { - if (virDomainBlkioDeviceParseXML(nodes[i], - &def->blkio.devices[i]) < 0) - goto error; - def->blkio.ndevices++; - for (j = 0; j < i; j++) { - if (STREQ(def->blkio.devices[j].path, - def->blkio.devices[i].path)) { - virReportError(VIR_ERR_XML_ERROR, - _("duplicate blkio device path '%s'"), - def->blkio.devices[i].path); - goto error; - } - } + if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid memorytune attribute 'id' value '%s'"), + tmp); + return -1; } - VIR_FREE(nodes); - - /* Extract other memory tunables */ - if (virDomainParseMemoryLimit("./memtune/hard_limit[1]", NULL, ctxt, - &def->mem.hard_limit) < 0) - goto error; + VIR_FREE(tmp); - if (virDomainParseMemoryLimit("./memtune/soft_limit[1]", NULL, ctxt, - &def->mem.soft_limit) < 0) - goto error; + tmp = virXMLPropString(node, "bandwidth"); + if (!tmp) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing memorytune attribute 'bandwidth'")); + return -1; + } + if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid memorytune attribute 'bandwidth' value '%s'"), + tmp); + return -1; + } + if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0) + return -1; - if (virDomainParseMemory("./memtune/min_guarantee[1]", NULL, ctxt, - &def->mem.min_guarantee, false, false) < 0) - goto error; + return 0; +} - if (virDomainParseMemoryLimit("./memtune/swap_hard_limit[1]", NULL, ctxt, - &def->mem.swap_hard_limit) < 0) - goto error; - if (virDomainVcpuParse(def, ctxt, xmlopt) < 0) - goto error; +static int +virDomainMemorytuneDefParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + xmlNodePtr node, + unsigned int flags) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt); + virDomainResctrlDefPtr resctrl = NULL; + virDomainResctrlDefPtr newresctrl = NULL; + g_autoptr(virBitmap) vcpus = NULL; + g_autofree xmlNodePtr *nodes = NULL; + g_autoptr(virResctrlAlloc) alloc = NULL; + ssize_t i = 0; + size_t nmons = 0; + size_t ret = -1; - if (virDomainDefParseIOThreads(def, ctxt) < 0) - goto error; + int n; - /* Extract cpu tunables. */ - if ((n = virXPathULongLong("string(./cputune/shares[1])", ctxt, - &def->cputune.shares)) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune shares value")); - goto error; - } else if (n == 0) { - def->cputune.sharesSpecified = true; - } + ctxt->node = node; - if (virXPathULongLong("string(./cputune/period[1])", ctxt, - &def->cputune.period) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune period value")); - goto error; - } + if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) + return -1; - if (virXPathLongLong("string(./cputune/quota[1])", ctxt, - &def->cputune.quota) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune quota value")); - goto error; - } + if (virBitmapIsAllClear(vcpus)) + return 0; - if (virXPathULongLong("string(./cputune/global_period[1])", ctxt, - &def->cputune.global_period) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune global period value")); - goto error; + if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot extract memory nodes under memorytune")); + return -1; } - if (virXPathLongLong("string(./cputune/global_quota[1])", ctxt, - &def->cputune.global_quota) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune global quota value")); - goto error; - } + if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) + return -1; - if (virXPathULongLong("string(./cputune/emulator_period[1])", ctxt, - &def->cputune.emulator_period) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune emulator period value")); - goto error; + if (resctrl) { + alloc = virObjectRef(resctrl->alloc); + } else { + if (!(alloc = virResctrlAllocNew())) + return -1; } - if (virXPathLongLong("string(./cputune/emulator_quota[1])", ctxt, - &def->cputune.emulator_quota) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune emulator quota value")); - goto error; + /* First, parse element if any element exists */ + for (i = 0; i < n; i++) { + if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0) + return -1; } + /* + * If this is a new allocation, format ID and append to resctrl, otherwise + * just update the existing alloc information, which is done in above + * virDomainMemorytuneDefParseMemory */ + if (!resctrl) { + if (!(newresctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) + return -1; - if (virXPathULongLong("string(./cputune/iothread_period[1])", ctxt, - &def->cputune.iothread_period) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune iothread period value")); - goto error; - } - - if (virXPathLongLong("string(./cputune/iothread_quota[1])", ctxt, - &def->cputune.iothread_quota) < -1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("can't parse cputune iothread quota value")); - goto error; + resctrl = newresctrl; } - if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0) - goto error; + /* Next, parse element */ + nmons = resctrl->nmonitors; + if (virDomainResctrlMonDefParse(def, ctxt, node, + VIR_RESCTRL_MONITOR_TYPE_MEMBW, + resctrl) < 0) + goto cleanup; - for (i = 0; i < n; i++) { - if (virDomainVcpuPinDefParseXML(def, nodes[i])) - goto error; + nmons = resctrl->nmonitors - nmons; + /* Now @nmons contains the new element number found in current + * element, and @n holds the number of new element, + * only append the new @newresctrl object to domain if any of them is + * not zero. */ + if (newresctrl && (nmons || n)) { + if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, newresctrl) < 0) + goto cleanup; } - VIR_FREE(nodes); - if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract emulatorpin nodes")); - goto error; - } + ret = 0; + cleanup: + virDomainResctrlDefFree(newresctrl); + return ret; +} - if (n) { - if (n > 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("only one emulatorpin is supported")); - VIR_FREE(nodes); - goto error; - } - if (!(def->cputune.emulatorpin = virDomainEmulatorPinDefParseXML(nodes[0]))) - goto error; - } - VIR_FREE(nodes); +static virDomainDefPtr +virDomainDefParseXML(xmlDocPtr xml, + xmlXPathContextPtr ctxt, + virDomainXMLOptionPtr xmlopt, + unsigned int flags) +{ + xmlNodePtr node = NULL; + size_t i, j; + int n; + long id = -1; + virDomainDefPtr def; + bool uuid_generated = false; + bool usb_none = false; + bool usb_other = false; + bool usb_master = false; + g_autofree xmlNodePtr *nodes = NULL; + g_autofree char *tmp = NULL; + if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA) { + g_autofree char *schema = NULL; - if ((n = virXPathNodeSet("./cputune/iothreadpin", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract iothreadpin nodes")); - goto error; + schema = virFileFindResource("domain.rng", + abs_top_srcdir "/docs/schemas", + PKGDATADIR "/schemas"); + if (!schema) + return NULL; + if (virXMLValidateAgainstSchema(schema, xml) < 0) + return NULL; } - for (i = 0; i < n; i++) { - if (virDomainIOThreadPinDefParseXML(nodes[i], def) < 0) - goto error; - } - VIR_FREE(nodes); + if (!(def = virDomainDefNew())) + return NULL; - if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract vcpusched nodes")); - goto error; - } + if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) + if (virXPathLong("string(./@id)", ctxt, &id) < 0) + id = -1; + def->id = (int)id; - for (i = 0; i < n; i++) { - if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0) - goto error; - } - VIR_FREE(nodes); + if (virDomainDefParseCaps(def, ctxt, xmlopt) < 0) + goto error; - if ((n = virXPathNodeSet("./cputune/iothreadsched", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract iothreadsched nodes")); + /* Extract domain name */ + if (!(def->name = virXPathString("string(./name[1])", ctxt))) { + virReportError(VIR_ERR_NO_NAME, NULL); goto error; } - for (i = 0; i < n; i++) { - if (virDomainIOThreadSchedParse(nodes[i], def) < 0) + /* Extract domain uuid. If both uuid and sysinfo/system/entry/uuid + * exist, they must match; and if only the latter exists, it can + * also serve as the uuid. */ + tmp = virXPathString("string(./uuid[1])", ctxt); + if (!tmp) { + if (virUUIDGenerate(def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to generate UUID")); goto error; + } + uuid_generated = true; + } else { + if (virUUIDParse(tmp, def->uuid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed uuid element")); + goto error; + } + VIR_FREE(tmp); } - VIR_FREE(nodes); - if ((n = virXPathNodeSet("./cputune/emulatorsched", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract emulatorsched nodes")); + /* Extract domain genid - a genid can either be provided or generated */ + if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0) goto error; - } - if (n) { - if (n > 1) { + if (n > 0) { + if (n != 1) { virReportError(VIR_ERR_XML_ERROR, "%s", - _("only one emulatorsched is supported")); - VIR_FREE(nodes); + _("element 'genid' can only appear once")); goto error; } - - if (virDomainEmulatorSchedParse(nodes[0], def) < 0) - goto error; + def->genidRequested = true; + if (!(tmp = virXPathString("string(./genid)", ctxt))) { + if (virUUIDGenerate(def->genid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to generate genid")); + goto error; + } + def->genidGenerated = true; + } else { + if (virUUIDParse(tmp, def->genid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed genid element")); + goto error; + } + VIR_FREE(tmp); + } } VIR_FREE(nodes); - if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract cachetune nodes")); + /* Extract short description of domain (title) */ + def->title = virXPathString("string(./title[1])", ctxt); + if (def->title && strchr(def->title, '\n')) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Domain title can't contain newlines")); goto error; } - for (i = 0; i < n; i++) { - if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0) - goto error; - } - VIR_FREE(nodes); - - if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot extract memorytune nodes")); - goto error; - } + /* Extract documentation if present */ + def->description = virXPathString("string(./description[1])", ctxt); - for (i = 0; i < n; i++) { - if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0) + /* analysis of security label, done early even though we format it + * late, so devices can refer to this for defaults */ + if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL)) { + if (virSecurityLabelDefsParseXML(def, ctxt, xmlopt, flags) == -1) goto error; } - VIR_FREE(nodes); - if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0) + /* Extract domain memory */ + if (virDomainParseMemory("./memory[1]", NULL, ctxt, + &def->mem.total_memory, false, true) < 0) goto error; - if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0) + if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt, + &def->mem.cur_balloon, false, true) < 0) goto error; - if (virDomainNumaGetCPUCountTotal(def->numa) > virDomainDefGetVcpusMax(def)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Number of CPUs in exceeds the" - " count")); + if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt, + &def->mem.max_memory, false, false) < 0) goto error; - } - if (virDomainNumaGetMaxCPUID(def->numa) >= virDomainDefGetVcpusMax(def)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("CPU IDs in exceed the count")); + if (virXPathUInt("string(./maxMemory[1]/@slots)", ctxt, &def->mem.memory_slots) == -2) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Failed to parse memory slot count")); goto error; } - if (virDomainNumatuneParseXML(def->numa, - def->placement_mode == - VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC, - ctxt) < 0) - goto error; - - if (virDomainNumatuneHasPlacementAuto(def->numa) && - !def->cpumask && !virDomainDefHasVcpuPin(def) && - !def->cputune.emulatorpin && - !virDomainIOThreadIDArrayHasPin(def)) - def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO; - - if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("cannot extract resource nodes")); + /* and info about it */ + if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) && + (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid memory core dump attribute value '%s'"), tmp); goto error; } + VIR_FREE(tmp); - if (n > 1) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("only one resource element is supported")); - goto error; + tmp = virXPathString("string(./memoryBacking/source/@type)", ctxt); + if (tmp) { + if ((def->mem.source = virDomainMemorySourceTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown memoryBacking/source/type '%s'"), tmp); + goto error; + } + VIR_FREE(tmp); } - if (n && - !(def->resource = virDomainResourceDefParse(nodes[0], ctxt))) - goto error; - VIR_FREE(nodes); - - if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) < 0) - goto error; - - for (i = 0; i < n; i++) { - int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name); - if (val < 0) { + tmp = virXPathString("string(./memoryBacking/access/@mode)", ctxt); + if (tmp) { + if ((def->mem.access = virDomainMemoryAccessTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unexpected feature '%s'"), nodes[i]->name); + _("unknown memoryBacking/access/mode '%s'"), tmp); goto error; } + VIR_FREE(tmp); + } - switch ((virDomainFeature) val) { - case VIR_DOMAIN_FEATURE_APIC: - if ((tmp = virXPathString("string(./features/apic/@eoi)", ctxt))) { - int eoi; - if ((eoi = virTristateSwitchTypeFromString(tmp)) <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown value for attribute eoi: '%s'"), - tmp); - goto error; - } - def->apic_eoi = eoi; - VIR_FREE(tmp); - } - G_GNUC_FALLTHROUGH; - case VIR_DOMAIN_FEATURE_ACPI: - case VIR_DOMAIN_FEATURE_PAE: - case VIR_DOMAIN_FEATURE_VIRIDIAN: - case VIR_DOMAIN_FEATURE_PRIVNET: - case VIR_DOMAIN_FEATURE_HYPERV: - case VIR_DOMAIN_FEATURE_KVM: - case VIR_DOMAIN_FEATURE_MSRS: - case VIR_DOMAIN_FEATURE_XEN: - def->features[val] = VIR_TRISTATE_SWITCH_ON; - break; - - case VIR_DOMAIN_FEATURE_CAPABILITIES: - if ((tmp = virXMLPropString(nodes[i], "policy"))) { - if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown policy attribute '%s' of feature '%s'"), - tmp, virDomainFeatureTypeToString(val)); - goto error; - } - VIR_FREE(tmp); - } else { - def->features[val] = VIR_TRISTATE_SWITCH_ABSENT; - } - break; - - case VIR_DOMAIN_FEATURE_VMCOREINFO: - case VIR_DOMAIN_FEATURE_HAP: - case VIR_DOMAIN_FEATURE_PMU: - case VIR_DOMAIN_FEATURE_PVSPINLOCK: - case VIR_DOMAIN_FEATURE_VMPORT: - case VIR_DOMAIN_FEATURE_SMM: - if ((tmp = virXMLPropString(nodes[i], "state"))) { - if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown state attribute '%s' of feature '%s'"), - tmp, virDomainFeatureTypeToString(val)); - goto error; - } - VIR_FREE(tmp); - } else { - def->features[val] = VIR_TRISTATE_SWITCH_ON; - } - break; - - case VIR_DOMAIN_FEATURE_GIC: - if ((tmp = virXMLPropString(nodes[i], "version"))) { - gic_version = virGICVersionTypeFromString(tmp); - if (gic_version < 0 || gic_version == VIR_GIC_VERSION_NONE) { - virReportError(VIR_ERR_XML_ERROR, - _("malformed gic version: %s"), tmp); - goto error; - } - def->gic_version = gic_version; - VIR_FREE(tmp); - } - def->features[val] = VIR_TRISTATE_SWITCH_ON; - break; - - case VIR_DOMAIN_FEATURE_IOAPIC: - tmp = virXMLPropString(nodes[i], "driver"); - if (tmp) { - int value = virDomainIOAPICTypeFromString(tmp); - if (value < 0 || value == VIR_DOMAIN_IOAPIC_NONE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unknown driver mode: %s"), - tmp); - goto error; - } - def->features[val] = value; - VIR_FREE(tmp); - } - break; - - case VIR_DOMAIN_FEATURE_HPT: - tmp = virXMLPropString(nodes[i], "resizing"); - if (tmp) { - int value = virDomainHPTResizingTypeFromString(tmp); - if (value < 0 || value == VIR_DOMAIN_HPT_RESIZING_NONE) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unknown HPT resizing setting: %s"), - tmp); - goto error; - } - def->hpt_resizing = (virDomainHPTResizing) value; - VIR_FREE(tmp); - } - - if (virDomainParseScaledValue("./features/hpt/maxpagesize", - NULL, - ctxt, - &def->hpt_maxpagesize, - 1024, - ULLONG_MAX, - false) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", - _("Unable to parse HPT maxpagesize setting")); - goto error; - } - def->hpt_maxpagesize = VIR_DIV_UP(def->hpt_maxpagesize, 1024); - - if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE || - def->hpt_maxpagesize > 0) { - def->features[val] = VIR_TRISTATE_SWITCH_ON; - } - break; - - case VIR_DOMAIN_FEATURE_HTM: - case VIR_DOMAIN_FEATURE_NESTED_HV: - case VIR_DOMAIN_FEATURE_CCF_ASSIST: - if (!(tmp = virXMLPropString(nodes[i], "state"))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing state attribute '%s' of feature '%s'"), - tmp, virDomainFeatureTypeToString(val)); - goto error; - } - if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown state attribute '%s' of feature '%s'"), - tmp, virDomainFeatureTypeToString(val)); - goto error; - } - VIR_FREE(tmp); - break; - - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_FEATURE_LAST: - break; + tmp = virXPathString("string(./memoryBacking/allocation/@mode)", ctxt); + if (tmp) { + if ((def->mem.allocation = virDomainMemoryAllocationTypeFromString(tmp)) <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown memoryBacking/allocation/mode '%s'"), tmp); + goto error; } + VIR_FREE(tmp); } - VIR_FREE(nodes); - if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { - int feature; - int value; - node = ctxt->node; - if ((n = virXPathNodeSet("./features/hyperv/*", ctxt, &nodes)) < 0) + if (virXPathNode("./memoryBacking/hugepages", ctxt)) { + /* hugepages will be used */ + if ((n = virXPathNodeSet("./memoryBacking/hugepages/page", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract hugepages nodes")); goto error; + } - for (i = 0; i < n; i++) { - feature = virDomainHypervTypeFromString((const char *)nodes[i]->name); - if (feature < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported HyperV Enlightenment feature: %s"), - nodes[i]->name); + if (n) { + if (VIR_ALLOC_N(def->mem.hugepages, n) < 0) goto error; - } - - ctxt->node = nodes[i]; - if (!(tmp = virXMLPropString(nodes[i], "state"))) { - virReportError(VIR_ERR_XML_ERROR, - _("missing 'state' attribute for " - "HyperV Enlightenment feature '%s'"), - nodes[i]->name); - goto error; + for (i = 0; i < n; i++) { + if (virDomainHugepagesParseXML(nodes[i], ctxt, + &def->mem.hugepages[i]) < 0) + goto error; + def->mem.nhugepages++; } - if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid value of state argument " - "for HyperV Enlightenment feature '%s'"), - nodes[i]->name); + VIR_FREE(nodes); + } else { + /* no hugepage pages */ + if (VIR_ALLOC(def->mem.hugepages) < 0) goto error; - } - - VIR_FREE(tmp); - def->hyperv_features[feature] = value; - - switch ((virDomainHyperv) feature) { - case VIR_DOMAIN_HYPERV_RELAXED: - case VIR_DOMAIN_HYPERV_VAPIC: - case VIR_DOMAIN_HYPERV_VPINDEX: - case VIR_DOMAIN_HYPERV_RUNTIME: - case VIR_DOMAIN_HYPERV_SYNIC: - case VIR_DOMAIN_HYPERV_STIMER: - case VIR_DOMAIN_HYPERV_RESET: - case VIR_DOMAIN_HYPERV_FREQUENCIES: - case VIR_DOMAIN_HYPERV_REENLIGHTENMENT: - case VIR_DOMAIN_HYPERV_TLBFLUSH: - case VIR_DOMAIN_HYPERV_IPI: - case VIR_DOMAIN_HYPERV_EVMCS: - break; - case VIR_DOMAIN_HYPERV_SPINLOCKS: - if (value != VIR_TRISTATE_SWITCH_ON) - break; - - if (virXPathUInt("string(./@retries)", ctxt, - &def->hyperv_spinlocks) < 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("invalid HyperV spinlock retry count")); - goto error; - } + def->mem.nhugepages = 1; + } + } - if (def->hyperv_spinlocks < 0xFFF) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("HyperV spinlock retry count must be " - "at least 4095")); - goto error; - } - break; + if ((node = virXPathNode("./memoryBacking/nosharepages", ctxt))) + def->mem.nosharepages = true; - case VIR_DOMAIN_HYPERV_VENDOR_ID: - if (value != VIR_TRISTATE_SWITCH_ON) - break; + if (virXPathBoolean("boolean(./memoryBacking/locked)", ctxt)) + def->mem.locked = true; - if (!(def->hyperv_vendor_id = virXMLPropString(nodes[i], - "value"))) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("missing 'value' attribute for " - "HyperV feature 'vendor_id'")); - goto error; - } + if (virXPathBoolean("boolean(./memoryBacking/discard)", ctxt)) + def->mem.discard = VIR_TRISTATE_BOOL_YES; - if (strlen(def->hyperv_vendor_id) > VIR_DOMAIN_HYPERV_VENDOR_ID_MAX) { - virReportError(VIR_ERR_XML_ERROR, - _("HyperV vendor_id value must not be more " - "than %d characters."), - VIR_DOMAIN_HYPERV_VENDOR_ID_MAX); - goto error; - } + /* Extract blkio cgroup tunables */ + if (virXPathUInt("string(./blkiotune/weight)", ctxt, + &def->blkio.weight) < 0) + def->blkio.weight = 0; - /* ensure that the string can be passed to qemu */ - if (strchr(def->hyperv_vendor_id, ',')) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("HyperV vendor_id value is invalid")); - goto error; - } + if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract blkiotune nodes")); + goto error; + } + if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0) + goto error; - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_HYPERV_LAST: - break; + for (i = 0; i < n; i++) { + if (virDomainBlkioDeviceParseXML(nodes[i], + &def->blkio.devices[i]) < 0) + goto error; + def->blkio.ndevices++; + for (j = 0; j < i; j++) { + if (STREQ(def->blkio.devices[j].path, + def->blkio.devices[i].path)) { + virReportError(VIR_ERR_XML_ERROR, + _("duplicate blkio device path '%s'"), + def->blkio.devices[i].path); + goto error; } } - VIR_FREE(nodes); - ctxt->node = node; } + VIR_FREE(nodes); - if (def->features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) { - int value; - if ((n = virXPathNodeSet("./features/hyperv/stimer/*", ctxt, &nodes)) < 0) - goto error; + /* Extract other memory tunables */ + if (virDomainParseMemoryLimit("./memtune/hard_limit[1]", NULL, ctxt, + &def->mem.hard_limit) < 0) + goto error; - for (i = 0; i < n; i++) { - if (STRNEQ((const char *)nodes[i]->name, "direct")) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported Hyper-V stimer feature: %s"), - nodes[i]->name); - goto error; - } + if (virDomainParseMemoryLimit("./memtune/soft_limit[1]", NULL, ctxt, + &def->mem.soft_limit) < 0) + goto error; - if (!(tmp = virXMLPropString(nodes[i], "state"))) { - virReportError(VIR_ERR_XML_ERROR, - _("missing 'state' attribute for " - "Hyper-V stimer '%s' feature"), "direct"); - goto error; - } + if (virDomainParseMemory("./memtune/min_guarantee[1]", NULL, ctxt, + &def->mem.min_guarantee, false, false) < 0) + goto error; - if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid value of state argument " - "for Hyper-V stimer '%s' feature"), "direct"); - goto error; - } + if (virDomainParseMemoryLimit("./memtune/swap_hard_limit[1]", NULL, ctxt, + &def->mem.swap_hard_limit) < 0) + goto error; - VIR_FREE(tmp); - def->hyperv_stimer_direct = value; - } - VIR_FREE(nodes); + if (virDomainVcpuParse(def, ctxt, xmlopt) < 0) + goto error; + + if (virDomainDefParseIOThreads(def, ctxt) < 0) + goto error; + + /* Extract cpu tunables. */ + if ((n = virXPathULongLong("string(./cputune/shares[1])", ctxt, + &def->cputune.shares)) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune shares value")); + goto error; + } else if (n == 0) { + def->cputune.sharesSpecified = true; } - if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { - int feature; - int value; - if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) < 0) - goto error; + if (virXPathULongLong("string(./cputune/period[1])", ctxt, + &def->cputune.period) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune period value")); + goto error; + } - for (i = 0; i < n; i++) { - feature = virDomainKVMTypeFromString((const char *)nodes[i]->name); - if (feature < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported KVM feature: %s"), - nodes[i]->name); - goto error; - } + if (virXPathLongLong("string(./cputune/quota[1])", ctxt, + &def->cputune.quota) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune quota value")); + goto error; + } - switch ((virDomainKVM) feature) { - case VIR_DOMAIN_KVM_HIDDEN: - case VIR_DOMAIN_KVM_DEDICATED: - if (!(tmp = virXMLPropString(nodes[i], "state"))) { - virReportError(VIR_ERR_XML_ERROR, - _("missing 'state' attribute for " - "KVM feature '%s'"), - nodes[i]->name); - goto error; - } + if (virXPathULongLong("string(./cputune/global_period[1])", ctxt, + &def->cputune.global_period) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune global period value")); + goto error; + } + + if (virXPathLongLong("string(./cputune/global_quota[1])", ctxt, + &def->cputune.global_quota) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune global quota value")); + goto error; + } + + if (virXPathULongLong("string(./cputune/emulator_period[1])", ctxt, + &def->cputune.emulator_period) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune emulator period value")); + goto error; + } + + if (virXPathLongLong("string(./cputune/emulator_quota[1])", ctxt, + &def->cputune.emulator_quota) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune emulator quota value")); + goto error; + } - if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid value of state argument " - "for KVM feature '%s'"), - nodes[i]->name); - goto error; - } - VIR_FREE(tmp); - def->kvm_features[feature] = value; - break; + if (virXPathULongLong("string(./cputune/iothread_period[1])", ctxt, + &def->cputune.iothread_period) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune iothread period value")); + goto error; + } - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_KVM_LAST: - break; - } - } - VIR_FREE(nodes); + if (virXPathLongLong("string(./cputune/iothread_quota[1])", ctxt, + &def->cputune.iothread_quota) < -1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("can't parse cputune iothread quota value")); + goto error; } - if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) { - int feature; - int value; - g_autofree char *ptval = NULL; + if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0) + goto error; - if ((n = virXPathNodeSet("./features/xen/*", ctxt, &nodes)) < 0) + for (i = 0; i < n; i++) { + if (virDomainVcpuPinDefParseXML(def, nodes[i])) goto error; + } + VIR_FREE(nodes); - for (i = 0; i < n; i++) { - feature = virDomainXenTypeFromString((const char *)nodes[i]->name); - if (feature < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported Xen feature: %s"), - nodes[i]->name); - goto error; - } - - if (!(tmp = virXMLPropString(nodes[i], "state"))) { - virReportError(VIR_ERR_XML_ERROR, - _("missing 'state' attribute for " - "Xen feature '%s'"), - nodes[i]->name); - goto error; - } + if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract emulatorpin nodes")); + goto error; + } - if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("invalid value of state argument " - "for Xen feature '%s'"), - nodes[i]->name); - goto error; - } + if (n) { + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only one emulatorpin is supported")); + VIR_FREE(nodes); + goto error; + } - VIR_FREE(tmp); - def->xen_features[feature] = value; + if (!(def->cputune.emulatorpin = virDomainEmulatorPinDefParseXML(nodes[0]))) + goto error; + } + VIR_FREE(nodes); - switch ((virDomainXen) feature) { - case VIR_DOMAIN_XEN_E820_HOST: - break; - case VIR_DOMAIN_XEN_PASSTHROUGH: - if (value != VIR_TRISTATE_SWITCH_ON) - break; + if ((n = virXPathNodeSet("./cputune/iothreadpin", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract iothreadpin nodes")); + goto error; + } - if ((ptval = virXMLPropString(nodes[i], "mode"))) { - int mode = virDomainXenPassthroughModeTypeFromString(ptval); + for (i = 0; i < n; i++) { + if (virDomainIOThreadPinDefParseXML(nodes[i], def) < 0) + goto error; + } + VIR_FREE(nodes); - if (mode < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported mode '%s' for Xen passthrough feature"), - ptval); - goto error; - } + if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract vcpusched nodes")); + goto error; + } - if (mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SYNC_PT && - mode != VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SHARE_PT) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("'mode' attribute for Xen feature " - "'passthrough' must be 'sync_pt' or 'share_pt'")); - goto error; - } - def->xen_passthrough_mode = mode; - } - break; + for (i = 0; i < n; i++) { + if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0) + goto error; + } + VIR_FREE(nodes); - /* coverity[dead_error_begin] */ - case VIR_DOMAIN_XEN_LAST: - break; - } - } - VIR_FREE(nodes); + if ((n = virXPathNodeSet("./cputune/iothreadsched", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract iothreadsched nodes")); + goto error; } - if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) { - int rv = virDomainParseScaledValue("string(./features/smm/tseg)", - "string(./features/smm/tseg/@unit)", - ctxt, - &def->tseg_size, - 1024 * 1024, /* Defaults to mebibytes */ - ULLONG_MAX, - false); - if (rv < 0) + for (i = 0; i < n; i++) { + if (virDomainIOThreadSchedParse(nodes[i], def) < 0) goto error; - def->tseg_specified = rv; } + VIR_FREE(nodes); - if (def->features[VIR_DOMAIN_FEATURE_MSRS] == VIR_TRISTATE_SWITCH_ON) { - if ((node = virXPathNode("./features/msrs", ctxt)) == NULL) - goto error; + if ((n = virXPathNodeSet("./cputune/emulatorsched", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract emulatorsched nodes")); + goto error; + } - if (!(tmp = virXMLPropString(node, "unknown"))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing 'unknown' attribute for feature '%s'"), - virDomainFeatureTypeToString(VIR_DOMAIN_FEATURE_MSRS)); + if (n) { + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only one emulatorsched is supported")); + VIR_FREE(nodes); goto error; } - if ((def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN] = virDomainMsrsUnknownTypeFromString(tmp)) < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown 'unknown' value '%s'"), - tmp); + if (virDomainEmulatorSchedParse(nodes[0], def) < 0) goto error; - } - VIR_FREE(tmp); } + VIR_FREE(nodes); - if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes)) < 0) + if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract cachetune nodes")); goto error; + } for (i = 0; i < n; i++) { - int val = virDomainProcessCapsFeatureTypeFromString((const char *)nodes[i]->name); - if (val < 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unexpected capability feature '%s'"), nodes[i]->name); + if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0) goto error; - } + } + VIR_FREE(nodes); - if ((tmp = virXMLPropString(nodes[i], "state"))) { - if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown state attribute '%s' of feature capability '%s'"), - tmp, virDomainProcessCapsFeatureTypeToString(val)); - goto error; - } - VIR_FREE(tmp); - } else { - def->caps_features[val] = VIR_TRISTATE_SWITCH_ON; - } + if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract memorytune nodes")); + goto error; + } + + for (i = 0; i < n; i++) { + if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0) + goto error; + } + VIR_FREE(nodes); + + if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0) + goto error; + + if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0) + goto error; + + if (virDomainNumaGetCPUCountTotal(def->numa) > virDomainDefGetVcpusMax(def)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Number of CPUs in exceeds the" + " count")); + goto error; + } + + if (virDomainNumaGetMaxCPUID(def->numa) >= virDomainDefGetVcpusMax(def)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("CPU IDs in exceed the count")); + goto error; + } + + if (virDomainNumatuneParseXML(def->numa, + def->placement_mode == + VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC, + ctxt) < 0) + goto error; + + if (virDomainNumatuneHasPlacementAuto(def->numa) && + !def->cpumask && !virDomainDefHasVcpuPin(def) && + !def->cputune.emulatorpin && + !virDomainIOThreadIDArrayHasPin(def)) + def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO; + + if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract resource nodes")); + goto error; + } + + if (n > 1) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("only one resource element is supported")); + goto error; } + + if (n && + !(def->resource = virDomainResourceDefParse(nodes[0], ctxt))) + goto error; VIR_FREE(nodes); + if (virDomainFeaturesDefParse(def, ctxt) < 0) + goto error; + if (virDomainEventActionParseXML(ctxt, "on_reboot", "string(./on_reboot[1])", &def->onReboot, -- GitLab