[libvirt] [PATCH 0/2] *** Expose host's cache information in capabilities ***

This series patches are the first step to support CAT featues, which also called cache tune in libvirt. First to expose cache information which could be tuned in capabilites XML, later will add some more interface to do cache tune. Some discussion about this feature support can be found from: https://www.redhat.com/archives/libvir-list/2017-January/msg00644.html Eli Qiao (2): Resctrl: Add some utils functions Resctrl: expose cache information to capabilities include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/conf/capabilities.c | 30 +++ src/conf/capabilities.h | 15 ++ src/libvirt_private.syms | 5 + src/qemu/qemu_capabilities.c | 42 ++++ src/qemu/qemu_driver.c | 4 + src/util/virerror.c | 1 + src/util/virresctrl.c | 500 +++++++++++++++++++++++++++++++++++++++++++ src/util/virresctrl.h | 121 +++++++++++ tests/Makefile.am | 4 + 11 files changed, 724 insertions(+) create mode 100644 src/util/virresctrl.c create mode 100644 src/util/virresctrl.h -- 1.9.1

This patch adds some utils struct and functions to expose resctrl information. virResCtrlAvailable: If resctrl interface exist on host virResCtrlGet: get specify type resource contral information virResCtrlInit: initialize resctrl struct from the host's sys fs. ResCtrlAll[]: an array to maintain resource contral information. Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 4 + src/util/virerror.c | 1 + src/util/virresctrl.c | 500 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virresctrl.h | 121 +++++++++++ tests/Makefile.am | 4 + 7 files changed, 632 insertions(+) create mode 100644 src/util/virresctrl.c create mode 100644 src/util/virresctrl.h diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 2efee8f..3dd2d08 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 resource control */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/src/Makefile.am b/src/Makefile.am index d84c984..4026f8d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -162,6 +162,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/virscsivhost.c util/virscsivhost.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2d23e46..fe1334d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2295,6 +2295,10 @@ virRandomGenerateWWN; virRandomInt; +# util/virresctrl.h +virResCtrlAvailable; +virResCtrlInit; + # util/virrotatingfile.h virRotatingFileReaderConsume; virRotatingFileReaderFree; diff --git a/src/util/virerror.c b/src/util/virerror.c index ef17fb5..93dfd4f 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", + "Rescouce Control", ) diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c new file mode 100644 index 0000000..bcb47f1 --- /dev/null +++ b/src/util/virresctrl.c @@ -0,0 +1,500 @@ +/* + * virresctrl.c: methods for managing resource contral + * + * 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 <sys/ioctl.h> +#if defined HAVE_SYS_SYSCALL_H +# include <sys/syscall.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "virresctrl.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virhostcpu.h" +#include "virlog.h" +#include "virstring.h" +#include "nodeinfo.h" + +VIR_LOG_INIT("util.resctrl"); + +#define VIR_FROM_THIS VIR_FROM_RESCTRL + +#define CONSTRUCT_RESCTRL_PATH(domain_name, item_name) \ +do { \ + if (NULL == domain_name) { \ + if (asprintf(&path, "%s/%s", RESCTRL_DIR, item_name) < 0)\ + return -1; \ + } \ + else { \ + if (asprintf(&path, "%s/%s/%s", RESCTRL_DIR, \ + domain_name, \ + item_name) < 0) \ + return -1; \ + } \ +} while(0) + +static virResCtrl ResCtrlAll[] = { + { + .name = "L3", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L3DATA", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L3CODE", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L2", + .domains = NULL, + .cache_level = "l2", + }, +}; + +/* Return pointer of and ncount of schemata*/ +static virResSchemataPtr virParseSchemata(const char* schemata, int *ncount) +{ + char type[MAX_SCHEMATA_LEN]; + const char *p, *q; + int pos; + int ischemata; + virResSchemataPtr tmpschemata, pschemata; + + unsigned int socket_no = 0; + p = q = schemata; + pos = strchr(schemata, ':') - p; + + if(virStrncpy(type, schemata, pos, strlen(schemata)) == NULL) { + return NULL; + } + + *ncount = 1; + while((q = strchr(p, ';')) != 0) { + p = q + 1; + (*ncount) ++; + } + + /* allocat an arrry to keep schemata */ + if(VIR_ALLOC_N_QUIET(tmpschemata, *ncount) < 0) { + return NULL; + } + + pschemata = tmpschemata; + + p = q = schemata + pos + 1; + + char *tmp; + + while(*p != '\0'){ + if (*p == '='){ + q = p + 1; + + if (VIR_STRDUP(tmpschemata->name, type) < 0) + goto cleanup; + + tmpschemata->socket_no = socket_no++; + + while(*p != ';' && *p != '\0') p++; + + if (VIR_STRNDUP(tmp, q, p-q) < 0) + goto cleanup; + + if (virStrToLong_i(tmp, NULL, 16, &ischemata) < 0) + goto cleanup; + + VIR_FREE(tmp); + tmpschemata->schemata = ischemata; + tmpschemata ++; + } + p++; + } + + return pschemata; + +cleanup: + VIR_FREE(pschemata); + return NULL; +} + +static int virResCtrlGetStr(const char *domain_name, const char *item_name, char **ret) +{ + char *path; + int rc = 0; + + CONSTRUCT_RESCTRL_PATH(domain_name, item_name); + + if (virFileReadAll(path, MAX_FILE_LEN, ret) < 0) { + rc = -1; + goto cleanup; + } + +cleanup: + VIR_FREE(path); + return rc; +} + +static int virResCtrlGetTasks(const char *domain_name, char **pids) +{ + return virResCtrlGetStr(domain_name, "tasks", pids); +} + +static int virResCtrlGetSchemata(const int type, const char *name, char **schemata) +{ + int rc; + char *tmp, *end; + char *buf; + + if ((rc = virResCtrlGetStr(name, "schemata", &buf)) < 0) + return rc; + + tmp = strstr(buf, ResCtrlAll[type].name); + end = strchr(tmp, '\n'); + *end = '\0'; + if(VIR_STRDUP(*schemata, tmp) < 0) + rc = -1; + + VIR_FREE(buf); + return rc; +} + +static int virResCtrlGetInfoStr(const int type, const char *item, char **str) +{ + int ret = 0; + char *tmp; + char *path; + + if (asprintf(&path, "%s/%s/%s", RESCTRL_INFO_DIR, ResCtrlAll[type].name, item) < 0) + return -1; + if (virFileReadAll(path, 10, str) < 0) { + ret = -1; + goto cleanup; + } + + if ((tmp = strchr(*str, '\n'))) { + *tmp = '\0'; + } + +cleanup: + VIR_FREE(path); + return ret; +} + +static virResDomainPtr virResCtrlGetAllDomains(int type, int *len) +{ + struct dirent *ent; + DIR *dp = NULL; + int direrr; + char *schematas, *tasks; + + *len = 0; + virResDomainPtr header, tmp, tmp_pre; + header = tmp = tmp_pre = NULL; + if (virDirOpenQuiet(&dp, RESCTRL_DIR) < 0) { + if (errno == ENOENT) + return NULL; + VIR_ERROR(_("Unable to open %s (%d)"), RESCTRL_DIR, errno); + goto cleanup; + } + + /* read default domain */ + if(VIR_ALLOC(header) < 0) + return NULL; + header->pre = NULL; + header->next = NULL; + if(VIR_STRDUP(header->name, "default") < 0) + goto cleanup; + + if (virResCtrlGetSchemata(type, NULL, &schematas) < 0) { + goto cleanup; + } + header->schematas = virParseSchemata(schematas, &(header->n_sockets)); + VIR_FREE(schematas); + *len = 1; + + while ((direrr = virDirRead(dp, &ent, NULL)) > 0) { + if ((ent->d_type != DT_DIR) || STREQ(ent->d_name, "info")) + continue; + + if(virResCtrlGetTasks(ent->d_name, &tasks) < 0) + continue; + + if(VIR_ALLOC(tmp) < 0) + return NULL; + + tmp->next = NULL; + + if(header->next == NULL) + header->next = tmp; + + if(tmp_pre == NULL) + tmp->pre = header; + else { + tmp->pre = tmp_pre; + tmp_pre->next = tmp; + } + + if(VIR_STRDUP(tmp->name, ent->d_name) < 0) + goto cleanup; + + tmp_pre = tmp; + if (virResCtrlGetSchemata(type, tmp->name, &schematas) < 0) { + goto cleanup; + } + + (*len) ++; + tmp->schematas = virParseSchemata(schematas, &(tmp->n_sockets)); + tmp->tasks = tasks; + VIR_FREE(schematas); + } + return header; + +cleanup: + + VIR_DIR_CLOSE(dp); + return NULL; +} + + +static int virResCtrlGetCPUValue(const char* path, char** value) +{ + int ret = -1; + char* tmp; + + if(virFileReadAll(path, 10, value) < 0) { + goto cleanup; + } + if ((tmp = strchr(*value, '\n'))) { + *tmp = '\0'; + } + ret = 0; +cleanup: + return ret; +} + +static int virResctrlGetCPUSocketID(const size_t cpu, int* socket_id) +{ + int ret = -1; + char* physical_package_path = NULL; + char* physical_package = NULL; + if (virAsprintf(&physical_package_path, + "%s/cpu/cpu%zu/topology/physical_package_id", + SYSFS_SYSTEM_PATH, cpu) < 0) { + return -1; + } + + if(virResCtrlGetCPUValue(physical_package_path, + &physical_package) < 0) + goto cleanup; + + if (virStrToLong_i(physical_package, NULL, 0, socket_id) < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(physical_package); + VIR_FREE(physical_package_path); + return ret; +} + +static int virResCtrlGetCPUCache(const size_t cpu, int type, int *cache) +{ + int ret = -1; + char* cache_dir = NULL; + char* cache_str = NULL; + char* tmp; + int carry = -1; + + if (virAsprintf(&cache_dir, + "%s/cpu/cpu%zu/cache/index%d/size", + SYSFS_SYSTEM_PATH, cpu, type) < 0) + return -1; + + if(virResCtrlGetCPUValue(cache_dir, &cache_str) < 0) + goto cleanup; + + tmp = cache_str; + + while (*tmp != '\0') + tmp++; + if (*(tmp - 1) == 'K') { + *(tmp - 1) = '\0'; + carry = 1; + } + else if (*(tmp - 1) == 'M') { + *(tmp - 1) = '\0'; + carry = 1024; + } + + if (virStrToLong_i(cache_str, NULL, 0, cache) < 0) + goto cleanup; + + *cache = (*cache) * carry; + + if (*cache < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(cache_dir); + VIR_FREE(cache_str); + return ret; +} + +/* + * Fill cache informations for specify cache type +*/ +static int virResCtrlParseCPUCache(int type) +{ + int index = -1; + int npresent_cpus; + + if ((npresent_cpus = virHostCPUGetCount()) < 0) + return -1; + + if (type == RDT_RESOURCE_L3 + || type == RDT_RESOURCE_L3DATA + || type == RDT_RESOURCE_L3CODE) + index = 3; + else if (type == RDT_RESOURCE_L2) { + index = 2; + } + + if (index == -1) + return -1; + + for(size_t i = 0; i < npresent_cpus ; i ++) { + int s_id; + int cache_size; + + if (virResctrlGetCPUSocketID(i, &s_id) < 0) { + return -1; + } + if (ResCtrlAll[type].cpu_mask[s_id] == NULL) { + if (!(ResCtrlAll[type].cpu_mask[s_id] = virBitmapNew(npresent_cpus))) + return -1; + } + + ignore_value(virBitmapSetBit(ResCtrlAll[type].cpu_mask[s_id], i)); + + if (ResCtrlAll[type].cache_size[s_id] == 0) { + if (virResCtrlGetCPUCache(i, index, &cache_size) < 0) { + return -1; + } + ResCtrlAll[type].cache_size[s_id] = cache_size; + ResCtrlAll[type].cache_min[s_id] = cache_size / ResCtrlAll[type].cbm_len; + } + } + return 0; +} + +static int virResCtrlGetConfig(int type) +{ + int ret; + int i; + char *str; + + /* Read min_cbm_bits from resctrl. + eg: /sys/fs/resctrl/info/L3/num_closids + */ + if ((ret = virResCtrlGetInfoStr(type, "num_closids", &str)) < 0) { + return ret; + } + if (virStrToLong_i(str, NULL, 10, &ResCtrlAll[type].num_closid) < 0) { + return -1; + } + VIR_FREE(str); + + /* Read min_cbm_bits from resctrl. + eg: /sys/fs/resctrl/info/L3/cbm_mask + */ + if ((ret = virResCtrlGetInfoStr(type, "min_cbm_bits", &str)) < 0) { + return ret; + } + if (virStrToLong_i(str, NULL, 10, &ResCtrlAll[type].min_cbm_bits) < 0) { + return -1; + } + VIR_FREE(str); + + /* Read cbm_mask string from resctrl. + eg: /sys/fs/resctrl/info/L3/cbm_mask + */ + if ((ret = virResCtrlGetInfoStr(type, "cbm_mask", &str)) < 0) { + return ret; + } + + /* Calculate cbm length from the default cbm_mask. */ + ResCtrlAll[type].cbm_len = strlen(str) * 4; + VIR_FREE(str); + + /* Get all resctrl comains from /sys/fs/resctrl */ + ResCtrlAll[type].domains = virResCtrlGetAllDomains(type, &ResCtrlAll[type].num_domains); + ResCtrlAll[type].num_sockets = ResCtrlAll[type].domains->n_sockets; + + /* Get cache_left information */ + for(i = 0; i < ResCtrlAll[type].num_sockets; i++) { + ResCtrlAll[type].cpu_mask[i] = NULL; + } + + if((ret = virResCtrlParseCPUCache(type)) < 0) + return ret; + + ResCtrlAll[type].enabled = true; + + return ret; +} + +int virResCtrlInit(void) { + int i = 0; + char *tmp; + int rc = 0; + + for(i = 0; i <RDT_NUM_RESOURCES; i ++) { + if ((rc = asprintf(&tmp, "%s/%s", RESCTRL_INFO_DIR, ResCtrlAll[i].name)) < 0) { + continue; + } + if (virFileExists(tmp)) { + if ((rc = virResCtrlGetConfig(i)) < 0 ) + VIR_WARN("Ignor error while get config for %d", i); + } + + VIR_FREE(tmp); + } + return rc; +} + +bool virResCtrlAvailable(void) { + if (!virFileExists(RESCTRL_INFO_DIR)) + return false; + return true; +} + +virResCtrlPtr virResCtrlGet(int type) { + return &ResCtrlAll[type]; +} diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h new file mode 100644 index 0000000..432f191 --- /dev/null +++ b/src/util/virresctrl.h @@ -0,0 +1,121 @@ +/* + * * virrscctrl.h: methods for managing rscctrl + * * + * * Copyright (C) 2016 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" +# include "domain_conf.h" + +#define RESCTRL_DIR "/sys/fs/resctrl" +#define RESCTRL_INFO_DIR "/sys/fs/resctrl/info" +#define SYSFS_SYSTEM_PATH "/sys/devices/system" + +#define MAX_CPU_SOCKET_NUM 8 +#define MAX_CBM_BIT_LEN 32 +#define MAX_SCHEMATA_LEN 1024 +#define MAX_FILE_LEN ( 10 * 1024 * 1024) + +enum { + RDT_RESOURCE_L3, + RDT_RESOURCE_L3DATA, + RDT_RESOURCE_L3CODE, + RDT_RESOURCE_L2, + /* Must be the last */ + RDT_NUM_RESOURCES, +}; + +/** + * a virResSchemata represents a schemata object under a resource control + * domain. + */ +typedef struct _virResSchemata virResSchemata; +typedef virResSchemata *virResSchemataPtr; + +struct _virResSchemata { + char *name; + unsigned int socket_no; + int schemata; +}; + +/** + * a virResDomain represents a resource control domain. It's a double linked + * list. + */ + +typedef struct _virResDomain virResDomain; +typedef virResDomain *virResDomainPtr; + +struct _virResDomain { + char* name; + virResSchemataPtr schematas; + char* tasks; + int n_sockets; + virResDomainPtr pre; + virResDomainPtr next; +}; + +/** + * struct rdt_resource - attributes of an RDT resource + * @enabled: Is this feature enabled on this machine + * @name: Name to use in "schemata" file + * @num_closid: Number of CLOSIDs available + * @max_cbm: Largest Cache Bit Mask allowed + * @min_cbm_bits: Minimum number of consecutive bits to be set + * in a cache bit mask + * @domains: All resource domains for this resource + * @num_domains: Number of domains active + * @num_tmp_cbms: Number of CBMs in tmp_cbms + * @cache_level: Which cache level defines scope of this domain + * @num_sockets: Number of sockets on this machine. + * @cache_size: Cache size on each cpu socket in KiB. + * @cache_left: Cache left on each cpu socket in KiB. + * @cache_min: The minimum cache of allocatioin. + * @cpu_mask: cpu mask on each socket. + */ +typedef struct _virResCtrl virResCtrl; +typedef virResCtrl *virResCtrlPtr; + +struct _virResCtrl { + bool enabled; + const char *name; + int num_closid; + int cbm_len; + int min_cbm_bits; + virResDomainPtr domains; + int num_domains; + const char* cache_level; + int num_sockets; + int cache_size[MAX_CPU_SOCKET_NUM]; + int cache_left[MAX_CPU_SOCKET_NUM]; + int cache_min[MAX_CPU_SOCKET_NUM]; + virBitmapPtr cpu_mask[MAX_CPU_SOCKET_NUM]; +}; + + +bool virResCtrlAvailable(void); +int virResCtrlInit(void); +virResCtrlPtr virResCtrlGet(int); + +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index ecd04e8..e9c9abc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1185,6 +1185,10 @@ virrotatingfiletest_SOURCES = \ virrotatingfiletest_CFLAGS = $(AM_CFLAGS) virrotatingfiletest_LDADD = $(LDADDS) +virresctrltest_SOURCES = \ + virresctrltest.c testutils.h testutils.c +virresctrltest_LDADD = $(LDADDS) + if WITH_LINUX virusbtest_SOURCES = \ virusbtest.c testutils.h testutils.c -- 1.9.1

Hi Eli Qiao, On Wed, Jan 18, 2017 at 03:26:19PM +0800, Eli Qiao wrote:
This patch adds some utils struct and functions to expose resctrl information.
virResCtrlAvailable: If resctrl interface exist on host virResCtrlGet: get specify type resource contral information virResCtrlInit: initialize resctrl struct from the host's sys fs. ResCtrlAll[]: an array to maintain resource contral information.
Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- include/libvirt/virterror.h | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 4 + src/util/virerror.c | 1 + src/util/virresctrl.c | 500 ++++++++++++++++++++++++++++++++++++++++++++ src/util/virresctrl.h | 121 +++++++++++ tests/Makefile.am | 4 + 7 files changed, 632 insertions(+) create mode 100644 src/util/virresctrl.c create mode 100644 src/util/virresctrl.h
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 2efee8f..3dd2d08 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 resource control */
# ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/src/Makefile.am b/src/Makefile.am index d84c984..4026f8d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -162,6 +162,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/virscsivhost.c util/virscsivhost.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2d23e46..fe1334d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2295,6 +2295,10 @@ virRandomGenerateWWN; virRandomInt;
+# util/virresctrl.h +virResCtrlAvailable; +virResCtrlInit; + # util/virrotatingfile.h virRotatingFileReaderConsume; virRotatingFileReaderFree; diff --git a/src/util/virerror.c b/src/util/virerror.c index ef17fb5..93dfd4f 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", + "Rescouce Control",
Typo.
diff --git a/src/util/virresctrl.c b/src/util/virresctrl.c new file mode 100644 index 0000000..bcb47f1 --- /dev/null +++ b/src/util/virresctrl.c @@ -0,0 +1,500 @@ +/* + * virresctrl.c: methods for managing resource contral
control
+ * + * 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 <sys/ioctl.h> +#if defined HAVE_SYS_SYSCALL_H +# include <sys/syscall.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "virresctrl.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virhostcpu.h" +#include "virlog.h" +#include "virstring.h" +#include "nodeinfo.h" + +VIR_LOG_INIT("util.resctrl"); + +#define VIR_FROM_THIS VIR_FROM_RESCTRL + +#define CONSTRUCT_RESCTRL_PATH(domain_name, item_name) \ +do { \ + if (NULL == domain_name) { \ + if (asprintf(&path, "%s/%s", RESCTRL_DIR, item_name) < 0)\ + return -1; \ + } \ + else { \ + if (asprintf(&path, "%s/%s/%s", RESCTRL_DIR, \ + domain_name, \ + item_name) < 0) \ + return -1; \ + } \ +} while(0) + +static virResCtrl ResCtrlAll[] = { + { + .name = "L3", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L3DATA", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L3CODE", + .domains = NULL, + .cache_level = "l3", + }, + { + .name = "L2", + .domains = NULL, + .cache_level = "l2", + }, +}; + +/* Return pointer of and ncount of schemata*/ +static virResSchemataPtr virParseSchemata(const char* schemata, int *ncount) +{ + char type[MAX_SCHEMATA_LEN]; + const char *p, *q; + int pos; + int ischemata; + virResSchemataPtr tmpschemata, pschemata; + + unsigned int socket_no = 0; + p = q = schemata; + pos = strchr(schemata, ':') - p; + + if(virStrncpy(type, schemata, pos, strlen(schemata)) == NULL) { + return NULL; + } + + *ncount = 1; + while((q = strchr(p, ';')) != 0) { + p = q + 1; + (*ncount) ++; + } + + /* allocat an arrry to keep schemata */
typo
+ if(VIR_ALLOC_N_QUIET(tmpschemata, *ncount) < 0) { + return NULL; + } + + pschemata = tmpschemata; + + p = q = schemata + pos + 1; + + char *tmp; + + while(*p != '\0'){ + if (*p == '='){ + q = p + 1; + + if (VIR_STRDUP(tmpschemata->name, type) < 0) + goto cleanup; + + tmpschemata->socket_no = socket_no++;
Isnt this incorrect if there are two lines (one for data and one for code) in the schemata file?
+ while(*p != ';' && *p != '\0') p++; + + if (VIR_STRNDUP(tmp, q, p-q) < 0) + goto cleanup; + + if (virStrToLong_i(tmp, NULL, 16, &ischemata) < 0) + goto cleanup; + + VIR_FREE(tmp); + tmpschemata->schemata = ischemata; + tmpschemata ++; + } + p++; + } + + return pschemata; + +cleanup: + VIR_FREE(pschemata); + return NULL; +} + +static int virResCtrlGetStr(const char *domain_name, const char *item_name, char **ret) +{ + char *path; + int rc = 0; + + CONSTRUCT_RESCTRL_PATH(domain_name, item_name); + + if (virFileReadAll(path, MAX_FILE_LEN, ret) < 0) { + rc = -1; + goto cleanup; + } + +cleanup: + VIR_FREE(path); + return rc; +} + +static int virResCtrlGetTasks(const char *domain_name, char **pids) +{ + return virResCtrlGetStr(domain_name, "tasks", pids); +} + +static int virResCtrlGetSchemata(const int type, const char *name, char **schemata) +{ + int rc; + char *tmp, *end; + char *buf; + + if ((rc = virResCtrlGetStr(name, "schemata", &buf)) < 0) + return rc; + + tmp = strstr(buf, ResCtrlAll[type].name); + end = strchr(tmp, '\n'); + *end = '\0'; + if(VIR_STRDUP(*schemata, tmp) < 0) + rc = -1; + + VIR_FREE(buf); + return rc; +} + +static int virResCtrlGetInfoStr(const int type, const char *item, char **str) +{ + int ret = 0; + char *tmp;
+ char *path; + + if (asprintf(&path, "%s/%s/%s", RESCTRL_INFO_DIR, ResCtrlAll[type].name, item) < 0) + return -1; + if (virFileReadAll(path, 10, str) < 0) { + ret = -1; + goto cleanup; + } + + if ((tmp = strchr(*str, '\n'))) { + *tmp = '\0'; + } + +cleanup: + VIR_FREE(path); + return ret; +} + +static virResDomainPtr virResCtrlGetAllDomains(int type, int *len) +{ + struct dirent *ent; + DIR *dp = NULL; + int direrr; + char *schematas, *tasks; + + *len = 0; + virResDomainPtr header, tmp, tmp_pre; + header = tmp = tmp_pre = NULL; + if (virDirOpenQuiet(&dp, RESCTRL_DIR) < 0) { + if (errno == ENOENT) + return NULL; + VIR_ERROR(_("Unable to open %s (%d)"), RESCTRL_DIR, errno); + goto cleanup; + } + + /* read default domain */ + if(VIR_ALLOC(header) < 0) + return NULL; + header->pre = NULL; + header->next = NULL; + if(VIR_STRDUP(header->name, "default") < 0) + goto cleanup; + + if (virResCtrlGetSchemata(type, NULL, &schematas) < 0) { + goto cleanup; + } + header->schematas = virParseSchemata(schematas, &(header->n_sockets)); + VIR_FREE(schematas); + *len = 1; + + while ((direrr = virDirRead(dp, &ent, NULL)) > 0) { + if ((ent->d_type != DT_DIR) || STREQ(ent->d_name, "info")) + continue; + + if(virResCtrlGetTasks(ent->d_name, &tasks) < 0) + continue; + + if(VIR_ALLOC(tmp) < 0) + return NULL; + + tmp->next = NULL; + + if(header->next == NULL) + header->next = tmp; + + if(tmp_pre == NULL) + tmp->pre = header; + else { + tmp->pre = tmp_pre; + tmp_pre->next = tmp; + } + + if(VIR_STRDUP(tmp->name, ent->d_name) < 0) + goto cleanup; + + tmp_pre = tmp; + if (virResCtrlGetSchemata(type, tmp->name, &schematas) < 0) { + goto cleanup; + } + + (*len) ++; + tmp->schematas = virParseSchemata(schematas, &(tmp->n_sockets)); + tmp->tasks = tasks; + VIR_FREE(schematas); + } + return header; + +cleanup: + + VIR_DIR_CLOSE(dp); + return NULL; +} + + +static int virResCtrlGetCPUValue(const char* path, char** value) +{ + int ret = -1; + char* tmp; + + if(virFileReadAll(path, 10, value) < 0) { + goto cleanup; + } + if ((tmp = strchr(*value, '\n'))) { + *tmp = '\0'; + } + ret = 0; +cleanup: + return ret; +} + +static int virResctrlGetCPUSocketID(const size_t cpu, int* socket_id) +{ + int ret = -1; + char* physical_package_path = NULL; + char* physical_package = NULL; + if (virAsprintf(&physical_package_path, + "%s/cpu/cpu%zu/topology/physical_package_id", + SYSFS_SYSTEM_PATH, cpu) < 0) { + return -1; + } + + if(virResCtrlGetCPUValue(physical_package_path, + &physical_package) < 0) + goto cleanup; + + if (virStrToLong_i(physical_package, NULL, 0, socket_id) < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(physical_package); + VIR_FREE(physical_package_path); + return ret; +} + +static int virResCtrlGetCPUCache(const size_t cpu, int type, int *cache) +{ + int ret = -1; + char* cache_dir = NULL; + char* cache_str = NULL; + char* tmp; + int carry = -1; + + if (virAsprintf(&cache_dir, + "%s/cpu/cpu%zu/cache/index%d/size", + SYSFS_SYSTEM_PATH, cpu, type) < 0) + return -1; + + if(virResCtrlGetCPUValue(cache_dir, &cache_str) < 0) + goto cleanup; + + tmp = cache_str; + + while (*tmp != '\0') + tmp++; + if (*(tmp - 1) == 'K') { + *(tmp - 1) = '\0'; + carry = 1; + } + else if (*(tmp - 1) == 'M') { + *(tmp - 1) = '\0'; + carry = 1024; + } + + if (virStrToLong_i(cache_str, NULL, 0, cache) < 0) + goto cleanup; + + *cache = (*cache) * carry; + + if (*cache < 0) + goto cleanup; + + ret = 0; +cleanup: + VIR_FREE(cache_dir); + VIR_FREE(cache_str); + return ret; +} + +/* + * Fill cache informations for specify cache type +*/ +static int virResCtrlParseCPUCache(int type) +{ + int index = -1; + int npresent_cpus; + + if ((npresent_cpus = virHostCPUGetCount()) < 0) + return -1; + + if (type == RDT_RESOURCE_L3 + || type == RDT_RESOURCE_L3DATA + || type == RDT_RESOURCE_L3CODE) + index = 3; + else if (type == RDT_RESOURCE_L2) { + index = 2; + } + + if (index == -1) + return -1; + + for(size_t i = 0; i < npresent_cpus ; i ++) { + int s_id; + int cache_size; + + if (virResctrlGetCPUSocketID(i, &s_id) < 0) { + return -1; + } + if (ResCtrlAll[type].cpu_mask[s_id] == NULL) { + if (!(ResCtrlAll[type].cpu_mask[s_id] = virBitmapNew(npresent_cpus))) + return -1; + } + + ignore_value(virBitmapSetBit(ResCtrlAll[type].cpu_mask[s_id], i)); + + if (ResCtrlAll[type].cache_size[s_id] == 0) { + if (virResCtrlGetCPUCache(i, index, &cache_size) < 0) { + return -1; + } + ResCtrlAll[type].cache_size[s_id] = cache_size; + ResCtrlAll[type].cache_min[s_id] = cache_size / ResCtrlAll[type].cbm_len; + } + } + return 0; +} + +static int virResCtrlGetConfig(int type) +{ + int ret; + int i; + char *str; + + /* Read min_cbm_bits from resctrl. + eg: /sys/fs/resctrl/info/L3/num_closids + */ + if ((ret = virResCtrlGetInfoStr(type, "num_closids", &str)) < 0) { + return ret; + } + if (virStrToLong_i(str, NULL, 10, &ResCtrlAll[type].num_closid) < 0) { + return -1; + } + VIR_FREE(str); + + /* Read min_cbm_bits from resctrl. + eg: /sys/fs/resctrl/info/L3/cbm_mask + */ + if ((ret = virResCtrlGetInfoStr(type, "min_cbm_bits", &str)) < 0) { + return ret; + } + if (virStrToLong_i(str, NULL, 10, &ResCtrlAll[type].min_cbm_bits) < 0) { + return -1; + } + VIR_FREE(str); + + /* Read cbm_mask string from resctrl. + eg: /sys/fs/resctrl/info/L3/cbm_mask + */ + if ((ret = virResCtrlGetInfoStr(type, "cbm_mask", &str)) < 0) { + return ret; + } + + /* Calculate cbm length from the default cbm_mask. */ + ResCtrlAll[type].cbm_len = strlen(str) * 4; + VIR_FREE(str); + + /* Get all resctrl comains from /sys/fs/resctrl */ + ResCtrlAll[type].domains = virResCtrlGetAllDomains(type, &ResCtrlAll[type].num_domains); + ResCtrlAll[type].num_sockets = ResCtrlAll[type].domains->n_sockets; + + /* Get cache_left information */ + for(i = 0; i < ResCtrlAll[type].num_sockets; i++) { + ResCtrlAll[type].cpu_mask[i] = NULL; + } + + if((ret = virResCtrlParseCPUCache(type)) < 0) + return ret; + + ResCtrlAll[type].enabled = true; + + return ret; +} + +int virResCtrlInit(void) { + int i = 0; + char *tmp; + int rc = 0; + + for(i = 0; i <RDT_NUM_RESOURCES; i ++) { + if ((rc = asprintf(&tmp, "%s/%s", RESCTRL_INFO_DIR, ResCtrlAll[i].name)) < 0) { + continue; + } + if (virFileExists(tmp)) { + if ((rc = virResCtrlGetConfig(i)) < 0 ) + VIR_WARN("Ignor error while get config for %d", i);
Typo.
+ } + + VIR_FREE(tmp); + } + return rc; +} + +bool virResCtrlAvailable(void) { + if (!virFileExists(RESCTRL_INFO_DIR)) + return false; + return true; +} + +virResCtrlPtr virResCtrlGet(int type) { + return &ResCtrlAll[type]; +} diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h new file mode 100644 index 0000000..432f191 --- /dev/null +++ b/src/util/virresctrl.h @@ -0,0 +1,121 @@ +/* + * * virrscctrl.h: methods for managing rscctrl + * * + * * Copyright (C) 2016 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" +# include "domain_conf.h" + +#define RESCTRL_DIR "/sys/fs/resctrl" +#define RESCTRL_INFO_DIR "/sys/fs/resctrl/info" +#define SYSFS_SYSTEM_PATH "/sys/devices/system" + +#define MAX_CPU_SOCKET_NUM 8 +#define MAX_CBM_BIT_LEN 32 +#define MAX_SCHEMATA_LEN 1024 +#define MAX_FILE_LEN ( 10 * 1024 * 1024) + +enum { + RDT_RESOURCE_L3, + RDT_RESOURCE_L3DATA, + RDT_RESOURCE_L3CODE, + RDT_RESOURCE_L2, + /* Must be the last */ + RDT_NUM_RESOURCES, +}; + +/** + * a virResSchemata represents a schemata object under a resource control + * domain. + */ +typedef struct _virResSchemata virResSchemata; +typedef virResSchemata *virResSchemataPtr; + +struct _virResSchemata { + char *name; + unsigned int socket_no; + int schemata; +}; + +/** + * a virResDomain represents a resource control domain. It's a double linked + * list. + */ + +typedef struct _virResDomain virResDomain; +typedef virResDomain *virResDomainPtr; + +struct _virResDomain { + char* name; + virResSchemataPtr schematas; + char* tasks; + int n_sockets; + virResDomainPtr pre; + virResDomainPtr next; +}; + +/** + * struct rdt_resource - attributes of an RDT resource + * @enabled: Is this feature enabled on this machine + * @name: Name to use in "schemata" file + * @num_closid: Number of CLOSIDs available + * @max_cbm: Largest Cache Bit Mask allowed + * @min_cbm_bits: Minimum number of consecutive bits to be set + * in a cache bit mask + * @domains: All resource domains for this resource + * @num_domains: Number of domains active + * @num_tmp_cbms: Number of CBMs in tmp_cbms + * @cache_level: Which cache level defines scope of this domain + * @num_sockets: Number of sockets on this machine. + * @cache_size: Cache size on each cpu socket in KiB. + * @cache_left: Cache left on each cpu socket in KiB. + * @cache_min: The minimum cache of allocatioin. + * @cpu_mask: cpu mask on each socket. + */ +typedef struct _virResCtrl virResCtrl; +typedef virResCtrl *virResCtrlPtr; + +struct _virResCtrl { + bool enabled; + const char *name; + int num_closid; + int cbm_len; + int min_cbm_bits; + virResDomainPtr domains; + int num_domains; + const char* cache_level; + int num_sockets; + int cache_size[MAX_CPU_SOCKET_NUM]; + int cache_left[MAX_CPU_SOCKET_NUM]; + int cache_min[MAX_CPU_SOCKET_NUM]; + virBitmapPtr cpu_mask[MAX_CPU_SOCKET_NUM]; +}; + + +bool virResCtrlAvailable(void); +int virResCtrlInit(void); +virResCtrlPtr virResCtrlGet(int); + +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index ecd04e8..e9c9abc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1185,6 +1185,10 @@ virrotatingfiletest_SOURCES = \ virrotatingfiletest_CFLAGS = $(AM_CFLAGS) virrotatingfiletest_LDADD = $(LDADDS)
+virresctrltest_SOURCES = \ + virresctrltest.c testutils.h testutils.c +virresctrltest_LDADD = $(LDADDS) + if WITH_LINUX virusbtest_SOURCES = \ virusbtest.c testutils.h testutils.c -- 1.9.1

This patch expose cache information to host's capabilites xml. <cache> <bank id='0' type='l3' size='56320' unit='KiB' cpus='0-21,44-65' min='2816' scope='L3DATA'/> <bank id='1' type='l3' size='56320' unit='KiB' cpus='22-43,66-87' min='2816' scope='L3DATA'/> <bank id='2' type='l3' size='56320' unit='KiB' cpus='0-21,44-65' min='2816' scope='L3CODE'/> <bank id='3' type='l3' size='56320' unit='KiB' cpus='22-43,66-87' min='2816' scope='L3CODE'/> </cache> There are some nits difference from RFC on mailing list. https://www.redhat.com/archives/libvir-list/2017-January/msg00644.html Signed-off-by: Eli Qiao <liyong.qiao@intel.com> --- src/conf/capabilities.c | 30 ++++++++++++++++++++++++++++++ src/conf/capabilities.h | 15 +++++++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_capabilities.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_driver.c | 4 ++++ 5 files changed, 92 insertions(+) diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c index 9ab343b..26aef73 100644 --- a/src/conf/capabilities.c +++ b/src/conf/capabilities.c @@ -844,6 +844,31 @@ virCapabilitiesFormatNUMATopology(virBufferPtr buf, return 0; } +static int +virCapabilitiesFormatCache(virBufferPtr buf, + size_t ncachebank, + virCapsHostCacheBankPtr *cachebank) +{ + size_t i; + + virBufferAddLit(buf, "<cache>\n"); + virBufferAdjustIndent(buf, 2); + + for( i = 0 ; i < ncachebank; i++) { + virBufferAsprintf(buf, + "<bank id='%u' type='%s' size='%llu' unit='KiB' cpus='%s' min='%llu' scope='%s'/>\n", + cachebank[i]->id, + cachebank[i]->type, + cachebank[i]->size, + cachebank[i]->cpus, + cachebank[i]->min, + cachebank[i]->scope); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</cache>\n"); + return 0; +} + /** * virCapabilitiesFormatXML: * @caps: capabilities to format @@ -931,6 +956,11 @@ virCapabilitiesFormatXML(virCapsPtr caps) virBufferAddLit(&buf, "</migration_features>\n"); } + if (caps->host.ncachebank && + virCapabilitiesFormatCache(&buf, caps->host.ncachebank, + caps->host.cachebank) < 0) + return NULL; + if (caps->host.netprefix) virBufferAsprintf(&buf, "<netprefix>%s</netprefix>\n", caps->host.netprefix); diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index cfdc34a..b9f4633 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -138,6 +138,17 @@ struct _virCapsHostSecModel { virCapsHostSecModelLabelPtr labels; }; +typedef struct _virCapsHostCacheBank virCapsHostCacheBank; +typedef virCapsHostCacheBank *virCapsHostCacheBankPtr; +struct _virCapsHostCacheBank { + unsigned int id; + char* type; + char* cpus; + unsigned long long size; + unsigned long long min; + char* scope; +}; + typedef struct _virCapsHost virCapsHost; typedef virCapsHost *virCapsHostPtr; struct _virCapsHost { @@ -160,6 +171,10 @@ struct _virCapsHost { size_t nsecModels; virCapsHostSecModelPtr secModels; + size_t ncachebank; + size_t ncachebank_max; + virCapsHostCacheBankPtr *cachebank; + char *netprefix; virCPUDefPtr cpu; int nPagesSize; /* size of pagesSize array */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fe1334d..bdd717a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2298,6 +2298,7 @@ virRandomInt; # util/virresctrl.h virResCtrlAvailable; virResCtrlInit; +virResCtrlGet; # util/virrotatingfile.h virRotatingFileReaderConsume; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 5427059..99cc7ce 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -45,6 +45,7 @@ #include "qemu_domain.h" #define __QEMU_CAPSRIV_H_ALLOW__ #include "qemu_capspriv.h" +#include "virresctrl.h" #include <fcntl.h> #include <sys/stat.h> @@ -1093,7 +1094,45 @@ virQEMUCapsInitCPU(virCapsPtr caps, goto cleanup; } +static int +virQEMUCapsInitCache(virCapsPtr caps) +{ + int i, j; + unsigned bk_id = 0; + virResCtrlPtr resctrl; + virCapsHostCacheBankPtr bank; + for (i = 0; i < RDT_NUM_RESOURCES; i ++) + { + resctrl = virResCtrlGet(i); + if(resctrl->enabled) { + for( j = 0; j < resctrl->num_sockets; j++) + { + if(VIR_RESIZE_N(caps->host.cachebank, caps->host.ncachebank_max, + caps->host.ncachebank, 1) < 0) + return -1; + + if(VIR_ALLOC(bank) < 0) + return -1; + + bank->id = bk_id++; + if(VIR_STRDUP(bank->type, resctrl->cache_level) < 0) + goto err; + if(VIR_STRDUP(bank->scope, resctrl->name) < 0) + goto err; + if(VIR_STRDUP(bank->cpus, virBitmapFormat(resctrl->cpu_mask[j])) < 0) + goto err; + bank->size = resctrl->cache_size[j]; + bank->min = resctrl->cache_min[j]; + caps->host.cachebank[caps->host.ncachebank++] = bank; + } + } + } + return 0; +err: + VIR_FREE(bank); + return -1; +} static int virQEMUCapsInitPages(virCapsPtr caps) { @@ -1139,6 +1178,9 @@ virCapsPtr virQEMUCapsInit(virQEMUCapsCachePtr cache) if (virQEMUCapsInitCPU(caps, hostarch) < 0) VIR_WARN("Failed to get host CPU"); + if (virQEMUCapsInitCache(caps) < 0) + VIR_WARN("Failed to get host cache"); + /* Add the power management features of the host */ if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0) VIR_WARN("Failed to get host power management capabilities"); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b359e77..91afc34 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -105,6 +105,7 @@ #include "vircgroup.h" #include "virperf.h" #include "virnuma.h" +#include "virresctrl.h" #include "dirname.h" #include "network/bridge_driver.h" @@ -842,6 +843,9 @@ qemuStateInitialize(bool privileged, run_gid = cfg->group; } + if(virResCtrlAvailable() && virResCtrlInit() < 0) + VIR_WARN("Faild to initialize resource control."); + qemu_driver->qemuCapsCache = virQEMUCapsCacheNew(cfg->libDir, cfg->cacheDir, run_uid, -- 1.9.1

On Wed, Jan 18, 2017 at 03:26:20PM +0800, Eli Qiao wrote:
This patch expose cache information to host's capabilites xml.
<cache> <bank id='0' type='l3' size='56320' unit='KiB' cpus='0-21,44-65' min='2816' scope='L3DATA'/> <bank id='1' type='l3' size='56320' unit='KiB' cpus='22-43,66-87' min='2816' scope='L3DATA'/> <bank id='2' type='l3' size='56320' unit='KiB' cpus='0-21,44-65' min='2816' scope='L3CODE'/> <bank id='3' type='l3' size='56320' unit='KiB' cpus='22-43,66-87' min='2816' scope='L3CODE'/> </cache>
There are some nits difference from RFC on mailing list. https://www.redhat.com/archives/libvir-list/2017-January/msg00644.html
I think this is somewhat misleading - it is makes it look like we have 56320 KiB of L3 cache for DATA and 56320 KiB of L3 cache for CODE, ie 112640 KiB total per socket. IIUC, what we actually have is just 56320 KiB of L3 cache, which can be split between DATA or CODE. I think the nested syntax shows that better <cache> <bank id='0' type='l3' size='56320' unit='KiB' cpus='0-21,44-65'> <control min='2816' scope='L3DATA'/> <control min='2816' scope='L3CODE'/> </bank> <bank id='1' type='l3' size='56320' unit='KiB' cpus='22-43,66-87'> <control min='2816' scope='L3DATA'/> <control min='2816' scope='L3CODE'/> </bank> </cache> Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|
participants (3)
-
Daniel P. Berrange
-
Eli Qiao
-
Marcelo Tosatti