[libvirt] [PATCH RFC V2 0/2] Implement l3 CAT

Allow user to define cachetune in domain xml. V2 -> V1 changes: * Redefine cachetune xml in domain xml. * Create a struct for driver to talk with util/virresctrl.* * Nit fixes Eli Qiao (2): Resctrl: Add new xml element to support cache tune Resctrl: Do cache allocation while boot a qemu VM docs/schemas/domaincommon.rng | 29 ++ include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/conf/domain_conf.c | 112 ++++ src/conf/domain_conf.h | 19 + src/libvirt_private.syms | 9 + src/qemu/qemu_process.c | 81 +++ src/util/virerror.c | 1 + src/util/virresctrl.c | 822 ++++++++++++++++++++++++++++++ src/util/virresctrl.h | 88 ++++ tests/Makefile.am | 8 +- tests/virresctrldata/L3-free.schemata | 1 + tests/virresctrldata/L3CODE-free.schemata | 1 + tests/virresctrldata/L3DATA-free.schemata | 1 + tests/virresctrldata/linux-resctrl | 1 + tests/virresctrldata/linux-resctrl-cdp | 1 + tests/virresctrltest.c | 119 +++++ 17 files changed, 1294 insertions(+), 1 deletion(-) create mode 100644 src/util/virresctrl.c create mode 100644 src/util/virresctrl.h create mode 100644 tests/virresctrldata/L3-free.schemata create mode 100644 tests/virresctrldata/L3CODE-free.schemata create mode 100644 tests/virresctrldata/L3DATA-free.schemata create mode 120000 tests/virresctrldata/linux-resctrl create mode 120000 tests/virresctrldata/linux-resctrl-cdp create mode 100644 tests/virresctrltest.c -- 1.9.1

This patch adds new xml element to support cache tune as: <cputune> ... <cachetune cacheId='0' type='both' sizeKiB='2816' vcpus='0,1'/> ... </cputune> cacheId: reference of the host's cache banks id, it's from capabilities xml. type: cache bank type, it could be both, code, data. sizeKiB: must be multiple of granularity, must be greater than or equal to minimum. vcpus: cache allocation on vcpu set, if empty, will apply the allocation on all vcpus. Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- docs/schemas/domaincommon.rng | 29 +++++++++++ src/conf/domain_conf.c | 113 +++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 19 +++++++ 3 files changed, 160 insertions(+), 1 deletion(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index e259e3e..6e0262e 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -834,6 +834,25 @@ </attribute> </element> </zeroOrMore> + <zeroOrMore> + <element name="cachetune"> + <attribute name="cacheId"> + <ref name="cacheid"/> + </attribute> + <attribute name="type"> + <ref name="cachetype"/> + </attribute> + <!-- Cache size is an attribute in KiB, no way to express a unit --> + <attribute name="sizeKiB"> + <ref name="unsignedLong"/> + </attribute> + <optional> + <attribute name="vcpus"> + <ref name="cpuset"/> + </attribute> + </optional> + </element> + </zeroOrMore> <optional> <element name="emulatorpin"> <attribute name="cpuset"> @@ -5693,6 +5712,16 @@ <param name="minInclusive">-1</param> </data> </define> + <define name="cacheid"> + <data type="unsignedShort"> + <param name="pattern">[0-9]+</param> + </data> + </define> + <define name="cachetype"> + <data type="string"> + <param name="pattern">(both|code|data)</param> + </data> + </define> <!-- weight currently is in range [100, 1000] --> <define name="weight"> <data type="unsignedInt"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0409c62..fa8d03e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2927,6 +2927,11 @@ void virDomainDefFree(virDomainDefPtr def) xmlFreeNode(def->metadata); + for (i = 0; i < def->cachetune.n_banks; i++) + virBitmapFree(def->cachetune.cache_banks[i].vcpus); + + VIR_FREE(def->cachetune.cache_banks); + VIR_FREE(def); } @@ -16318,6 +16323,81 @@ virDomainVcpuPinDefParseXML(virDomainDefPtr def, return ret; } +/* Parse the XML definition for cachetune + * and a cachetune has the form + * <cacheId='0' type='both' sizeKib='1024' vcpus='0,1'/> + */ +static int +virDomainCacheTuneDefParseXML(virDomainDefPtr def, + int n, + xmlNodePtr* nodes) +{ + char* tmp = NULL; + size_t i; + int type = -1; + virDomainCacheBankPtr bank = NULL; + + if (VIR_ALLOC_N(bank, n) < 0) + goto cleanup; + + for (i = 0; i < n; i++) { + if (!(tmp = virXMLPropString(nodes[i], "cacheId"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("missing cacheId in cache tune")); + goto cleanup; + } + if (virStrToLong_uip(tmp, NULL, 10, &(bank[i].cache_id)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid setting for cacheId '%s'"), tmp); + goto cleanup; + } + VIR_FREE(tmp); + + if (!(tmp = virXMLPropString(nodes[i], "type"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing cache type")); + goto cleanup; + } + if ((type = virCacheTypeFromString(tmp)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("'unsupported cache type '%s'"), tmp); + goto cleanup; + } + VIR_FREE(tmp); + + if (!(tmp = virXMLPropString(nodes[i], "sizeKiB"))) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("missing sizeKiB in cache tune")); + goto cleanup; + } + if (virStrToLong_ull(tmp, NULL, 10, &(bank[i].size)) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("invalid setting for cache sizeKiB '%s'"), tmp); + goto cleanup; + } + /* convert to B */ + bank[i].size *= 1024; + VIR_FREE(tmp); + + if ((tmp = virXMLPropString(nodes[i], "vcpus"))) { + if (virBitmapParse(tmp, &bank[i].vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) + goto cleanup; + + if (virBitmapIsAllClear(bank[i].vcpus)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Invalid value of vcpus '%s'"), tmp); + goto cleanup; + } + } + } + + def->cachetune.cache_banks = bank; + def->cachetune.n_banks = n; + return 0; + + cleanup: + VIR_FREE(bank); + VIR_FREE(tmp); + return -1; +} /* Parse the XML definition for a iothreadpin * and an iothreadspin has the form @@ -17606,6 +17686,14 @@ virDomainDefParseXML(xmlDocPtr xml, } VIR_FREE(nodes); + if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) + goto error; + + if (n > 0 && virDomainCacheTuneDefParseXML(def, n, nodes) < 0) + goto error; + + VIR_FREE(nodes); + if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract emulatorpin nodes")); @@ -17642,7 +17730,6 @@ virDomainDefParseXML(xmlDocPtr xml, virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract vcpusched nodes")); goto error; - } for (i = 0; i < n; i++) { if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0) @@ -24390,6 +24477,28 @@ virDomainSchedulerFormat(virBufferPtr buf, } +static void +virDomainCacheTuneDefFormat(virBufferPtr buf, + virDomainCachetunePtr cache) +{ + size_t i; + + for (i = 0; i < cache->n_banks; i ++) { + virBufferAsprintf(buf, "<cachetune cacheId='%u' type='%s' " + "sizeKiB='%llu'", + cache->cache_banks[i].cache_id, + virCacheTypeToString(cache->cache_banks[i].type), + cache->cache_banks[i].size / 1024); + + if (cache->cache_banks[i].vcpus) + virBufferAsprintf(buf, " vcpus='%s'/>\n", + virBitmapFormat(cache->cache_banks[i].vcpus)); + else + virBufferAddLit(buf, "/>\n"); + } +} + + static int virDomainCputuneDefFormat(virBufferPtr buf, virDomainDefPtr def) @@ -24436,6 +24545,8 @@ virDomainCputuneDefFormat(virBufferPtr buf, "</iothread_quota>\n", def->cputune.iothread_quota); + virDomainCacheTuneDefFormat(&childrenBuf, &def->cachetune); + for (i = 0; i < def->maxvcpus; i++) { char *cpumask; virDomainVcpuDefPtr vcpu = def->vcpus[i]; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6d9ee97..729b676 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2190,6 +2190,23 @@ struct _virDomainMemtune { int allocation; /* enum virDomainMemoryAllocation */ }; +typedef struct _virDomainCacheBank virDomainCacheBank; +typedef virDomainCacheBank *virDomainCacheBankPtr; + +struct _virDomainCacheBank { + unsigned int cache_id; /* cache id to be allocated on */ + int type; /* enum virCacheType */ + unsigned long long size; /* in B */ + virBitmapPtr vcpus; +}; + +typedef struct _virDomainCachetune virDomainCachetune; +typedef virDomainCachetune *virDomainCachetunePtr; +struct _virDomainCachetune { + size_t n_banks; + virDomainCacheBankPtr cache_banks; +}; + typedef struct _virDomainPowerManagement virDomainPowerManagement; typedef virDomainPowerManagement *virDomainPowerManagementPtr; @@ -2263,6 +2280,8 @@ struct _virDomainDef { virDomainCputune cputune; + virDomainCachetune cachetune; + virDomainNumaPtr numa; virDomainResourceDefPtr resource; virDomainIdMapDef idmap; -- 1.9.1

On Mon, Jun 26, 2017 at 06:33:39PM +0800, Eli Qiao wrote:
This patch adds new xml element to support cache tune as:
<cputune> ... <cachetune cacheId='0' type='both' sizeKiB='2816' vcpus='0,1'/> ... </cputune>
cacheId: reference of the host's cache banks id, it's from capabilities xml. type: cache bank type, it could be both, code, data. sizeKiB: must be multiple of granularity, must be greater than or equal to minimum.
Why would you come up with something like 'sizeKib'? That's so inconvenient.
vcpus: cache allocation on vcpu set, if empty, will apply the allocation on all vcpus.
Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- docs/schemas/domaincommon.rng | 29 +++++++++++ src/conf/domain_conf.c | 113 +++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 19 +++++++ 3 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0409c62..fa8d03e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17642,7 +17730,6 @@ virDomainDefParseXML(xmlDocPtr xml, virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract vcpusched nodes")); goto error; - }
for (i = 0; i < n; i++) { if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0)
And this hunk breaks the whole compilation, of course. I'm not at work for few days plus there's a public holiday in my country, so I won't be here for a while. That shouldn't stop anyone else reviewing your code, but I'm giving you a heads-up that I'll probably work on a separate solution if I have some extra time on my hands during vacation. Have a nice day, Martin

On Friday, 30 June 2017 at 7:06 PM, Martin Kletzander wrote:
On Mon, Jun 26, 2017 at 06:33:39PM +0800, Eli Qiao wrote:
This patch adds new xml element to support cache tune as:
<cputune> ... <cachetune cacheId='0' type='both' sizeKiB='2816' vcpus='0,1'/> ... </cputune>
cacheId: reference of the host's cache banks id, it's from capabilities xml. type: cache bank type, it could be both, code, data. sizeKiB: must be multiple of granularity, must be greater than or equal to minimum.
Why would you come up with something like 'sizeKib'? That's so inconvenient.
Sure,
vcpus: cache allocation on vcpu set, if empty, will apply the allocation on all vcpus.
Signed-off-by: Eli Qiao <liyong.qiao@intel.com (mailto:liyong.qiao@intel.com)> --- docs/schemas/domaincommon.rng | 29 +++++++++++ src/conf/domain_conf.c | 113 +++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 19 +++++++ 3 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 0409c62..fa8d03e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17642,7 +17730,6 @@ virDomainDefParseXML(xmlDocPtr xml, virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract vcpusched nodes")); goto error; - }
for (i = 0; i < n; i++) { if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0)
And this hunk breaks the whole compilation, of course.
ops, stupid me.
I'm not at work for few days plus there's a public holiday in my country, so I won't be here for a while. That shouldn't stop anyone else reviewing your code, but I'm giving you a heads-up that I'll probably work on a separate solution if I have some extra time on my hands during vacation.
Sure, that’s great if you can have your version, I would like to help reviewing and testing
Have a nice day, Martin
-- libvir-list mailing list libvir-list@redhat.com (mailto:libvir-list@redhat.com) https://www.redhat.com/mailman/listinfo/libvir-list

Set cachetune if user define cachetune in domain xml when using qemu driver. This patch adds 3 major private interface. virResctrlGetFreeCache: return free cache, default cache substract cache allocated. virResctrlSetCachetunes: set cache banks which defined in a domain. virResctrlRemoveCachetunes: remove cache allocation group from the host. Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 9 + src/qemu/qemu_process.c | 81 +++ src/util/virerror.c | 1 + src/util/virresctrl.c | 822 ++++++++++++++++++++++++++++++ src/util/virresctrl.h | 88 ++++ tests/Makefile.am | 8 +- tests/virresctrldata/L3-free.schemata | 1 + tests/virresctrldata/L3CODE-free.schemata | 1 + tests/virresctrldata/L3DATA-free.schemata | 1 + tests/virresctrldata/linux-resctrl | 1 + tests/virresctrldata/linux-resctrl-cdp | 1 + tests/virresctrltest.c | 119 +++++ 14 files changed, 1134 insertions(+), 1 deletion(-) create mode 100644 src/util/virresctrl.c create mode 100644 src/util/virresctrl.h create mode 100644 tests/virresctrldata/L3-free.schemata create mode 100644 tests/virresctrldata/L3CODE-free.schemata create mode 100644 tests/virresctrldata/L3DATA-free.schemata create mode 120000 tests/virresctrldata/linux-resctrl create mode 120000 tests/virresctrldata/linux-resctrl-cdp create mode 100644 tests/virresctrltest.c diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 2efee8f..4bc0c74 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -132,6 +132,7 @@ typedef enum { VIR_FROM_PERF = 65, /* Error from perf */ VIR_FROM_LIBSSH = 66, /* Error from libssh connection transport */ + VIR_FROM_RESCTRL = 67, /* Error from resctrl */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/src/Makefile.am b/src/Makefile.am index eae32dc..8dbb778 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -167,6 +167,7 @@ UTIL_SOURCES = \ util/virprocess.c util/virprocess.h \ util/virqemu.c util/virqemu.h \ util/virrandom.h util/virrandom.c \ + util/virresctrl.h util/virresctrl.c \ util/virrotatingfile.h util/virrotatingfile.c \ util/virscsi.c util/virscsi.h \ util/virscsihost.c util/virscsihost.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c1e9471..72e5096 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2439,6 +2439,15 @@ virRandomGenerateWWN; virRandomInt; +# util/virresctrl.h +virResctrlBitmap2String; +virResctrlFreeSchemata; +virResctrlGetFreeCache; +virResctrlRemoveCachetunes; +virResctrlSetCachetunes; +virResctrlTypeToString; + + # util/virrotatingfile.h virRotatingFileReaderConsume; virRotatingFileReaderFree; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index fa9990e..3b11caa 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -70,6 +70,7 @@ #include "virbitmap.h" #include "viratomic.h" #include "virnuma.h" +#include "virresctrl.h" #include "virstring.h" #include "virhostdev.h" #include "secret_util.h" @@ -5130,6 +5131,77 @@ qemuProcessSetupVcpus(virDomainObjPtr vm) return 0; } +static int +qemuProcessSetCacheBanks(virCapsHostPtr caps, virDomainObjPtr vm) +{ + size_t i, j; + virDomainCachetunePtr cachetune; + virResctrlCachetunePtr resctrl_cachetune; + unsigned int max_vcpus = virDomainDefGetVcpusMax(vm->def); + pid_t *pids = NULL; + virDomainVcpuDefPtr vcpu; + size_t npids = 0; + size_t count = 0; + int ret = -1; + + cachetune = &(vm->def->cachetune); + + if (VIR_ALLOC_N(resctrl_cachetune, cachetune->n_banks) < 0) + goto cleanup; + + /* construct resctrl_cachetune array */ + for (i = 0; i < cachetune->n_banks; i++) { + + resctrl_cachetune[i].cache_id = cachetune->cache_banks[i].cache_id; + resctrl_cachetune[i].type = cachetune->cache_banks[i].type; + resctrl_cachetune[i].size = cachetune->cache_banks[i].size; + + /* get granularity from host's capabilities */ + for (j = 0; j < caps->ncaches; j++) { + /* even enable CDP, granularity for code and data are same */ + if (caps->caches[j]->id == resctrl_cachetune[i].cache_id && + caps->caches[j]->controls) { + resctrl_cachetune[i].granularity = caps->caches[j]->controls[0]->granularity; + break; + } + } + + /* create pids of vcpus array */ + if (cachetune->cache_banks[i].vcpus) { + for (j = 0; j < max_vcpus; j++) { + if (virBitmapIsBitSet(cachetune->cache_banks[i].vcpus, j)) { + + vcpu = virDomainDefGetVcpu(vm->def, j); + if (!vcpu->online) + continue; + + if (VIR_RESIZE_N(pids, npids, count, 1) < 0) + goto cleanup; + pids[count ++] = qemuDomainGetVcpuPid(vm, j); + } + } + } + } + + /* If not specify vcpus in cachetune, add vm->pid */ + if (pids == NULL) { + if (VIR_ALLOC_N(pids, 1) < 0) + goto cleanup; + pids[0] = vm->pid; + count = 1; + } + + ret = virResctrlSetCachetunes(vm->def->uuid, + resctrl_cachetune, + cachetune->n_banks, + pids, + count); + + cleanup: + VIR_FREE(resctrl_cachetune); + VIR_FREE(pids); + return ret; +} int qemuProcessSetupIOThread(virDomainObjPtr vm, @@ -5966,6 +6038,12 @@ qemuProcessLaunch(virConnectPtr conn, qemuProcessAutoDestroyAdd(driver, vm, conn) < 0) goto cleanup; + VIR_DEBUG("Cache allocation"); + if (vm->def->cachetune.n_banks > 0 && + qemuProcessSetCacheBanks(&driver->caps->host, + vm) < 0) + goto cleanup; + ret = 0; cleanup: @@ -6472,6 +6550,9 @@ void qemuProcessStop(virQEMUDriverPtr driver, virPerfFree(priv->perf); priv->perf = NULL; + if (&(vm->def->cachetune) != NULL) + virResctrlRemoveCachetunes(vm->def->uuid); + qemuProcessRemoveDomainStatus(driver, vm); /* Remove VNC and Spice ports from port reservation bitmap, but only if diff --git a/src/util/virerror.c b/src/util/virerror.c index ef17fb5..02fabcc 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -139,6 +139,7 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST, "Perf", /* 65 */ "Libssh transport layer", + "Resource Control", ) diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c new file mode 100644 index 0000000..541c69c --- /dev/null +++ b/src/util/virresctrl.c @@ -0,0 +1,822 @@ +/* + * virresctrl.c: methods for managing resource control + * + * Copyright (C) 2017 Intel, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Eli Qiao <liyong.qiao@intel.com> + */ + +#include <config.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" +#include "virresctrl.h" +#include "virstring.h" +#include "viruuid.h" + +VIR_LOG_INIT("util.resctrl"); + +#define VIR_FROM_THIS VIR_FROM_RESCTRL +#define SYSFS_RESCTRL_PATH "/sys/fs/resctrl" +#define MAX_CBM_LEN 20 +#define VIR_RESCTRL_LOCK(fd, op) flock(fd, op) +#define VIR_RESCTRL_UNLOCK(fd) flock(fd, LOCK_UN) +#define CONSTRUCT_RESCTRL_PATH(domain_name, item_name) \ +do { \ + if (NULL == domain_name) { \ + if (virAsprintf(&path, "%s/%s", SYSFS_RESCTRL_PATH, item_name) < 0) \ + return -1; \ + } else { \ + if (virAsprintf(&path, "%s/%s/%s", SYSFS_RESCTRL_PATH, domain_name, \ + item_name) < 0) \ + return -1; \ + } \ +} while (0) + +VIR_ENUM_IMPL(virResctrl, VIR_RESCTRL_TYPE_LAST, + "L3", + "L3CODE", + "L3DATA") + +/** + * a virResctrlGroup represents a resource control group, it's a directory + * under /sys/fs/resctrl. + * e.g. /sys/fs/resctrl/CG1 + * |-- cpus + * |-- schemata + * `-- tasks + * # cat schemata + * L3DATA:0=fffff;1=fffff + * L3CODE:0=fffff;1=fffff + * + * Besides, it can also represent the default resource control group of the + * host. + */ + +typedef struct _virResctrlGroup virResctrlGroup; +typedef virResctrlGroup *virResctrlGroupPtr; +struct _virResctrlGroup { + char *name; /* resource group name, NULL for default host group */ + size_t n_tasks; /* number of tasks assigned to the resource group */ + char **tasks; /* task id list */ + virResctrlSchemataPtr schemata[VIR_RESCTRL_TYPE_LAST]; /* Array for schemata */ +}; + +/* All resource control groups on this host, including default resource group */ +typedef struct _virResctrlHost virResctrlHost; +typedef virResctrlHost *virResctrlHostPtr; +struct _virResctrlHost { + size_t n_groups; /* number of resource control group */ + virResctrlGroupPtr *groups; /* list of resource control group */ +}; + +void +virResctrlFreeSchemata(virResctrlSchemataPtr ptr) +{ + size_t i; + + if (!ptr) + return; + + for (i = 0; i < ptr->n_masks; i++) { + virBitmapFree(ptr->masks[i]->mask); + VIR_FREE(ptr->masks[i]); + } + + VIR_FREE(ptr); + ptr = NULL; +} + +static void +virResctrlFreeGroup(virResctrlGroupPtr ptr) +{ + size_t i; + + if (!ptr) + return; + + for (i = 0; i < ptr->n_tasks; i++) + VIR_FREE(ptr->tasks[i]); + VIR_FREE(ptr->name); + + for (i = 0; i < VIR_RESCTRL_TYPE_LAST; i++) + virResctrlFreeSchemata(ptr->schemata[i]); + + VIR_FREE(ptr); + ptr = NULL; +} + +/* Return specify type of schemata string from schematalval. + e.g., 0=f;1=f */ +static int +virResctrlGetSchemataString(virResctrlType type, + const char *schemataval, + char **schematastr) +{ + int rc = -1; + char *prefix = NULL; + char **lines = NULL; + + if (virAsprintf(&prefix, + "%s:", + virResctrlTypeToString(type)) < 0) + return -1; + + lines = virStringSplit(schemataval, "\n", 0); + + if (VIR_STRDUP(*schematastr, + virStringListGetFirstWithPrefix(lines, prefix)) < 0) + goto cleanup; + + if (*schematastr == NULL) + rc = -1; + else + rc = 0; + + cleanup: + VIR_FREE(prefix); + virStringListFree(lines); + return rc; +} + +static int +virResctrlRemoveSysGroup(const char* name) +{ + char *path = NULL; + int ret = -1; + + if ((ret = virAsprintf(&path, "%s/%s", SYSFS_RESCTRL_PATH, name)) < 0) + return ret; + + ret = rmdir(path); + + VIR_FREE(path); + return ret; +} + +static int +virResctrlNewSysGroup(const char *name) +{ + char *path = NULL; + int ret = -1; + mode_t mode = 0755; + + if (virAsprintf(&path, "%s/%s", SYSFS_RESCTRL_PATH, name) < 0) + return -1; + + if (virDirCreate(path, mode, 0, 0, 0) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(path); + return ret; +} + +static int +virResctrlWrite(const char *name, const char *item, const char *content) +{ + char *path; + int writefd; + int rc = -1; + + CONSTRUCT_RESCTRL_PATH(name, item); + + if (!virFileExists(path)) + goto cleanup; + + if ((writefd = open(path, O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR)) < 0) + goto cleanup; + + if (safewrite(writefd, content, strlen(content)) < 0) + goto cleanup; + rc = 0; + + cleanup: + VIR_FREE(path); + VIR_FORCE_CLOSE(writefd); + return rc; +} + +static +virBitmapPtr virResctrlMask2Bitmap(const char *mask) +{ + virBitmapPtr bitmap; + unsigned int tmp; + size_t i; + + if (virStrToLong_ui(mask, NULL, 16, &tmp) < 0) + return NULL; + + bitmap = virBitmapNewEmpty(); + + for (i = 0; i < MAX_CBM_LEN; i++) { + if (((tmp & 0x1) == 0x1) && + (virBitmapSetBitExpand(bitmap, i) < 0)) + goto error; + tmp = tmp >> 1; + } + return bitmap; + + error: + virBitmapFree(bitmap); + return NULL; +} + +char *virResctrlBitmap2String(virBitmapPtr bitmap) +{ + char *tmp; + char *ret = NULL; + char *p; + tmp = virBitmapString(bitmap); + /* skip "0x" */ + p = tmp + 2; + + /* first non-0 position */ + while (*++p == '0'); + + if (VIR_STRDUP(ret, p) < 0) + ret = NULL; + + VIR_FREE(tmp); + return ret; +} + +static int +virResctrlParseSchemata(const char* schemata_str, + virResctrlSchemataPtr schemata) +{ + VIR_DEBUG("schemata_str=%s, schemata=%p", schemata_str, schemata); + + int ret = -1; + size_t i; + virResctrlMaskPtr mask; + char **schemata_list; + char *mask_str; + + /* parse 0=fffff;1=f */ + schemata_list = virStringSplit(schemata_str, ";", 0); + + if (!schemata_list) + goto cleanup; + + for (i = 0; schemata_list[i] != NULL; i++) { + /* parse 0=fffff */ + mask_str = strchr(schemata_list[i], '='); + + if (!mask_str) + goto cleanup; + + if (VIR_ALLOC(mask) < 0) + goto cleanup; + + mask->cache_id = i; + mask->mask = virResctrlMask2Bitmap(mask_str + 1); + schemata->n_masks += 1; + schemata->masks[i] = mask; + + } + ret = 0; + + cleanup: + virStringListFree(schemata_list); + return ret; +} + +static int +virResctrlLoadGroup(const char *name, + virResctrlHostPtr host) +{ + VIR_DEBUG("name=%s, host=%p\n", name, host); + + int ret = -1; + char *schemataval = NULL; + char *schemata_str = NULL; + virResctrlType i; + int rv; + virResctrlGroupPtr grp; + virResctrlSchemataPtr schemata; + + rv = virFileReadValueString(&schemataval, + SYSFS_RESCTRL_PATH "/%s/schemata", + name ? name : ""); + + if (rv < 0) + return -1; + + if (VIR_ALLOC(grp) < 0) + goto cleanup; + + if (VIR_STRDUP(grp->name, name) < 0) + goto cleanup; + + for (i = 0; i < VIR_RESCTRL_TYPE_LAST; i++) { + rv = virResctrlGetSchemataString(i, schemataval, &schemata_str); + + if (rv < 0) + continue; + + if (VIR_ALLOC(schemata) < 0) + goto cleanup; + + schemata->type = i; + + if (virResctrlParseSchemata(schemata_str, schemata) < 0) { + VIR_FREE(schemata); + VIR_FREE(schemata_str); + goto cleanup; + } + + grp->schemata[i] = schemata; + VIR_FREE(schemata_str); + } + + if (VIR_APPEND_ELEMENT(host->groups, + host->n_groups, + grp) < 0) { + virResctrlFreeGroup(grp); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(schemataval); + return ret; +} + +static int +virResctrlLoadHost(virResctrlHostPtr host) +{ + int rv = -1; + DIR *dirp = NULL; + char *path = NULL; + struct dirent *ent; + + rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH); + if (rv < 0) + return -1; + + /* load default group first */ + if (virResctrlLoadGroup(NULL, host) < 0) + return -1; + + while ((rv = virDirRead(dirp, &ent, path)) > 0) { + /* resctrl is not hierarchical, only read directory under + /sys/fs/resctrl */ + if ((ent->d_type != DT_DIR) || STREQ(ent->d_name, "info")) + continue; + + if (virResctrlLoadGroup(ent->d_name, host) < 0) + return -1; + } + return 0; +} + +static void +virResctrlRefreshHost(virResctrlHostPtr host) +{ + virResctrlGroupPtr default_grp = NULL; + virResctrlSchemataPtr schemata = NULL; + size_t i, j; + virResctrlType t; + + default_grp = host->groups[0]; + + for (t = 0; t < VIR_RESCTRL_TYPE_LAST; t++) { + if (default_grp->schemata[t] != NULL) { + for (i = 0; i < default_grp->schemata[t]->n_masks; i++) { + /* Reset default group's mask */ + virBitmapSetAll(default_grp->schemata[t]->masks[i]->mask); + /* Loop each other resource group except default group */ + for (j = 1; j < host->n_groups; j++) { + schemata = host->groups[j]->schemata[t]; + virBitmapSubtract(default_grp->schemata[t]->masks[i]->mask, + schemata->masks[i]->mask); + } + } + } + } +} + +static virResctrlGroupPtr +virResctrlGetFreeGroup(void) +{ + size_t i; + virResctrlHostPtr host = NULL; + virResctrlGroupPtr grp = NULL; + + if (VIR_ALLOC(host) < 0) + return NULL; + + if (virResctrlLoadHost(host) < 0) + goto error; + + virResctrlRefreshHost(host); + + for (i = 1; i < host->n_groups; i++) + virResctrlFreeGroup(host->groups[i]); + + grp = host->groups[0]; + VIR_FREE(host); + + return grp; + + error: + virResctrlFreeGroup(grp); + return NULL; +} + +virResctrlSchemataPtr +virResctrlGetFreeCache(virResctrlType type) +{ + VIR_DEBUG("type=%d", type); + + virResctrlType t; + virResctrlGroupPtr grp = NULL; + virResctrlSchemataPtr schemata = NULL; + int lockfd = -1; + + lockfd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY); + if (lockfd < 0) + return NULL; + + VIR_RESCTRL_LOCK(lockfd, LOCK_SH); + + if ((grp = virResctrlGetFreeGroup()) == NULL) + goto cleanup; + + for (t = 0; t < VIR_RESCTRL_TYPE_LAST; t++) { + if (t == type) + schemata = grp->schemata[t]; + else + virResctrlFreeSchemata(grp->schemata[t]); + } + + cleanup: + VIR_RESCTRL_UNLOCK(lockfd); + VIR_FORCE_CLOSE(lockfd); + return schemata; +} + +static int +virResctrlCalculateCbm(int cbm_len, + virBitmapPtr defaultcbm, + virBitmapPtr newcbm) +{ + VIR_DEBUG("cbm_len=%d, defaultcbm=%p, newcbm=%p", + cbm_len, defaultcbm, newcbm); + + ssize_t pos = -1; + size_t i; + + /* not enough cache way to be allocated */ + if (virBitmapCountBits(defaultcbm) < cbm_len + 1) + return -1; + + while ((pos = virBitmapNextSetBit(defaultcbm, pos)) >= 0) { + for (i = 0; i < cbm_len; i++) + ignore_value(virBitmapSetBitExpand(newcbm, i + pos)); + /* Test if newcbm is sub set of defaultcbm */ + if (virBitmapNextClearBit(defaultcbm, pos) > i + pos) { + break; + } else { + pos = pos + i - 1; + virBitmapClearAll(newcbm); + } + } + + if (virBitmapCountBits(newcbm) != cbm_len) + return -1; + + /* consume default cbm after allocation */ + virBitmapSubtract(defaultcbm, newcbm); + + return 0; +} + +/* Fill mask value for newly created resource group base on hostcachebank + * and domcachebank */ +static int +virResctrlFillMask(virResctrlGroupPtr grp, + virResctrlGroupPtr free_grp, + virResctrlCachetunePtr cachetune) +{ + VIR_DEBUG("grp=%p, free_grp=%p, cachetune=%p", + grp, free_grp, cachetune); + + unsigned int cache_id; + unsigned int cache_type; + int cbm_candidate_len; + virResctrlSchemataPtr schemata; + virResctrlMaskPtr mask; + + cache_type = cachetune->type; + cache_id = cachetune->cache_id; + schemata = grp->schemata[cache_type]; + + if ((schemata == NULL) && (VIR_ALLOC(schemata) < 0)) + return -1; + + if (VIR_ALLOC(mask) < 0) + return -1; + + mask->cache_id = cache_id; + mask->mask = virBitmapNewEmpty(); + + /* here should be control->granularity and control->min + also domcachebank size should be checked while define domain xml */ + cbm_candidate_len = cachetune->size / cachetune->granularity; + if (virResctrlCalculateCbm(cbm_candidate_len, + free_grp->schemata[cache_type]->masks[cache_id]->mask, + mask->mask) < 0) + goto error; + + schemata->type = cache_type; + schemata->n_masks += 1; + schemata->masks[cache_id] = mask; + grp->schemata[cache_type] = schemata; + + return 0; + + error: + VIR_FREE(schemata); + return -1; +} + +/* only keep the highest consecutive bits */ +static void +virResctrlTrimMask(virResctrlMaskPtr mask) +{ + size_t i; + ssize_t setbit = -1; + ssize_t clearbit = -1; + + clearbit = virBitmapNextClearBit(mask->mask, -1); + setbit = virBitmapNextSetBit(mask->mask, -1); + for (i = setbit; i < clearbit; i++) + ignore_value(virBitmapClearBit(mask->mask, i)); +} + +static int +virResctrlCompleteMask(virResctrlSchemataPtr schemata, + virResctrlSchemataPtr defaultschemata) +{ + size_t i; + virResctrlMaskPtr mask; + + if (schemata == NULL && VIR_ALLOC(schemata) < 0) + return -1; + + if (schemata->n_masks == defaultschemata->n_masks) + return 0; + + for (i = 0; i < defaultschemata->n_masks; i++) { + if (schemata->masks[i] == NULL) { + if (VIR_ALLOC(mask) < 0) + goto error; + + mask->cache_id = i; + mask->mask = virBitmapNewEmpty(); + schemata->n_masks += 1; + schemata->masks[i] = mask; + /* resctrl doesn't allow mask to be zero + use higher bits to fill up the cbm which + domaincache bank doens't provide */ + ignore_value(virBitmapSetBitExpand(mask->mask, + virBitmapLastSetBit(defaultschemata->masks[i]->mask))); + } + /* only keep the highest consecutive bits for default group */ + virResctrlTrimMask(defaultschemata->masks[i]); + } + + return 0; + + error: + VIR_FREE(schemata); + return -1; +} + +/* complete the schemata in the resrouce group before it can be write back + to resctrl */ +static int +virResctrlCompleteGroup(virResctrlGroupPtr grp, + virResctrlGroupPtr default_grp) +{ + virResctrlType t; + virResctrlSchemataPtr schemata; + virResctrlSchemataPtr defaultschemata; + + /* NOTES: resctrl system require we need provide all cache's cbm mask */ + for (t = 0; t < VIR_RESCTRL_TYPE_LAST; t++) { + defaultschemata = default_grp->schemata[t]; + if (defaultschemata != NULL) { + schemata = grp->schemata[t]; + if (virResctrlCompleteMask(schemata, defaultschemata) < 0) + return -1; + } + } + return 0; +} + +static +char *virResctrlGetSchemataStr(virResctrlSchemataPtr schemata) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + size_t i; + + virBufferAsprintf(&buf, "%s:%u=%s", + virResctrlTypeToString(schemata->type), + schemata->masks[0]->cache_id, + virResctrlBitmap2String(schemata->masks[0]->mask)); + + for (i = 1; i < schemata->n_masks; i ++) + virBufferAsprintf(&buf, ";%u=%s", + schemata->masks[i]->cache_id, + virResctrlBitmap2String(schemata->masks[i]->mask)); + + return virBufferContentAndReset(&buf); +} + +static int +virResctrlFlushGroup(virResctrlGroupPtr grp) +{ + int ret = -1; + size_t i; + char *schemata_str = NULL; + virResctrlType t; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + VIR_DEBUG("grp=%p", grp); + + if (grp->name != NULL && virResctrlNewSysGroup(grp->name) < 0) + return -1; + + for (t = 0; t < VIR_RESCTRL_TYPE_LAST; t++) { + if (grp->schemata[t] != NULL) { + schemata_str = virResctrlGetSchemataStr(grp->schemata[t]); + virBufferAsprintf(&buf, "%s\n", schemata_str); + VIR_FREE(schemata_str); + } + } + + schemata_str = virBufferContentAndReset(&buf); + + if (virResctrlWrite(grp->name, "schemata", schemata_str) < 0) + goto cleanup; + + for (i = 0; i < grp->n_tasks; i++) { + if (virResctrlWrite(grp->name, "tasks", grp->tasks[i]) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(schemata_str); + return ret; +} + +int virResctrlSetCachetunes(unsigned char* uuid, + virResctrlCachetunePtr cachetunes, size_t ncachetune, + pid_t *pids, size_t npid) +{ + size_t i; + int ret = -1; + char name[VIR_UUID_STRING_BUFLEN]; + char *tmp; + int lockfd = -1; + virResctrlGroupPtr grp = NULL; + virResctrlGroupPtr default_grp = NULL; + + virUUIDFormat(uuid, name); + + if (ncachetune < 1) + return 0; + + /* create new resource group */ + if (VIR_ALLOC(grp) < 0) + goto error; + + if (VIR_STRDUP(grp->name, name) < 0) + goto error; + + /* allocate file lock */ + lockfd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY); + if (lockfd < 0) + goto error; + + VIR_RESCTRL_LOCK(lockfd, LOCK_EX); + + if ((default_grp = virResctrlGetFreeGroup()) == NULL) + goto error; + + /* Allocate cache for each cache bank defined in cache tune */ + for (i = 0; i < ncachetune; i++) { + /* fill up newly crated grp and consume from default_grp */ + if (virResctrlFillMask(grp, default_grp, &cachetunes[i]) < 0) + goto error; + } + + /* Add tasks to grp */ + for (i = 0; i < npid; i++) { + if (virAsprintf(&tmp, "%llu", (long long)pids[i]) < 0) + goto error; + + if (VIR_APPEND_ELEMENT(grp->tasks, + grp->n_tasks, + tmp) < 0) { + VIR_FREE(tmp); + goto error; + } + } + + if (virResctrlCompleteGroup(grp, default_grp) < 0) { + VIR_WARN("Failed to complete group"); + goto error; + } + + if (virResctrlFlushGroup(grp) < 0) + goto error; + + if (virResctrlFlushGroup(default_grp) < 0) { + virResctrlRemoveSysGroup(grp->name); + goto error; + } + + ret = 0; + + error: + VIR_RESCTRL_UNLOCK(lockfd); + VIR_FORCE_CLOSE(lockfd); + virResctrlFreeGroup(grp); + virResctrlFreeGroup(default_grp); + return ret; +} + +int virResctrlRemoveCachetunes(unsigned char* uuid) +{ + int ret = -1; + int lockfd = -1; + size_t i; + virResctrlType t; + virResctrlSchemataPtr schemata; + char name[VIR_UUID_STRING_BUFLEN]; + virResctrlGroupPtr default_grp = NULL; + + virUUIDFormat(uuid, name); + + VIR_DEBUG("name=%s", name); + + lockfd = open(SYSFS_RESCTRL_PATH, O_DIRECTORY); + if (lockfd < 0) + return -1; + + VIR_RESCTRL_LOCK(lockfd, LOCK_SH); + + if (virResctrlRemoveSysGroup(name) < 0) + goto cleanup; + + if ((default_grp = virResctrlGetFreeGroup()) == NULL) + goto cleanup; + + for (t = 0; t < VIR_RESCTRL_TYPE_LAST; t++) { + schemata = default_grp->schemata[t]; + if (schemata != NULL) { + for (i = 0; i < schemata->n_masks; i++) + virResctrlTrimMask(schemata->masks[i]); + } + } + + if (virResctrlFlushGroup(default_grp) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_RESCTRL_UNLOCK(lockfd); + VIR_FORCE_CLOSE(lockfd); + return ret; +} diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h new file mode 100644 index 0000000..49781ad --- /dev/null +++ b/src/util/virresctrl.h @@ -0,0 +1,88 @@ +/* + * virresctrl.h: header for managing resctrl control + * + * Copyright (C) 2017 Intel, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Eli Qiao <liyong.qiao@intel.com> + */ + +#ifndef __VIR_RESCTRL_H__ +# define __VIR_RESCTRL_H__ + +#include "virutil.h" +#include "virbitmap.h" + +#define MAX_CACHE_ID 16 + +typedef enum { + VIR_RESCTRL_TYPE_L3, + VIR_RESCTRL_TYPE_L3_CODE, + VIR_RESCTRL_TYPE_L3_DATA, + + VIR_RESCTRL_TYPE_LAST +} virResctrlType; + +VIR_ENUM_DECL(virResctrl); + +typedef struct _virResctrlCachetune virResctrlCachetune; +typedef virResctrlCachetune *virResctrlCachetunePtr; +struct _virResctrlCachetune { + unsigned int cache_id; + virResctrlType type; + unsigned long long size; /* in B */ + unsigned long long granularity; /* min size of the cache */ +}; + +/* + * a virResctrlMask represents one of mask object in a + * resource control group. + * e.g., 0=f + */ +typedef struct _virResctrlMask virResctrlMask; +typedef virResctrlMask *virResctrlMaskPtr; +struct _virResctrlMask { + unsigned int cache_id; /* cache resource id */ + virBitmapPtr mask; /* the cbm mask */ +}; + +/* + * a virResctrlSchemata represents schemata objects of specific type of + * resource in a resource control group. + * eg: L3:0=f,1=ff + */ +typedef struct _virResctrlSchemata virResctrlSchemata; +typedef virResctrlSchemata *virResctrlSchemataPtr; +struct _virResctrlSchemata { + virResctrlType type; /* resource control type, e.g., L3 */ + size_t n_masks; /* number of masks */ + virResctrlMaskPtr masks[MAX_CACHE_ID]; /* array of mask, use array for easy index */ +}; + +/* Get free cache of the host, result saved in schemata */ +virResctrlSchemataPtr virResctrlGetFreeCache(virResctrlType type); + +/* Get mask string from Bitmap */ +char *virResctrlBitmap2String(virBitmapPtr bitmap); + +void virResctrlFreeSchemata(virResctrlSchemataPtr ptr); + +int virResctrlSetCachetunes(unsigned char* uuid, + virResctrlCachetunePtr cachetunes, size_t ncachetune, + pid_t *pids, size_t npid); +int virResctrlRemoveCachetunes(unsigned char* uuid); +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 19986dc..e0b9923 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -229,6 +229,7 @@ if WITH_LINUX test_programs += fchosttest test_programs += scsihosttest test_programs += vircaps2xmltest +test_programs += virresctrltest test_libraries += virusbmock.la \ virnetdevbandwidthmock.la \ virnumamock.la \ @@ -1149,8 +1150,13 @@ virnumamock_la_CFLAGS = $(AM_CFLAGS) virnumamock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS) virnumamock_la_LIBADD = $(MOCKLIBS_LIBS) +virresctrltest_SOURCES = \ + virresctrltest.c testutils.h testutils.c virfilewrapper.h virfilewrapper.c +virresctrltest_LDADD = $(LDADDS) + else ! WITH_LINUX -EXTRA_DIST += vircaps2xmltest.c virnumamock.c virfilewrapper.c virfilewrapper.h +EXTRA_DIST += vircaps2xmltest.c virnumamock.c virfilewrapper.c \ + virfilewrapper.h virresctrltest.c endif ! WITH_LINUX if WITH_NSS diff --git a/tests/virresctrldata/L3-free.schemata b/tests/virresctrldata/L3-free.schemata new file mode 100644 index 0000000..9b47d25 --- /dev/null +++ b/tests/virresctrldata/L3-free.schemata @@ -0,0 +1 @@ +L3:0=1ffff;1=1ffff diff --git a/tests/virresctrldata/L3CODE-free.schemata b/tests/virresctrldata/L3CODE-free.schemata new file mode 100644 index 0000000..7039c45 --- /dev/null +++ b/tests/virresctrldata/L3CODE-free.schemata @@ -0,0 +1 @@ +L3CODE:0=cffff;1=cffff diff --git a/tests/virresctrldata/L3DATA-free.schemata b/tests/virresctrldata/L3DATA-free.schemata new file mode 100644 index 0000000..30f1cbd --- /dev/null +++ b/tests/virresctrldata/L3DATA-free.schemata @@ -0,0 +1 @@ +L3DATA:0=3ffff;1=3ffff diff --git a/tests/virresctrldata/linux-resctrl b/tests/virresctrldata/linux-resctrl new file mode 120000 index 0000000..069dfb2 --- /dev/null +++ b/tests/virresctrldata/linux-resctrl @@ -0,0 +1 @@ +../vircaps2xmldata/linux-resctrl \ No newline at end of file diff --git a/tests/virresctrldata/linux-resctrl-cdp b/tests/virresctrldata/linux-resctrl-cdp new file mode 120000 index 0000000..c5a973f --- /dev/null +++ b/tests/virresctrldata/linux-resctrl-cdp @@ -0,0 +1 @@ +../vircaps2xmldata/linux-resctrl-cdp \ No newline at end of file diff --git a/tests/virresctrltest.c b/tests/virresctrltest.c new file mode 100644 index 0000000..a9d75f1 --- /dev/null +++ b/tests/virresctrltest.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) Intel, Inc. 2017 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Eli Qiao <liyong.qiao@intel.com> + */ + +#include <config.h> +#include <stdlib.h> + +#include "testutils.h" +#include "virbitmap.h" +#include "virfilewrapper.h" +#include "virresctrl.h" + + +#define VIR_FROM_THIS VIR_FROM_NONE + +struct virResctrlData { + const char *filename; + virResctrlType type; +}; + +static void +GetSchemataStr(virResctrlSchemataPtr schemata, char **str) +{ + size_t i; + + virBuffer buf = VIR_BUFFER_INITIALIZER; + virBufferAsprintf(&buf, "%s:%u=%s", + virResctrlTypeToString(schemata->type), + schemata->masks[0]->cache_id, + virResctrlBitmap2String(schemata->masks[0]->mask)); + + for (i = 1; i < schemata->n_masks; i ++) + virBufferAsprintf(&buf, ";%u=%s", + schemata->masks[i]->cache_id, + virResctrlBitmap2String(schemata->masks[i]->mask)); + + *str = virBufferContentAndReset(&buf); +} + +static int +test_virResctrl(const void *opaque) +{ + struct virResctrlData *data = (struct virResctrlData *) opaque; + char *dir = NULL; + char *resctrl = NULL; + int ret = -1; + virResctrlSchemataPtr schemata = NULL; + char *schemata_str = NULL; + char *schemata_file; + + if (virAsprintf(&resctrl, "%s/virresctrldata/linux-%s/resctrl", + abs_srcdir, data->filename) < 0) + goto cleanup; + + if (virAsprintf(&schemata_file, "%s/virresctrldata/%s-free.schemata", + abs_srcdir, virResctrlTypeToString(data->type)) < 0) + goto cleanup; + + if (virFileWrapperAddPrefix("/sys/fs/resctrl", resctrl) < 0) + goto cleanup; + + if ((schemata = virResctrlGetFreeCache(data->type)) == NULL) + goto cleanup; + + virFileWrapperClearPrefixes(); + + GetSchemataStr(schemata, &schemata_str); + + if (virTestCompareToFile(schemata_str, schemata_file) < 0) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(dir); + VIR_FREE(resctrl); + VIR_FREE(schemata_str); + virResctrlFreeSchemata(schemata); + return ret; +} + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST_FULL(filename, type) \ + do { \ + struct virResctrlData data = {filename, \ + type}; \ + if (virTestRun(filename, test_virResctrl, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_FULL("resctrl", VIR_RESCTRL_TYPE_L3); + DO_TEST_FULL("resctrl-cdp", VIR_RESCTRL_TYPE_L3_CODE); + DO_TEST_FULL("resctrl-cdp", VIR_RESCTRL_TYPE_L3_DATA); + + return ret; +} + +VIR_TEST_MAIN(mymain) -- 1.9.1
participants (3)
-
Eli Qiao
-
Eli Qiao
-
Martin Kletzander