On 10/9/18 6:30 AM, Wang Huaqiang wrote:
Introducing <monitor> element under <cachetune> to
represent
a cache monitor.
Supports two kind of monitors, which are, monitor under default
allocation or monitor under particular allocation.
I still don't see what the big difference is with singling out default.
Does it really matter - what advantage is there. Having a monitor for
'n' vcpus is a choice.
Monitor supervises the cache or memory bandwidth usage for
At this point memory bandwidth is not in play...
interested vcpu thread set, if the vcpu thread set is belong to some
resctrl allocation, then the monitor will be created under this
allocation, that is, creating a resctrl monitoring group directory under
the directory of '@alloc->path/mon_group'. Otherwise, the monitor
will be created under default allocation.
This is becoming increasing difficult to describe/decipher - makes me
wonder who would really use it.
For default allocation monitor, it will have such kind of XML layout:
<cachetune vcpus='1'>
<monitor level=3 vcpus='1'/>
</cachetune>
For other type monitor, the XML layout will be something like:
<cachetune vcpus='2-4'>
<cache id='0' level='3' type='both' size='3'
unit='MiB'/>
<cache id='1' level='3' type='both' size='3'
unit='MiB'/>
<monitor level=3 vcpus='2'/>
</cachetune>
Since all we get is the "cache_occupancy" that would seem to me to be
most important when there is a <cache> bank, right? So what would the
first (or your default style) collect/display. Or does cache not really
matter.
IOW: What is cache_occupancy measuring? Each cache? The entire thing? If
there's no cache elements, then what?
I honestly think this just needs to be simplified as much as possible.
When you monitor specific vcpus within a cachetune, then you get what?
If the cachetune has no specific cache entries, you get what? If you
monitor multiple vcpus within a cachetune then you get what? (?An
aggregation of all?). This whole default and specific description
doesn't make sense.
Signed-off-by: Wang Huaqiang <huaqiang.wang(a)intel.com>
---
docs/formatdomain.html.in | 26 +++
docs/schemas/domaincommon.rng | 10 +
src/conf/domain_conf.c | 217 ++++++++++++++++++++-
src/conf/domain_conf.h | 11 ++
tests/genericxml2xmlindata/cachetune-cdp.xml | 3 +
.../cachetune-colliding-monitor.xml | 30 +++
tests/genericxml2xmlindata/cachetune-small.xml | 7 +
tests/genericxml2xmltest.c | 2 +
8 files changed, 301 insertions(+), 5 deletions(-)
create mode 100644 tests/genericxml2xmlindata/cachetune-colliding-monitor.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index b1651e3..2fd665c 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -759,6 +759,12 @@
<cachetune vcpus='0-3'>
<cache id='0' level='3' type='both'
size='3' unit='MiB'/>
<cache id='1' level='3' type='both'
size='3' unit='MiB'/>
+ <monitor level='3' vcpus='1'/>
+ <monitor level='3' vcpus='0-3'/>
+ </cachetune>
+ <cachetune vcpus='4-5'>
+ <monitor level='3' vcpus='4'/>
+ <monitor level='3' vcpus='5'/>
</cachetune>
<memorytune vcpus='0-3'>
<node id='0' bandwidth='60'/>
@@ -978,6 +984,26 @@
</dd>
</dl>
</dd>
+ <dt><code>monitor</code></dt>
+ <dd>
+ The optional element <code>monitor</code> creates the cache
+ monitor(s) for current cache allocation and has the following
+ required attributes:
+ <dl>
+ <dt><code>level</code></dt>
+ <dd>
+ Host cache level the monitor belongs to.
+ </dd>
+ <dt><code>vcpus</code></dt>
+ <dd>
+ vCPU list the monitor applies to. A monitor's vCPU list
+ can only be the member(s) of the vCPU list of associating
+ allocation. The default monitor has the same vCPU list as the
+ associating allocation. For non-default monitors, there
+ are no vCPU overlap permitted.
+ </dd>
+ </dl>
+ </dd>
</dl>
</dd>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 5c533d6..7ce49d3 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -981,6 +981,16 @@
</optional>
</element>
</zeroOrMore>
+ <zeroOrMore>
+ <element name="monitor">
+ <attribute name="level">
+ <ref name='unsignedInt'/>
+ </attribute>
+ <attribute name="vcpus">
+ <ref name='cpuset'/>
+ </attribute>
+ </element>
+ </zeroOrMore>
</element>
</zeroOrMore>
<zeroOrMore>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9a514a6..4f4604f 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2955,13 +2955,30 @@ virDomainLoaderDefFree(virDomainLoaderDefPtr loader)
static void
+virDomainResctrlMonDefFree(virDomainResctrlMonDefPtr domresmon)
+{
+ if (!domresmon)
+ return;
+
+ virBitmapFree(domresmon->vcpus);
+ virObjectUnref(domresmon->instance);
VIR_FREE(domresmon);
+}
+
+
+static void
virDomainResctrlDefFree(virDomainResctrlDefPtr resctrl)
{
+ size_t i = 0;
+
if (!resctrl)
return;
+ for (i = 0; i < resctrl->nmonitors; i++)
+ virDomainResctrlMonDefFree(resctrl->monitors[i]);
+
virObjectUnref(resctrl->alloc);
virBitmapFree(resctrl->vcpus);
+ VIR_FREE(resctrl->monitors);
VIR_FREE(resctrl);
}
@@ -18919,6 +18936,154 @@ virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
return ret;
}
Two blank lines
+/* Checking if the monitor's vcpus is conflicted with existing
allocation
+ * and monitors.
+ *
+ * Returns 1 if @vcpus equals to @resctrl->vcpus, means it is a default
+ * monitor. Returns - 1 if a conflict found. Returns 0 if no conflict and
+ * @vcpus is not equal to @resctrl->vcpus.
+ * */
+static int
+virDomainResctrlMonValidateVcpu(virDomainResctrlDefPtr resctrl,
+ virBitmapPtr vcpus)
This should be *ValidateVcpus.
+{
+ size_t i = 0;
+ int vcpu = -1;
+
+ if (virBitmapIsAllClear(vcpus)) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("vcpus is empty"));
+ return -1;
+ }
+
+ while ((vcpu = virBitmapNextSetBit(vcpus, vcpu)) >= 0) {
+ if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Monitor vcpus conflicts with allocation"));
+ return -1;
+ }
+ }
+
+ if (resctrl->alloc && virBitmapEqual(vcpus, resctrl->vcpus))
The ->alloc check is confusing in light of having a monitor as a child
of cachetune.
+ return 1;
+
+ for (i = 0; i < resctrl->nmonitors; i++) {
+ if (virBitmapEqual(resctrl->vcpus, resctrl->monitors[i]->vcpus))
+ continue;
+
+ if (virBitmapOverlaps(vcpus, resctrl->monitors[i]->vcpus)) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Monitor vcpus conflicts with monitors"));
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+virDomainResctrlMonDefParse(virDomainDefPtr def,
+ xmlXPathContextPtr ctxt,
+ xmlNodePtr node,
+ virResctrlMonitorType tag,
+ virDomainResctrlDefPtr resctrl)
+{
+ virDomainResctrlMonDefPtr domresmon = NULL;
+ xmlNodePtr oldnode = ctxt->node;
+ xmlNodePtr *nodes = NULL;
+ unsigned int level = 0;
+ char * tmp = NULL;
+ char * id = NULL;
+ size_t i = 0;
+ int n = 0;
+ int rv = -1;
+ int ret = -1;
+
+ ctxt->node = node;
+
+ if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot extract monitor nodes"));
+ goto cleanup;
+ }
+
+ for (i = 0; i < n; i++) {
+
+ if (VIR_ALLOC(domresmon) < 0)
+ goto cleanup;
+
+ domresmon->tag = tag;
+
+ domresmon->instance = virResctrlMonitorNew();
+ if (!domresmon->instance) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Could not create monitor"));
+ goto cleanup;
+ }
+
+ 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;
+ }
+
+ if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("Invalid monitor attribute 'level' value
'%s'"),
+ tmp);
+ goto cleanup;
+ }
+
+ if (virResctrlMonitorSetCacheLevel(domresmon->instance, level) < 0)
+ goto cleanup;
+
+ VIR_FREE(tmp);
+ }
+
+ if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0)
+ goto cleanup;
+
+ rv = virDomainResctrlMonValidateVcpu(resctrl, domresmon->vcpus);
+
+ /* If monitor's vcpu list is identical to allocation's vcpu list,
+ * set as default monitor */
+ if (rv == 1 && resctrl->alloc)
I'm still not seeing the need for default... FWIW: The resctrl->alloc
check is unnecessary since the only way rv == 1 is if it's there.
+ virResctrlMonitorSetDefault(domresmon->instance);
+ else if (rv < 0)
+ goto cleanup;
+
+ if (!(tmp = virBitmapFormat(domresmon->vcpus)))
+ goto cleanup;
+
+ if (virAsprintf(&id, "vcpus_%s", tmp) < 0)
+ goto cleanup;
+
+ if (virResctrlMonitorSetID(domresmon->instance, id) < 0)
+ goto cleanup;
+
+ if (VIR_APPEND_ELEMENT(resctrl->monitors,
+ resctrl->nmonitors,
+ domresmon) < 0)
+ goto cleanup;
+
+ VIR_FREE(id);
+ VIR_FREE(tmp);
+ domresmon = NULL;
+ }
+
+ ret = 0;
+ cleanup:
+ ctxt->node = oldnode;
+ VIR_FREE(id);
+ VIR_FREE(tmp);
+ virDomainResctrlMonDefFree(domresmon);
VIR_FREE(nodes);
+ return ret;
+}
+
static virDomainResctrlDefPtr
virDomainResctrlNew(virResctrlAllocPtr alloc,
@@ -19041,15 +19206,20 @@ virDomainCachetuneDefParse(virDomainDefPtr def,
}
}
- if (virResctrlAllocIsEmpty(alloc)) {
- ret = 0;
- goto cleanup;
- }
-
resctrl = virDomainResctrlNew(alloc, vcpus);
So if @alloc == NULL (which is one of the reasons AllocIsEmpty is true)
means that we'll be virObjectRef(NULL) in ResctrlNew(). In this case
@alloc could be NULL if there were no <cache> entries, IIUC.
I'm starting to see a real downside to a resctrl->alloc == NULL. I
really don't want to continue to see churn on the internal hierarchy
though.
However, if I look at how it could be filled in within the context of
this function, the virDomainResctrlVcpuMatch call and subsequent
possible allocation of @alloc would seemingly be possible outside the
context of whether specific <cache> entries existed. Probably could just
do away with the term default allocation - all it seems to be is an
allocation without <cache> elements, but it can have <monitor> elements.
If someone places a <cachetune> without <cache> and without <monitor>,
so what - who cares. Probably doesn't do much other than limit other
<cachetune> (and perhaps <memorytune>) elements.
if (!resctrl)
goto cleanup;
+ if (virDomainResctrlMonDefParse(def, ctxt, node,
+ VIR_RESCTRL_MONITOR_TYPE_CACHE,
+ resctrl) < 0)
+ goto cleanup;
+
+ if (virResctrlAllocIsEmpty(alloc) && !resctrl->nmonitors) {
+ ret = 0;
+ goto cleanup;
+ }
+
Moving the AllocIsEmpty check should be separate.
I'm losing steam, but the next couple of patches had Coverity issues, so
I figured I'll note that before going back to read all the comments
you've posted today while I was reviewing without trying to go back.
John
if (virDomainResctrlAppend(def, node, resctrl, flags) < 0)
goto cleanup;
@@ -27085,12 +27255,42 @@ virDomainCachetuneDefFormatHelper(unsigned int level,
static int
+virDomainResctrlMonDefFormatHelper(virDomainResctrlMonDefPtr domresmon,
+ virResctrlMonitorType tag,
+ virBufferPtr buf)
+{
+ char *vcpus = NULL;
+ unsigned int level = 0;
+
+ if (domresmon->tag != tag)
+ return 0;
+
+ virBufferAddLit(buf, "<monitor ");
+
+ if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
+ level = virResctrlMonitorGetCacheLevel(domresmon->instance);
+ virBufferAsprintf(buf, "level='%u' ", level);
+ }
+
+ vcpus = virBitmapFormat(domresmon->vcpus);
+ if (!vcpus)
+ return -1;
+
+ virBufferAsprintf(buf, "vcpus='%s'/>\n", vcpus);
+
+ VIR_FREE(vcpus);
+ return 0;
+}
+
+
+static int
virDomainCachetuneDefFormat(virBufferPtr buf,
virDomainResctrlDefPtr resctrl,
unsigned int flags)
{
virBuffer childrenBuf = VIR_BUFFER_INITIALIZER;
char *vcpus = NULL;
+ size_t i = 0;
int ret = -1;
virBufferSetChildIndent(&childrenBuf, buf);
@@ -27099,6 +27299,13 @@ virDomainCachetuneDefFormat(virBufferPtr buf,
&childrenBuf) < 0)
goto cleanup;
+ for (i = 0; i < resctrl->nmonitors; i ++) {
+ if (virDomainResctrlMonDefFormatHelper(resctrl->monitors[i],
+ VIR_RESCTRL_MONITOR_TYPE_CACHE,
+ &childrenBuf) < 0)
+ goto cleanup;
+ }
+
if (virBufferCheckError(&childrenBuf) < 0)
goto cleanup;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index e30a4b2..60f6464 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2236,12 +2236,23 @@ struct _virDomainCputune {
};
+typedef struct _virDomainResctrlMonDef virDomainResctrlMonDef;
+typedef virDomainResctrlMonDef *virDomainResctrlMonDefPtr;
+struct _virDomainResctrlMonDef {
+ virBitmapPtr vcpus;
+ virResctrlMonitorType tag;
+ virResctrlMonitorPtr instance;
+};
+
typedef struct _virDomainResctrlDef virDomainResctrlDef;
typedef virDomainResctrlDef *virDomainResctrlDefPtr;
struct _virDomainResctrlDef {
virBitmapPtr vcpus;
virResctrlAllocPtr alloc;
+
+ virDomainResctrlMonDefPtr *monitors;
+ size_t nmonitors;
};
diff --git a/tests/genericxml2xmlindata/cachetune-cdp.xml
b/tests/genericxml2xmlindata/cachetune-cdp.xml
index 9718f06..9f4c139 100644
--- a/tests/genericxml2xmlindata/cachetune-cdp.xml
+++ b/tests/genericxml2xmlindata/cachetune-cdp.xml
@@ -8,9 +8,12 @@
<cachetune vcpus='0-1'>
<cache id='0' level='3' type='code' size='7680'
unit='KiB'/>
<cache id='1' level='3' type='data' size='3840'
unit='KiB'/>
+ <monitor level='3' vcpus='0'/>
+ <monitor level='3' vcpus='1'/>
</cachetune>
<cachetune vcpus='2'>
<cache id='1' level='3' type='code' size='6'
unit='MiB'/>
+ <monitor level='3' vcpus='2'/>
</cachetune>
<cachetune vcpus='3'>
<cache id='1' level='3' type='data' size='6912'
unit='KiB'/>
diff --git a/tests/genericxml2xmlindata/cachetune-colliding-monitor.xml
b/tests/genericxml2xmlindata/cachetune-colliding-monitor.xml
new file mode 100644
index 0000000..d481fb5
--- /dev/null
+++ b/tests/genericxml2xmlindata/cachetune-colliding-monitor.xml
@@ -0,0 +1,30 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>4</vcpu>
+ <cputune>
+ <cachetune vcpus='0-1'>
+ <cache id='0' level='3' type='both' size='768'
unit='KiB'/>
+ <monitor level='3' vcpus='2'/>
+ </cachetune>
+ </cputune>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i686</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/genericxml2xmlindata/cachetune-small.xml
b/tests/genericxml2xmlindata/cachetune-small.xml
index ab2d9cf..748be08 100644
--- a/tests/genericxml2xmlindata/cachetune-small.xml
+++ b/tests/genericxml2xmlindata/cachetune-small.xml
@@ -7,6 +7,13 @@
<cputune>
<cachetune vcpus='0-1'>
<cache id='0' level='3' type='both' size='768'
unit='KiB'/>
+ <monitor level='3' vcpus='0'/>
+ <monitor level='3' vcpus='1'/>
+ <monitor level='3' vcpus='0-1'/>
+ </cachetune>
+ <cachetune vcpus='2-3'>
+ <monitor level='3' vcpus='2'/>
+ <monitor level='3' vcpus='3'/>
</cachetune>
</cputune>
<os>
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
index fa941f0..4393d44 100644
--- a/tests/genericxml2xmltest.c
+++ b/tests/genericxml2xmltest.c
@@ -137,6 +137,8 @@ mymain(void)
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
DO_TEST_FULL("cachetune-colliding-types", false, true,
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
+ DO_TEST_FULL("cachetune-colliding-monitor", false, true,
+ TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);
DO_TEST("memorytune");
DO_TEST_FULL("memorytune-colliding-allocs", false, true,
TEST_COMPARE_DOM_XML2XML_RESULT_FAIL_PARSE);