[libvirt] [PATCH RFC V2] RFC: Reimplement cache allocation for a VM
by Eli Qiao
This is a RFC patch for the reimplement of `support cache tune(CAT) in
libvirt`[1].
This patch defines some structs to represent data struct in linux
resctrl fs which will be used later to do cache allocation.
The patch expose a private interface `virResctrlFreeSchemata`, which
will be used to query the cache allocation on the host.
Also added unit test cases to test this interface can works well.
There are already patch sets[2] to address it, and functional
works, but people doesn't like it cause it has global variable, and
missing unit test case for new added capabilites, etc.
Martin has proposed a test infra to do vircaps2xmltest, and I extened it
on top of it to extend resctrl control[3], this is kinds of new desiged
apart from [2], so I propose this RFC patch to do some rework on it.
[1] https://www.redhat.com/archives/libvir-list/2017-January/msg00683.html
[2] https://www.redhat.com/archives/libvir-list/2017-March/msg00181.html
[3] https://www.redhat.com/archives/libvir-list/2017-April/msg00516.html
---
include/libvirt/virterror.h | 1 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 6 +
src/util/virerror.c | 1 +
src/util/virresctrl.c | 423 ++++++++++++++++++++++++++++++
src/util/virresctrl.h | 86 ++++++
tests/Makefile.am | 7 +-
tests/virresctrldata/L3-free.schemata | 1 +
tests/virresctrldata/L3CODE-free.schemata | 1 +
tests/virresctrldata/L3DATA-free.schemata | 1 +
tests/virresctrltest.c | 117 +++++++++
11 files changed, 644 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 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 60eba37..0ae2af5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -165,6 +165,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 9d7760d..b7225fe 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2396,6 +2396,12 @@ virRandomGenerateWWN;
virRandomInt;
+# util/virresctrl.h
+virResctrlFreeSchemata;
+virResctrlGetFreeCache;
+virResctrlTypeToString;
+
+
# util/virrotatingfile.h
virRotatingFileReaderConsume;
virRotatingFileReaderFree;
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..778c2ec
--- /dev/null
+++ b/src/util/virresctrl.c
@@ -0,0 +1,423 @@
+/*
+ * 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(a)intel.com>
+ */
+
+#include <config.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "virresctrl.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "viralloc.h"
+#include "virstring.h"
+#include "virfile.h"
+
+VIR_LOG_INIT("util.resctrl");
+
+#define VIR_FROM_THIS VIR_FROM_RESCTRL
+#define SYSFS_RESCTRL_PATH "/sys/fs/resctrl/"
+
+VIR_ENUM_IMPL(virResctrl, VIR_RESCTRL_TYPE_LAST,
+ "L3",
+ "L3CODE",
+ "L3DATA",
+ "L2")
+
+/**
+ * a virResctrlDomain represents a resource control group, it's a directory
+ * under /sys/fs/resctrl.
+ * eg: /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, eg: CG1. If it represent host's
+ default resource group name, should be a NULL pointer */
+ size_t n_tasks; /* number of task assigned to the resource group */
+ char **tasks; /* task list which contains task id eg: 77454 */
+
+ size_t n_schematas; /* number of schemata the resource group contains,
+ eg: 2 */
+ virResctrlSchemataPtr *schematas; /* scheamta list */
+};
+
+/* All resource control groups on this host, including default resource group */
+typedef struct _virResctrlDomain virResctrlDomain;
+typedef virResctrlDomain *virResctrlDomainPtr;
+struct _virResctrlDomain {
+ 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_schemata_items; i++)
+ VIR_FREE(ptr->schemata_items[i]);
+}
+
+static void
+virResctrlFreeGroup(virResctrlGroupPtr ptr)
+{
+ size_t i;
+
+ if (!ptr)
+ return;
+
+ for (i = 0; i < ptr->n_tasks; i++)
+ VIR_FREE(ptr->tasks[i]);
+
+ for (i = 0; i < ptr->n_schematas; i++) {
+ virResctrlFreeSchemata(ptr->schematas[i]);
+ VIR_FREE(ptr->schematas[i]);
+ }
+}
+
+static void
+virResctrlFreeDomain(virResctrlDomainPtr ptr)
+{
+ size_t i;
+
+ if (!ptr)
+ return;
+
+ for (i = 0; i < ptr->n_groups; i++) {
+ virResctrlFreeGroup(ptr->groups[i]);
+ VIR_FREE(ptr->groups[i]);
+ }
+}
+
+static int
+virResctrlCopySchemata(virResctrlSchemataPtr src,
+ virResctrlSchemataPtr *dst)
+{
+ size_t i;
+ virResctrlSchemataItemPtr schemataitem;
+ virResctrlSchemataPtr schemata;
+
+ if (VIR_ALLOC(schemata) < 0)
+ return -1;
+
+ schemata->type = src->type;
+
+ for (i = 0; i < src->n_schemata_items; i++) {
+ if (VIR_ALLOC(schemataitem) < 0)
+ goto error;
+
+ schemataitem->cache_id = src->schemata_items[i]->cache_id;
+ schemataitem->continuous_schemata = src->schemata_items[i]->continuous_schemata;
+ schemataitem->schemata = src->schemata_items[i]->schemata;
+ schemataitem->size = src->schemata_items[i]->size;
+
+ if (VIR_APPEND_ELEMENT(schemata->schemata_items,
+ schemata->n_schemata_items,
+ schemataitem) < 0)
+ goto error;
+ }
+
+ *dst = schemata;
+
+ return 0;
+
+ error:
+ virResctrlFreeSchemata(schemata);
+ return -1;
+}
+
+static int
+virResctrlGetSchemataString(virResctrlType type,
+ const char *name,
+ char **schemata)
+{
+ int rc = -1;
+ char *tmp = NULL;
+ char *end = NULL;
+ char *buf = NULL;
+ char *type_suffix = NULL;
+
+ if (virFileReadValueString(&buf,
+ SYSFS_RESCTRL_PATH "%s/schemata",
+ name ? name : "") < 0)
+ return -1;
+
+ if (virAsprintf(&type_suffix,
+ "%s:",
+ virResctrlTypeToString(type)) < 0)
+ goto cleanup;
+
+ tmp = strstr(buf, type_suffix);
+
+ if (!tmp)
+ goto cleanup;
+
+ end = strchr(tmp, '\n');
+ if (end != NULL)
+ *end = '\0';
+
+ if (VIR_STRDUP(*schemata, tmp) < 0)
+ goto cleanup;
+
+ rc = 0;
+
+ cleanup:
+ VIR_FREE(buf);
+ VIR_FREE(type_suffix);
+ return rc;
+}
+
+static int
+virResctrlLoadSchemata(const char* schemata_str,
+ virResctrlSchemataPtr schemata)
+{
+ VIR_DEBUG("%s, %p\n", schemata_str, schemata);
+
+ int ret = -1;
+ char **lists = NULL;
+ char **sms = NULL;
+ char **sis = NULL;
+ size_t i;
+ virResctrlSchemataItemPtr si;
+
+ /* parse L3:0=fffff;1=f */
+ lists = virStringSplit(schemata_str, ":", 2);
+
+ if ((!lists) || (!lists[1]))
+ goto cleanup;
+
+ /* parse 0=fffff;1=f */
+ sms = virStringSplit(lists[1], ";", 0);
+ if (!sms)
+ goto cleanup;
+
+ for (i = 0; sms[i] != NULL; i++) {
+ /* parse 0=fffff */
+ sis = virStringSplit(sms[i], "=", 2);
+ if (!sis)
+ goto cleanup;
+
+ if (VIR_ALLOC(si) < 0)
+ goto cleanup;
+
+ if (virStrToLong_ui(sis[0], NULL, 10, &si->cache_id) < 0)
+ goto cleanup;
+
+ if (virStrToLong_ui(sis[1], NULL, 16, &si->continuous_schemata) < 0)
+ goto cleanup;
+
+ si->schemata = si->continuous_schemata;
+
+ if (VIR_APPEND_ELEMENT(schemata->schemata_items,
+ schemata->n_schemata_items,
+ si) < 0)
+ goto cleanup;
+
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(si);
+ virStringListFree(lists);
+ virStringListFree(sms);
+ virStringListFree(sis);
+ return ret;
+}
+
+static int
+virResctrlLoadGroup(const char *name,
+ virResctrlDomainPtr dom)
+{
+ VIR_DEBUG("%s, %p\n", name, dom);
+
+ int ret = -1;
+ char *path = NULL;
+ char *schemata_str;
+ virResctrlType i;
+ int rv;
+ virResctrlGroupPtr grp;
+ virResctrlSchemataPtr schemata;
+
+ if ((virAsprintf(&path, "%s/%s", SYSFS_RESCTRL_PATH, name)) < 0)
+ return -1;
+
+ if (!virFileExists(path))
+ goto cleanup;
+
+ 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, name, &schemata_str);
+ if (rv < 0)
+ continue;
+
+ if (VIR_ALLOC(schemata) < 0)
+ goto cleanup;
+
+ schemata->type = i;
+
+ if (virResctrlLoadSchemata(schemata_str, schemata) < 0)
+ goto cleanup;
+
+ VIR_FREE(schemata_str);
+
+ if (VIR_APPEND_ELEMENT(grp->schematas,
+ grp->n_schematas,
+ schemata) < 0)
+ goto cleanup;
+
+ virResctrlFreeSchemata(schemata);
+ }
+
+ if (VIR_APPEND_ELEMENT(dom->groups,
+ dom->n_groups,
+ grp) < 0)
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(path);
+ virResctrlFreeGroup(grp);
+ return ret;
+}
+
+static int
+virResctrlLoadDomain(virResctrlDomainPtr dom)
+{
+ int ret = -1;
+ int rv = -1;
+ DIR *dirp = NULL;
+ char *path = NULL;
+ struct dirent *ent;
+
+ VIR_DEBUG("%s, %p\n", "", dom);
+
+ rv = virDirOpenIfExists(&dirp, SYSFS_RESCTRL_PATH);
+
+ if (rv < 0)
+ goto cleanup;
+
+ /* load default resctrl group */
+ if (virResctrlLoadGroup("", dom) < 0)
+ goto cleanup;
+
+ while ((rv = virDirRead(dirp, &ent, path)) > 0) {
+ /* only read directory in resctrl */
+ if ((ent->d_type != DT_DIR) || STREQ(ent->d_name, "info"))
+ continue;
+
+ if (virResctrlLoadGroup(ent->d_name, dom) < 0)
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ virDirClose(&dirp);
+ return ret;
+}
+
+static void
+virResctrlRefreshDom(virResctrlDomainPtr dom, virResctrlType type)
+{
+ size_t i;
+ size_t j;
+ size_t k;
+
+ virResctrlGroupPtr default_grp = NULL;
+ virResctrlGroupPtr grp = NULL;
+ virResctrlSchemataPtr schemata = NULL;
+ virResctrlSchemataItemPtr schemataitem = NULL;
+
+ default_grp = dom->groups[0];
+
+ /* We are sure that the first group is the default one */
+ for (i = 1; i < dom->n_groups; i++) {
+ grp = dom->groups[i];
+ for (j = 0; j < grp->n_schematas; j++) {
+ schemata = grp->schematas[j];
+ /* we can only calculate one type of schemata */
+ if (schemata->type != type)
+ continue;
+ for (k = 0; k < schemata->n_schemata_items; k++) {
+ schemataitem = schemata->schemata_items[k];
+ /* if the schemata = 1, ignore it */
+ if (schemataitem->continuous_schemata > 1)
+ /* calculate default schemata, it can be non-continuous */
+ default_grp->schematas[j]->schemata_items[k]->schemata &= ~(schemataitem->continuous_schemata);
+ }
+ }
+ }
+}
+
+int virResctrlGetFreeCache(virResctrlType type,
+ virResctrlSchemataPtr *schemata)
+{
+ VIR_DEBUG("%d, %p\n", type, schemata);
+ int ret = -1;
+ size_t i;
+ virResctrlDomainPtr dom = NULL;
+ virResctrlGroupPtr grp = NULL;
+
+ if (VIR_ALLOC(dom) < 0)
+ return -1;
+
+ if (virResctrlLoadDomain(dom) < 0)
+ goto cleanup;
+
+ virResctrlRefreshDom(dom, type);
+ grp = dom->groups[0];
+
+ for (i = 0; i < grp->n_schematas; i ++)
+ if (grp->schematas[i]->type == type)
+ if (virResctrlCopySchemata(grp->schematas[i], schemata) < 0)
+ goto cleanup;
+
+ if (schemata != NULL)
+ ret = 0;
+
+ cleanup:
+ virResctrlFreeDomain(dom);
+ return ret;
+}
diff --git a/src/util/virresctrl.h b/src/util/virresctrl.h
new file mode 100644
index 0000000..1b040d4
--- /dev/null
+++ b/src/util/virresctrl.h
@@ -0,0 +1,86 @@
+/*
+ * 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(a)intel.com>
+ */
+
+#ifndef __VIR_RESCTRL_H__
+# define __VIR_RESCTRL_H__
+
+#include "virutil.h"
+
+typedef enum {
+ VIR_RESCTRL_TYPE_L3,
+ VIR_RESCTRL_TYPE_L3_CODE,
+ VIR_RESCTRL_TYPE_L3_DATA,
+ VIR_RESCTRL_TYPE_L2,
+
+ VIR_RESCTRL_TYPE_LAST
+} virResctrlType;
+
+VIR_ENUM_DECL(virResctrl);
+
+/*
+ * a virResctrlSchemataItem represents one of schemata object in a
+ * resource control group.
+ * eg: 0=f
+ */
+typedef struct _virResctrlSchemataItem virResctrlSchemataItem;
+typedef virResctrlSchemataItem *virResctrlSchemataItemPtr;
+struct _virResctrlSchemataItem {
+ unsigned int cache_id; /* cache resource id, eg: 0 */
+ unsigned int continuous_schemata; /* schemata, should be a continuous bits,
+ eg: f, this schemata can be persisted
+ to sysfs */
+ unsigned int schemata; /* schemata eg: f0f, a schemata which is calculated
+ at running time */
+ unsigned long long size; /* the cache size schemata represented in B,
+ eg: (min * bits of continuous_schemata) */
+};
+
+/*
+ * 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, eg: L3 */
+ size_t n_schemata_items; /* number of schemata item, eg: 2 */
+ virResctrlSchemataItemPtr *schemata_items; /* pointer list of schemata item */
+};
+
+/* Get free cache of the host, result saved in schemata */
+int virResctrlGetFreeCache(virResctrlType type,
+ virResctrlSchemataPtr *schemata);
+
+
+/* TODO Need to first define virDomainCachetunePtr */
+/* Set cache allocation for a VM domain */
+// int virResctrlSetCacheBanks(virDomainCachetunePtr cachetune,
+// unsigned char *group_name,
+// size_t n_pids,
+// pid_t *pids);
+//
+/* remove cache allocation for a VM domain */
+// int virResctrlRemoveCacheBanks(unsigned char *group_name);
+void virResctrlFreeSchemata(virResctrlSchemataPtr ptr);
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3cc828d..0e09e43 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 \
@@ -1150,6 +1151,10 @@ vircaps2xmltest_SOURCES = \
vircaps2xmltest.c testutils.h testutils.c virfilemock.c
vircaps2xmltest_LDADD = $(LDADDS)
+virresctrltest_SOURCES = \
+ virresctrltest.c testutils.h testutils.c virfilemock.c
+virresctrltest_LDADD = $(LDADDS)
+
virnumamock_la_SOURCES = \
virnumamock.c
virnumamock_la_CFLAGS = $(AM_CFLAGS)
@@ -1157,7 +1162,7 @@ virnumamock_la_LDFLAGS = $(MOCKLIBS_LDFLAGS)
virnumamock_la_LIBADD = $(MOCKLIBS_LIBS)
else ! WITH_LINUX
-EXTRA_DIST += vircaps2xmltest.c virnumamock.c
+EXTRA_DIST += vircaps2xmltest.c virresctrltest.c virnumamock.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/virresctrltest.c b/tests/virresctrltest.c
new file mode 100644
index 0000000..4926468
--- /dev/null
+++ b/tests/virresctrltest.c
@@ -0,0 +1,117 @@
+/*
+ * 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(a)intel.com>
+ */
+
+#include <config.h>
+#include <stdlib.h>
+
+#include "testutils.h"
+#include "virbitmap.h"
+#include "virfilemock.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=%x",
+ virResctrlTypeToString(schemata->type),
+ schemata->schemata_items[0]->cache_id,
+ schemata->schemata_items[0]->schemata);
+
+ for (i = 1; i < schemata->n_schemata_items; i ++)
+ virBufferAsprintf(&buf, ";%u=%x",
+ schemata->schemata_items[i]->cache_id,
+ schemata->schemata_items[i]->schemata);
+
+ *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;
+ 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;
+
+ virFileMockAddPrefix("/sys/fs/resctrl", resctrl);
+
+ if (virResctrlGetFreeCache(data->type, &schemata) < 0)
+ goto cleanup;
+
+ GetSchemataStr(schemata, &schemata_str);
+
+ if (virTestCompareToFile(schemata_str, schemata_file) < 0)
+ goto cleanup;
+
+ virFileMockClearPrefixes();
+
+ 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_PRELOAD(mymain, abs_builddir "/.libs/virnumamock.so")
--
1.9.1
7 years, 6 months
[libvirt] Making panic great again
by Ed Swierk
The panic device is currently documented as a way for "libvirt to receive
panic notification from a QEMU guest".
This is true, but not the whole story. When a guest triggers the panic
device, QEMU pauses the guest, and libvirt takes the action specified by
on_crash. This can interfere with the guest's own crash handling actions
(e.g. writing a dump file and rebooting itself) if the guest triggers the
panic device first (as Windows does).
None of this is an obvious side effect of a notification mechanism, so the
panic device documentation should mention it. (I'll send a documentation
patch shortly.)
Nor is this a desirable side effect, for guests that are configured to deal
with crashes themselves. Sure, you can avoid using the panic device with
such guests, but then virsh list or another application using the libvirt
API to monitor domain state won't notice guest crashes. And if you still
want libvirt to take action on guests that don't do it themselves, then you
have to be careful to include the panic device only for those domains.
Ideally libvirt would offer (1) a state indicating "this guest crashed and
needs help" independently of triggering an action, and (2) a way to trigger
an action only when needed to recover from the crash, excluding guests that
deal with their own crashes.
Sadly pvpanic and the HyperV crash MSR convey only that the guest crashed,
not whether the guest is configured to take some action on its own. So
there's no way to know precisely that a crashed (and not paused) guest is
in need of assistance.
But a state indicating "this guest crashed N minutes ago and hasn't
rebooted itself" would be a useful approximation. And triggering an action
N minutes after a guest crash if it hasn't rebooted itself in the meantime
would make it easy to cap the downtime of crashed domains. Both could be
implemented without changing either QEMU or panic device semantics.
Does this seem useful to anyone else?
--Ed
7 years, 6 months
[libvirt] [PATCH] virsh-domain-monitor: add human readable output for 'domblkinfo'.
by Julio Faracco
The virsh command 'domblkinfo' returns the capacity, allocation and phisycal
size of the devices attached in a domain. Usually, this sizes are very big
and hard to understand and calculate. This commits introduce a human readable
support to check the size of each field easilly.
For example, the command today:
virsh # domblkinfo my_domain hda
Capacity: 21474836480
Allocation: 14875545600
Physical: 21474836480
After this patch:
virsh # domblkinfo my_domain hda --human
Capacity: 20.0G
Allocation: 13.9G
Physical: 20.0G
This commit supports Bytes, KiB and MiB too.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1330940
Signed-off-by: Julio Faracco <jcfaracco(a)gmail.com>
---
tools/virsh-domain-monitor.c | 41 ++++++++++++++++++++++++++++++++++++++---
tools/virsh.pod | 5 +++--
2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c
index 3db4795..755d740 100644
--- a/tools/virsh-domain-monitor.c
+++ b/tools/virsh-domain-monitor.c
@@ -396,6 +396,10 @@ static const vshCmdOptDef opts_domblkinfo[] = {
.flags = VSH_OFLAG_REQ,
.help = N_("block device")
},
+ {.name = "human",
+ .type = VSH_OT_BOOL,
+ .help = N_("Human readable output")
+ },
{.name = NULL}
};
@@ -405,6 +409,7 @@ cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
virDomainBlockInfo info;
virDomainPtr dom;
bool ret = false;
+ bool human = false;
const char *device = NULL;
if (!(dom = virshCommandOptDomain(ctl, cmd, NULL)))
@@ -416,9 +421,39 @@ cmdDomblkinfo(vshControl *ctl, const vshCmd *cmd)
if (virDomainGetBlockInfo(dom, device, &info, 0) < 0)
goto cleanup;
- vshPrint(ctl, "%-15s %llu\n", _("Capacity:"), info.capacity);
- vshPrint(ctl, "%-15s %llu\n", _("Allocation:"), info.allocation);
- vshPrint(ctl, "%-15s %llu\n", _("Physical:"), info.physical);
+ human = vshCommandOptBool(cmd, "human");
+
+ if (!human) {
+ vshPrint(ctl, "%-15s %llu\n", _("Capacity:"), info.capacity);
+ vshPrint(ctl, "%-15s %llu\n", _("Allocation:"), info.allocation);
+ vshPrint(ctl, "%-15s %llu\n", _("Physical:"), info.physical);
+ } else {
+ double sizeCapacity = 1;
+ const char *sizeStr = "B";
+
+ /* Check if capacity can be in K. */
+ if (info.capacity >= 1024) {
+ sizeCapacity = 1024.0;
+ sizeStr = "K";
+ }
+ /* Check if capacity can be in M. */
+ if (info.capacity >= 1048576) {
+ sizeCapacity = 1048576.0;
+ sizeStr = "M";
+ }
+ /* Check if capacity can be in G. */
+ if (info.capacity >= 1073741824) {
+ sizeCapacity = 1073741824.0;
+ sizeStr = "G";
+ }
+
+ vshPrint(ctl, "%-15s %.1f%s\n", _("Capacity:"),
+ info.capacity/sizeCapacity, sizeStr);
+ vshPrint(ctl, "%-15s %.1f%s\n", _("Allocation:"),
+ info.allocation/sizeCapacity, sizeStr);
+ vshPrint(ctl, "%-15s %.1f%s\n", _("Physical:"),
+ info.physical/sizeCapacity, sizeStr);
+ }
ret = true;
diff --git a/tools/virsh.pod b/tools/virsh.pod
index e16f62f..9f67de2 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -841,12 +841,13 @@ B<domstate> command says that a domain was paused due to I/O error.
The B<domblkerror> command lists all block devices in error state and
the error seen on each of them.
-=item B<domblkinfo> I<domain> I<block-device>
+=item B<domblkinfo> I<domain> I<block-device> [I<--human>]
Get block device size info for a domain. A I<block-device> corresponds
to a unique target name (<target dev='name'/>) or source file (<source
file='name'/>) for one of the disk devices attached to I<domain> (see
-also B<domblklist> for listing these names).
+also B<domblklist> for listing these names). If I<--human> is set, the
+output will have a human readable design.
=item B<domblklist> I<domain> [I<--inactive>] [I<--details>]
--
2.7.4
7 years, 6 months
[libvirt] [PATCH] qemu: Adding 'downscript' feature for QEMU network interfaces.
by Julio Faracco
This commit adds the support for 'downscript' feature:
- For QEMU command line with the option:
'-net downscript=/etc/qemu-ifdown,...'.
- For Domains with a network interface description:
'<interface type='ethernet'>
...
<downscript path='/etc/qemu-ifdown'/>
...
</interface>'
The options 'script' and 'downscript' accept the argument 'no' to disable
the script executions. The way that the code was implemented, the XML file
accepts '<[down]script path='no'>' to solve this problem.
This commit updates the tests too.
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=825939
Signed-off-by: Julio Faracco <jcfaracco(a)gmail.com>
---
docs/schemas/domaincommon.rng | 8 ++++++++
src/conf/domain_conf.c | 13 +++++++++++++
src/conf/domain_conf.h | 1 +
src/qemu/qemu_parse_command.c | 4 ++++
tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.args | 2 +-
tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.xml | 1 +
tests/qemuargv2xmldata/qemuargv2xml-net-eth.args | 2 +-
tests/qemuargv2xmldata/qemuargv2xml-net-eth.xml | 1 +
tests/qemuxml2argvdata/qemuxml2argv-net-eth-ifname.xml | 1 +
tests/qemuxml2argvdata/qemuxml2argv-net-eth.xml | 1 +
tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth-ifname.xml | 1 +
tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth.xml | 1 +
12 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 281309e..2f88dda 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -2609,6 +2609,14 @@
</element>
</optional>
<optional>
+ <element name="downscript">
+ <attribute name="path">
+ <ref name="filePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <optional>
<element name="backenddomain">
<attribute name="name">
<ref name="domainName"/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 0ff216e..32d5720 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -1935,6 +1935,7 @@ virDomainNetDefClear(virDomainNetDefPtr def)
VIR_FREE(def->backend.vhost);
VIR_FREE(def->virtPortProfile);
VIR_FREE(def->script);
+ VIR_FREE(def->downscript);
VIR_FREE(def->domain_name);
VIR_FREE(def->ifname);
VIR_FREE(def->ifname_guest);
@@ -9589,6 +9590,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
char *ifname_guest = NULL;
char *ifname_guest_actual = NULL;
char *script = NULL;
+ char *downscript = NULL;
char *address = NULL;
char *port = NULL;
char *localaddr = NULL;
@@ -9761,6 +9763,9 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
} else if (!script &&
xmlStrEqual(cur->name, BAD_CAST "script")) {
script = virXMLPropString(cur, "path");
+ } else if (!downscript &&
+ xmlStrEqual(cur->name, BAD_CAST "downscript")) {
+ downscript = virXMLPropString(cur, "path");
} else if (!domain_name &&
xmlStrEqual(cur->name, BAD_CAST "backenddomain")) {
domain_name = virXMLPropString(cur, "name");
@@ -10074,6 +10079,10 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
def->script = script;
script = NULL;
}
+ if (downscript != NULL) {
+ def->downscript = downscript;
+ downscript = NULL;
+ }
if (domain_name != NULL) {
def->domain_name = domain_name;
domain_name = NULL;
@@ -10356,6 +10365,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
VIR_FREE(dev);
virDomainActualNetDefFree(actual);
VIR_FREE(script);
+ VIR_FREE(downscript);
VIR_FREE(bridge);
VIR_FREE(model);
VIR_FREE(backend);
@@ -22158,6 +22168,9 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferEscapeString(buf, "<script path='%s'/>\n",
def->script);
+ if (def->downscript)
+ virBufferEscapeString(buf, "<downscript path='%s'/>\n",
+ def->downscript);
virBufferEscapeString(buf, "<backenddomain name='%s'/>\n", def->domain_name);
if (def->ifname &&
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 09fb7aa..9deca76 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1024,6 +1024,7 @@ struct _virDomainNetDef {
unsigned long sndbuf;
} tune;
char *script;
+ char *downscript;
char *domain_name; /* backend domain name */
char *ifname; /* interface name on the host (<target dev='x'/>) */
virNetDevIPInfo hostIP;
diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c
index af9063c..d773917 100644
--- a/src/qemu/qemu_parse_command.c
+++ b/src/qemu/qemu_parse_command.c
@@ -1060,6 +1060,10 @@ qemuParseCommandLineNet(virDomainXMLOptionPtr xmlopt,
def->script = values[i];
values[i] = NULL;
} else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
+ STREQ(keywords[i], "downscript") && STRNEQ(values[i], "")) {
+ def->downscript = values[i];
+ values[i] = NULL;
+ } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
STREQ(keywords[i], "ifname")) {
def->ifname = values[i];
values[i] = NULL;
diff --git a/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.args b/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.args
index 4d74ae4..5d7129c 100644
--- a/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.args
+++ b/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.args
@@ -18,6 +18,6 @@ QEMU_AUDIO_DRV=none \
-usb \
-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=ide,bus=0,unit=0 \
-net nic,macaddr=00:11:22:33:44:55,vlan=0,model=rtl8139,name=net0 \
--net tap,ifname=nic02,script=/etc/qemu-ifup,vlan=0,name=hostnet0 \
+-net tap,ifname=nic02,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown,vlan=0,name=hostnet0 \
-serial none \
-parallel none
diff --git a/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.xml b/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.xml
index fa9a892..8e04efb 100644
--- a/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.xml
+++ b/tests/qemuargv2xmldata/qemuargv2xml-net-eth-ifname.xml
@@ -30,6 +30,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<target dev='nic02'/>
<model type='rtl8139'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
diff --git a/tests/qemuargv2xmldata/qemuargv2xml-net-eth.args b/tests/qemuargv2xmldata/qemuargv2xml-net-eth.args
index 89eb4c1..0e3fa21 100644
--- a/tests/qemuargv2xmldata/qemuargv2xml-net-eth.args
+++ b/tests/qemuargv2xmldata/qemuargv2xml-net-eth.args
@@ -18,6 +18,6 @@ QEMU_AUDIO_DRV=none \
-usb \
-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=ide,bus=0,unit=0 \
-net nic,macaddr=00:11:22:33:44:55,vlan=0,model=rtl8139,name=net0 \
--net tap,script=/etc/qemu-ifup,vlan=0,name=hostnet0 \
+-net tap,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown,vlan=0,name=hostnet0 \
-serial none \
-parallel none
diff --git a/tests/qemuargv2xmldata/qemuargv2xml-net-eth.xml b/tests/qemuargv2xmldata/qemuargv2xml-net-eth.xml
index 57c4be8..d177ca8 100644
--- a/tests/qemuargv2xmldata/qemuargv2xml-net-eth.xml
+++ b/tests/qemuargv2xmldata/qemuargv2xml-net-eth.xml
@@ -30,6 +30,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<model type='rtl8139'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-eth-ifname.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-eth-ifname.xml
index dd0d752..21d8259 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-net-eth-ifname.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-eth-ifname.xml
@@ -26,6 +26,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<target dev='nic02'/>
<model type='rtl8139'/>
</interface>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-eth.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-eth.xml
index 48acadf..9f40122 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-net-eth.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-eth.xml
@@ -26,6 +26,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<model type='rtl8139'/>
</interface>
<input type='mouse' bus='ps2'/>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth-ifname.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth-ifname.xml
index c36baa0..b71fd5a 100644
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth-ifname.xml
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth-ifname.xml
@@ -30,6 +30,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<target dev='nic02'/>
<model type='rtl8139'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth.xml
index 898bda6..c55cd68 100644
--- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth.xml
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-net-eth.xml
@@ -30,6 +30,7 @@
<interface type='ethernet'>
<mac address='00:11:22:33:44:55'/>
<script path='/etc/qemu-ifup'/>
+ <downscript path='/etc/qemu-ifdown'/>
<model type='rtl8139'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
--
2.7.4
7 years, 6 months