resmongroup element is used for feature of resctrl monitoring group, and keeps the information for how resctrl monitoring groups is arranged. --- docs/formatdomain.html.in | 17 +++ docs/schemas/domaincommon.rng | 14 ++ src/conf/domain_conf.c | 318 ++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 25 ++++ src/libvirt_private.syms | 3 + 5 files changed, 377 insertions(+) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index a3afe13..cfb10c4 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -757,6 +757,8 @@ <cache id='0' level='3' type='both' size='3' unit='MiB'/> <cache id='1' level='3' type='both' size='3' unit='MiB'/> </cachetune> + <resmongroup vcpus="0-3"/> + <resmongroup vcpus="4"/> </cputune> ... </domain> @@ -952,6 +954,21 @@ </dl> </dd> + <dt><code>resmongroup</code><span class="since">Since 4.6.0</span></dt> + <dd> + Optional <code>resmongroup</code> element can be used to create resctrl + monitoring group for purpose of reporting cache occupancy informatoin. + The attribute <code>vcpus</code> specifies vCPUs this monitoring group + applies and which is impacted by the <code>cachetune</code> + <code>vcpus</code> attribute. A <code>resmongroup</code> + <code>vcpus</code> is valid if it has the same setting with that of + <code>cachetune</code> element, but any other kind of overlap between + <code>vcpus</code> of <code>rdtmongroup</code> and + <code>cachetune</code> <code>vcpus</code> is not permitted. Further, + any other kind of vCPUs overlap between monitoring groups is allowed. + Optional attribute <code>id</code> specifies the group name. + + </dd> </dl> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index bd687ce..a8057b1 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -952,6 +952,20 @@ </element> </zeroOrMore> <zeroOrMore> + <element name="resmongroup"> + <attribute name="vcpus"> + <ref name='cpuset'/> + </attribute> + <optional> + <attribute name="id"> + <data type="string"> + <param name='pattern'>[a-zA-Z0-9,-_]+</param> + </data> + </attribute> + </optional> + </element> + </zeroOrMore> + <zeroOrMore> <element name="cachetune"> <attribute name="vcpus"> <ref name='cpuset'/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f4e59f6..0cdad79 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2975,6 +2975,18 @@ virDomainSEVDefFree(virDomainSEVDefPtr def) } +static void +virDomainCpuResmonDefFree(virDomainCpuResmonDefPtr resmon) +{ + if (!resmon) + return; + + virObjectUnref(resmon->mon); + virBitmapFree(resmon->vcpus); + VIR_FREE(resmon); +} + + void virDomainDefFree(virDomainDefPtr def) { size_t i; @@ -3152,6 +3164,10 @@ void virDomainDefFree(virDomainDefPtr def) virDomainCachetuneDefFree(def->cachetunes[i]); VIR_FREE(def->cachetunes); + for (i = 0; i < def->nresmons; i++) + virDomainCpuResmonDefFree(def->resmons[i]); + VIR_FREE(def->resmons); + VIR_FREE(def->keywrap); if (def->namespaceData && def->ns.free) @@ -19055,6 +19071,264 @@ virDomainCachetuneDefParse(virDomainDefPtr def, } +bool +virDomainCpuResmonDefValidate(virDomainDefPtr def, + const char *id, + virBitmapPtr vcpus, + virResctrlAllocPtr *alloc) +{ + ssize_t i = -1; + + /* vcpu should exist in current domain */ + while ((i = virBitmapNextSetBit(vcpus, i)) > -1) { + if (!virDomainDefGetVcpu(def, i)) + return false; + } + + if (alloc) + *alloc = NULL; + + /* if 'vcpus' equals to vcpus of any existing allocation group, means, mon + * group is sharing same resctrl resource group with allocation group, this + * is a legal case. Otherwise, no vcpu overlap is allowed between mon group + * and any aollocation group. + * if a mon group is sharing the same resource group with one allocation + * group, we hope the mon group and the allocation group have a same 'id', + * and the 'alloc' pointer points to the allocation group. */ + for (i = 0; i < def->ncachetunes; i++) { + if (virBitmapOverlaps(vcpus, def->cachetunes[i]->vcpus)) { + if (virBitmapEqual(vcpus, def->cachetunes[i]->vcpus)) { + const char *allocid = + virResctrlAllocGetID(def->cachetunes[i]->alloc); + if (!allocid || (id && STRNEQ(id, allocid))) + return false; + + if (alloc) + *alloc = def->cachetunes[i]->alloc; + return true; + } + + return false; + } + } + + /* if vcpus equals to vcpus of existing mon group vcpus, + * a mon group already created, return True. + * for new mon group no overlap for vcpus */ + for (i = 0; i < def->nresmons; i++) { + if (virBitmapEqual(vcpus, def->resmons[i]->vcpus) && + (!id || + STREQ(id, virResctrlMonGetID(def->resmons[i]->mon)))) + return true; + + if (virBitmapOverlaps(vcpus, def->resmons[i]->vcpus)) + return false; + } + + return true; +} + + +virDomainCpuResmonDefPtr +virDomainCpuResmonDefAdd(virDomainDefPtr def, + virBitmapPtr vcpuslist, + const char *monid) +{ + virDomainCpuResmonDefPtr resmon = NULL; + virResctrlMonPtr mon = NULL; + char *id = NULL; + char *vcpus_str = NULL; + size_t i = -1; + virDomainCpuResmonDefPtr ret = NULL; + virBitmapPtr vcpus = virBitmapNewCopy(vcpuslist); + + if (VIR_STRDUP(id, monid) < 0) + goto cleanup; + + for (i = 0; i < def->nresmons; i++) { + if (virBitmapEqual(vcpus, def->resmons[i]->vcpus)) { + if (!id || + STREQ(id, virResctrlMonGetID(def->resmons[i]->mon))) { + ret = def->resmons[i]; + goto cleanup; + } + virReportError(VIR_ERR_INVALID_ARG, + "%s", _("resource monitoring group id mismatch")); + goto cleanup; + } + } + + /* resouce group created by cachtune also has a mon group, if matched + * copying the group id and no sub-directory under resctrl fs will be + * created */ + for (i = 0; i < def->ncachetunes; i++) { + if (virBitmapEqual(vcpus, def->cachetunes[i]->vcpus)) { + const char *allocid + = virResctrlAllocGetID(def->cachetunes[i]->alloc); + /* for mon group matched in cachetunes list should never + * be disabled because we cannot disable an allocation + * group in runtime */ + if (!id) { + if (VIR_STRDUP(id, allocid) < 0) + goto cleanup; + + } + break; + } + } + + if (!id) { + vcpus_str = virBitmapFormat(vcpus); + if (virAsprintf(&id, "vcpus_%s", vcpus_str) < 0) + goto cleanup; + } + + if (VIR_ALLOC(resmon) < 0) + goto cleanup; + + mon = virResctrlMonNew(); + if (!mon) + goto cleanup; + + if (virResctrlMonSetID(mon, id) < 0) + goto cleanup; + + VIR_STEAL_PTR(resmon->vcpus, vcpus); + VIR_STEAL_PTR(resmon->mon, mon); + + if (VIR_APPEND_ELEMENT(def->resmons, def->nresmons, resmon) < 0) + goto cleanup; + + ret = def->resmons[def->nresmons - 1]; + cleanup: + virBitmapFree(vcpus); + VIR_FREE(id); + virDomainCpuResmonDefFree(resmon); + virObjectUnref(mon); + return ret; +} + + +virResctrlMonPtr +virDomainCpuResmonDefRemove(virDomainDefPtr def, + const char *monid) +{ + virDomainCpuResmonDefPtr resmon = NULL; + virResctrlMonPtr mon = NULL; + size_t i = -1; + + if (!monid) { + virReportError(VIR_ERR_INVALID_ARG, + _("Cannot remove resource monitoring group: " + "group name is NULL")); + goto error; + } + + for (i = 0; i < def->nresmons; i++) { + const char *id = virResctrlMonGetID(def->resmons[i]->mon); + if (!id) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot remove resource monitoring group: " + "error in get monitoring group name")); + goto error; + } + + if (STREQ(monid, id)) + break; + } + + if (i == def->nresmons) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot remove resource monitoring group: " + "no monitoring group '%s' found"), + monid); + goto error; + } + + resmon = def->resmons[i]; + VIR_DELETE_ELEMENT(def->resmons, i, def->nresmons); + + mon = resmon->mon; + virBitmapFree(resmon->vcpus); + VIR_FREE(resmon); + error: + return mon; +} + + +static int +virDomainCpuResmonDefParse(virDomainDefPtr def, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + xmlNodePtr oldnode = ctxt->node; + xmlNodePtr *nodes = NULL; + virBitmapPtr vcpus = NULL; + char *vcpus_str = NULL; + char *monid = NULL; + size_t i = 0; + int n = 0; + int ret = -1; + + if ((n = virXPathNodeSet("./cputune/resmongroup", ctxt, &nodes)) < 0) + goto cleanup; + + for (i = 0; i < n; i++) { + + if (!(vcpus_str = virXMLPropString(nodes[i], "vcpus"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Missing resmongroup attribute 'vcpus'")); + goto cleanup; + } + + if (virBitmapParse(vcpus_str, &vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("Invalid resmongroup attribute 'vcpus' value '%s'"), + vcpus_str); + goto cleanup; + } + + virBitmapShrink(vcpus, def->maxvcpus); + + if (virBitmapIsAllClear(vcpus)) { + ret = 0; + goto cleanup; + } + + if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) + monid = virXMLPropString(nodes[i], "id"); + + if (!virDomainCpuResmonDefValidate(def, monid, vcpus, NULL)) { + virReportError(VIR_ERR_INVALID_ARG, + "%s", _("vcpus or group name conflicts with domain " + "settings")); + goto cleanup; + } + + if (!virDomainCpuResmonDefAdd(def, vcpus, monid)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Error in add resource monitoring group settings " + "to configuration file")); + goto cleanup; + } + + virBitmapFree(vcpus); + vcpus = NULL; + VIR_FREE(monid); + monid = NULL; + } + + ret = 0; + cleanup: + ctxt->node = oldnode; + virBitmapFree(vcpus); + VIR_FREE(monid); + VIR_FREE(vcpus_str); + VIR_FREE(nodes); + return ret; +} + + static virDomainDefPtr virDomainDefParseXML(xmlDocPtr xml, xmlNodePtr root, @@ -19648,8 +19922,15 @@ virDomainDefParseXML(xmlDocPtr xml, if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0) goto error; } + VIR_FREE(nodes); + if (virDomainCpuResmonDefParse(def, ctxt, flags) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot extract CPU resource monitoring group setting")); + goto error; + } + if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0) goto error; @@ -26943,6 +27224,41 @@ virDomainCachetuneDefFormat(virBufferPtr buf, static int +virDomainCpuResmonDefFormat(virBufferPtr buf, + virDomainDefPtr def, + unsigned int flags) +{ + char *vcpus = NULL; + size_t i = 0; + int ret = -1; + + for (i = 0; i < def->nresmons; i++) { + vcpus = virBitmapFormat(def->resmons[i]->vcpus); + if (!vcpus) + goto cleanup; + + virBufferAsprintf(buf, "<resmongroup vcpus='%s'", vcpus); + + if (!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) { + const char *mon_id = virResctrlMonGetID(def->resmons[i]->mon); + if (!mon_id) + goto cleanup; + + virBufferAsprintf(buf, " id='%s'", mon_id); + } + virBufferAddLit(buf, "/>\n"); + + VIR_FREE(vcpus); + } + + ret = 0; + cleanup: + VIR_FREE(vcpus); + return ret; +} + + +static int virDomainCputuneDefFormat(virBufferPtr buf, virDomainDefPtr def, unsigned int flags) @@ -27047,6 +27363,8 @@ virDomainCputuneDefFormat(virBufferPtr buf, for (i = 0; i < def->ncachetunes; i++) virDomainCachetuneDefFormat(&childrenBuf, def->cachetunes[i], flags); + virDomainCpuResmonDefFormat(&childrenBuf, def, flags); + if (virBufferCheckError(&childrenBuf) < 0) return -1; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 41d2748..7d31254 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2237,6 +2237,13 @@ struct _virDomainCachetuneDef { virResctrlAllocPtr alloc; }; +typedef struct _virDomainCpuResmonDef virDomainCpuResmonDef; +typedef virDomainCpuResmonDef *virDomainCpuResmonDefPtr; + +struct _virDomainCpuResmonDef { + virBitmapPtr vcpus; + virResctrlMonPtr mon; +}; typedef struct _virDomainVcpuDef virDomainVcpuDef; typedef virDomainVcpuDef *virDomainVcpuDefPtr; @@ -2413,6 +2420,9 @@ struct _virDomainDef { virDomainCachetuneDefPtr *cachetunes; size_t ncachetunes; + virDomainCpuResmonDefPtr *resmons; + size_t nresmons; + virDomainNumaPtr numa; virDomainResourceDefPtr resource; virDomainIdMapDef idmap; @@ -3640,4 +3650,19 @@ virDomainDiskGetDetectZeroesMode(virDomainDiskDiscard discard, bool virDomainDefHasManagedPR(const virDomainDef *def); +bool +virDomainCpuResmonDefValidate(virDomainDefPtr def, + const char *id, + virBitmapPtr vcpus, + virResctrlAllocPtr *pairedalloc); + +virDomainCpuResmonDefPtr +virDomainCpuResmonDefAdd(virDomainDefPtr def, + virBitmapPtr vcpus, + const char *monid); + +virResctrlMonPtr +virDomainCpuResmonDefRemove(virDomainDefPtr def, + const char *monid); + #endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b10a3a5..9c2b2f0 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -243,6 +243,9 @@ virDomainControllerRemove; virDomainControllerTypeToString; virDomainCpuPlacementModeTypeFromString; virDomainCpuPlacementModeTypeToString; +virDomainCpuResmonDefAdd; +virDomainCpuResmonDefRemove; +virDomainCpuResmonDefValidate; virDomainDefAddController; virDomainDefAddImplicitDevices; virDomainDefAddUSBController; -- 2.7.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list