97a0425e04
xen-4.5.1-testing-src.tar.bz2 - Dropped patches now contained in tarball 556c2cf2-x86-don-t-crash-mapping-a-page-using-EFI-rt-page-tables.patch 556d9718-efi-fix-allocation-problems-if-ExitBootServices-fails.patch 556eabf7-x86-apic-Disable-the-LAPIC-later-in-smp_send_stop.patch 556eac15-x86-crash-don-t-use-set_fixmap-in-the-crash-path.patch 55780aaa-efi-avoid-calling-boot-services-after-ExitBootServices.patch 55780aff-x86-EFI-fix-EFI_MEMORY_WP-handling.patch 55780b43-EFI-early-add-mapbs-to-map-EfiBootServices-Code-Data.patch 55780b97-EFI-support-default-attributes-to-map-Runtime-service-areas.patch - Replace 5124efbe-add-qxl-support.patch with the variant that finally made it upstream, 554cc211-libxl-add-qxl.patch - bsc#931627 - VUL-0: CVE-2015-4105: XSA-130: xen: Guest triggerable qemu MSI-X pass-through error messages qemu-MSI-X-latch-writes.patch - bsc#907514 - Bus fatal error & sles12 sudden reboot has been observed - bsc#910258 - SLES12 Xen host crashes with FATAL NMI after shutdown of guest with VT-d NIC - bsc#918984 - Bus fatal error & sles11-SP4 sudden reboot has been observed - bsc#923967 - Partner-L3: Bus fatal error & sles11-SP3 sudden reboot has been observed x86-MSI-X-teardown.patch x86-MSI-X-enable.patch x86-MSI-X-guest-mask.patch x86-MSI-X-maskall.patch qemu-MSI-X-enable-maskall.patch OBS-URL: https://build.opensuse.org/package/show/Virtualization/xen?expand=0&rev=364
610 lines
19 KiB
Diff
610 lines
19 KiB
Diff
Index: xen-4.5.1-testing/tools/libxl/libxl_dm.c
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/libxl/libxl_dm.c
|
|
+++ xen-4.5.1-testing/tools/libxl/libxl_dm.c
|
|
@@ -445,6 +445,15 @@ static char ** libxl__build_device_model
|
|
flexarray_append(dm_args, "-mon");
|
|
flexarray_append(dm_args, "chardev=libxl-cmd,mode=control");
|
|
|
|
+ flexarray_append(dm_args, "-chardev");
|
|
+ flexarray_append(dm_args,
|
|
+ libxl__sprintf(gc, "socket,id=libxenstat-cmd,"
|
|
+ "path=%s/qmp-libxenstat-%d,server,nowait",
|
|
+ libxl__run_dir_path(), guest_domid));
|
|
+
|
|
+ flexarray_append(dm_args, "-mon");
|
|
+ flexarray_append(dm_args, "chardev=libxenstat-cmd,mode=control");
|
|
+
|
|
for (i = 0; i < guest_config->num_channels; i++) {
|
|
connection = guest_config->channels[i].connection;
|
|
devid = guest_config->channels[i].devid;
|
|
Index: xen-4.5.1-testing/tools/libxl/libxl_qmp.c
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/libxl/libxl_qmp.c
|
|
+++ xen-4.5.1-testing/tools/libxl/libxl_qmp.c
|
|
@@ -723,6 +723,13 @@ void libxl__qmp_cleanup(libxl__gc *gc, u
|
|
LOGE(ERROR, "Failed to remove QMP socket file %s", qmp_socket);
|
|
}
|
|
}
|
|
+
|
|
+ qmp_socket = GCSPRINTF("%s/qmp-libxenstat-%d", libxl__run_dir_path(), domid);
|
|
+ if (unlink(qmp_socket) == -1) {
|
|
+ if (errno != ENOENT) {
|
|
+ LOGE(ERROR, "Failed to remove QMP socket file %s", qmp_socket);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
int libxl__qmp_query_serial(libxl__qmp_handler *qmp)
|
|
Index: xen-4.5.1-testing/tools/xenstat/libxenstat/Makefile
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/xenstat/libxenstat/Makefile
|
|
+++ xen-4.5.1-testing/tools/xenstat/libxenstat/Makefile
|
|
@@ -24,7 +24,7 @@ MINOR=0
|
|
LIB=src/libxenstat.a
|
|
SHLIB=src/libxenstat.so.$(MAJOR).$(MINOR)
|
|
SHLIB_LINKS=src/libxenstat.so.$(MAJOR) src/libxenstat.so
|
|
-OBJECTS-y=src/xenstat.o
|
|
+OBJECTS-y=src/xenstat.o src/xenstat_qmp.o
|
|
OBJECTS-$(CONFIG_Linux) += src/xenstat_linux.o
|
|
OBJECTS-$(CONFIG_SunOS) += src/xenstat_solaris.o
|
|
OBJECTS-$(CONFIG_NetBSD) += src/xenstat_netbsd.o
|
|
@@ -32,7 +32,7 @@ OBJECTS-$(CONFIG_FreeBSD) += src/xenstat
|
|
SONAME_FLAGS=-Wl,$(SONAME_LDFLAG) -Wl,libxenstat.so.$(MAJOR)
|
|
|
|
CFLAGS+=-fPIC
|
|
-CFLAGS+=-Isrc $(CFLAGS_libxenctrl) $(CFLAGS_libxenstore) $(CFLAGS_xeninclude)
|
|
+CFLAGS+=-Isrc $(CFLAGS_libxenctrl) $(CFLAGS_libxenstore) $(CFLAGS_xeninclude) -include $(XEN_ROOT)/tools/config.h
|
|
|
|
LDLIBS-y = $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl)
|
|
LDLIBS-$(CONFIG_SunOS) += -lkstat
|
|
Index: xen-4.5.1-testing/tools/xenstat/xentop/Makefile
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/xenstat/xentop/Makefile
|
|
+++ xen-4.5.1-testing/tools/xenstat/xentop/Makefile
|
|
@@ -19,7 +19,7 @@ all install xentop:
|
|
else
|
|
|
|
CFLAGS += -DGCC_PRINTF -Werror $(CFLAGS_libxenstat)
|
|
-LDLIBS += $(LDLIBS_libxenstat) $(CURSES_LIBS) $(SOCKET_LIBS) -lm
|
|
+LDLIBS += $(LDLIBS_libxenstat) $(CURSES_LIBS) $(SOCKET_LIBS) -lm -lyajl
|
|
CFLAGS += -DHOST_$(XEN_OS)
|
|
|
|
# Include configure output (config.h) to headers search path
|
|
Index: xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_priv.h
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/xenstat/libxenstat/src/xenstat_priv.h
|
|
+++ xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_priv.h
|
|
@@ -109,5 +109,7 @@ extern int xenstat_collect_networks(xens
|
|
extern void xenstat_uninit_networks(xenstat_handle * handle);
|
|
extern int xenstat_collect_vbds(xenstat_node * node);
|
|
extern void xenstat_uninit_vbds(xenstat_handle * handle);
|
|
+extern void read_attributes_qdisk(xenstat_node * node);
|
|
+extern xenstat_vbd *xenstat_save_vbd(xenstat_domain * domain, xenstat_vbd * vbd);
|
|
|
|
#endif /* XENSTAT_PRIV_H */
|
|
Index: xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat.c
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/xenstat/libxenstat/src/xenstat.c
|
|
+++ xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat.c
|
|
@@ -657,6 +657,27 @@ static void xenstat_uninit_xen_version(x
|
|
* VBD functions
|
|
*/
|
|
|
|
+/* Save VBD information */
|
|
+xenstat_vbd *xenstat_save_vbd(xenstat_domain *domain, xenstat_vbd *vbd)
|
|
+{
|
|
+ xenstat_vbd *vbds = domain->vbds;
|
|
+
|
|
+ domain->num_vbds++;
|
|
+ domain->vbds = realloc(domain->vbds,
|
|
+ domain->num_vbds *
|
|
+ sizeof(xenstat_vbd));
|
|
+
|
|
+ if (domain->vbds == NULL) {
|
|
+ domain->num_vbds = 0;
|
|
+ free(vbds);
|
|
+ }
|
|
+ else {
|
|
+ domain->vbds[domain->num_vbds - 1] = *vbd;
|
|
+ }
|
|
+
|
|
+ return domain->vbds;
|
|
+}
|
|
+
|
|
/* Free VBD information */
|
|
static void xenstat_free_vbds(xenstat_node * node)
|
|
{
|
|
Index: xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_linux.c
|
|
===================================================================
|
|
--- xen-4.5.1-testing.orig/tools/xenstat/libxenstat/src/xenstat_linux.c
|
|
+++ xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_linux.c
|
|
@@ -417,6 +417,9 @@ int xenstat_collect_vbds(xenstat_node *
|
|
}
|
|
}
|
|
|
|
+ /* Get qdisk statistics */
|
|
+ read_attributes_qdisk(node);
|
|
+
|
|
rewinddir(priv->sysfsvbd);
|
|
|
|
for(dp = readdir(priv->sysfsvbd); dp != NULL ;
|
|
@@ -477,18 +480,10 @@ int xenstat_collect_vbds(xenstat_node *
|
|
continue;
|
|
}
|
|
|
|
- if (domain->vbds == NULL) {
|
|
- domain->num_vbds = 1;
|
|
- domain->vbds = malloc(sizeof(xenstat_vbd));
|
|
- } else {
|
|
- domain->num_vbds++;
|
|
- domain->vbds = realloc(domain->vbds,
|
|
- domain->num_vbds *
|
|
- sizeof(xenstat_vbd));
|
|
- }
|
|
- if (domain->vbds == NULL)
|
|
+ if ((xenstat_save_vbd(domain, &vbd)) == NULL) {
|
|
+ perror("Allocation error");
|
|
return 0;
|
|
- domain->vbds[domain->num_vbds - 1] = vbd;
|
|
+ }
|
|
}
|
|
|
|
return 1;
|
|
Index: xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_qmp.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-4.5.1-testing/tools/xenstat/libxenstat/src/xenstat_qmp.c
|
|
@@ -0,0 +1,451 @@
|
|
+/* libxenstat: statistics-collection library for Xen
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <fcntl.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/socket.h>
|
|
+#include <sys/poll.h>
|
|
+#include <sys/un.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <xenctrl.h>
|
|
+
|
|
+#include "xenstat_priv.h"
|
|
+
|
|
+#ifdef HAVE_YAJL_YAJL_VERSION_H
|
|
+# include <yajl/yajl_version.h>
|
|
+#endif
|
|
+
|
|
+/* YAJL version check */
|
|
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
|
|
+# define HAVE_YAJL_V2 1
|
|
+#endif
|
|
+
|
|
+#ifdef HAVE_YAJL_V2
|
|
+
|
|
+#include <yajl/yajl_tree.h>
|
|
+
|
|
+static unsigned char *qmp_query(int, char *);
|
|
+
|
|
+enum query_blockstats {
|
|
+ QMP_STATS_RETURN = 0,
|
|
+ QMP_STATS_DEVICE = 1,
|
|
+ QMP_STATS = 2,
|
|
+ QMP_RD_BYTES = 3,
|
|
+ QMP_WR_BYTES = 4,
|
|
+ QMP_RD_OPERATIONS = 5,
|
|
+ QMP_WR_OPERATIONS = 6,
|
|
+};
|
|
+
|
|
+enum query_block {
|
|
+ QMP_BLOCK_RETURN = 0,
|
|
+ QMP_BLOCK_DEVICE = 1,
|
|
+ QMP_INSERTED = 2,
|
|
+ QMP_FILE = 3,
|
|
+};
|
|
+
|
|
+
|
|
+/* Given the qmp device name, get the image filename associated with it
|
|
+ QMP Syntax for querying block infomation:
|
|
+ In: { "execute": "query-block" }
|
|
+ Out: {"return": [{
|
|
+ "device": 'str, "locked": 'bool', "removable": bool,
|
|
+ "inserted": {
|
|
+ "iops_rd": 'int',
|
|
+ "image": {
|
|
+ "virtual-size": 'int', "filename": 'str', "cluster-size": 'int',
|
|
+ "format": 'str', "actual-size": 'int', "dirty-flag": 'bool'
|
|
+ },
|
|
+ "iops_wr": 'int', "ro": 'bool', "backing_file_depth": 'int',
|
|
+ "drv": 'str', "iops": 'int', "bps_wr": 'int', "encrypted": 'bool',
|
|
+ "bps": 'int', "bps_rd": 'int',
|
|
+ "file": 'str', "encryption_key_missing": 'bool'
|
|
+ },
|
|
+ "type": 'str'
|
|
+ }]}
|
|
+*/
|
|
+static char *qmp_get_block_image(xenstat_node *node, char *qmp_devname, int qfd)
|
|
+{
|
|
+ char *tmp, *file = NULL;
|
|
+ char *query_block_cmd = "{ \"execute\": \"query-block\" }";
|
|
+ static const char *const qblock[] = {
|
|
+ [ QMP_BLOCK_RETURN ] = "return",
|
|
+ [ QMP_BLOCK_DEVICE ] = "device",
|
|
+ [ QMP_INSERTED ] = "inserted",
|
|
+ [ QMP_FILE ] = "file",
|
|
+ };
|
|
+ const char *ptr[] = {0, 0};
|
|
+ unsigned char *qmp_stats;
|
|
+ yajl_val info, ret_obj, dev_obj, n;
|
|
+ int i;
|
|
+
|
|
+ if ((qmp_stats = qmp_query(qfd, query_block_cmd)) == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ /* Use libyajl version 2.0.3 or newer for the tree parser feature with bug fixes */
|
|
+ if ((info = yajl_tree_parse((char *)qmp_stats, NULL, 0)) == NULL) {
|
|
+ free(qmp_stats);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ptr[0] = qblock[QMP_BLOCK_RETURN]; /* "return" */
|
|
+ if ((ret_obj = yajl_tree_get(info, ptr, yajl_t_array)) == NULL)
|
|
+ goto done;
|
|
+
|
|
+ for (i=0; i<YAJL_GET_ARRAY(ret_obj)->len; i++) {
|
|
+ n = YAJL_GET_ARRAY(ret_obj)->values[i];
|
|
+
|
|
+ ptr[0] = qblock[QMP_BLOCK_DEVICE]; /* "device" */
|
|
+ if ((dev_obj = yajl_tree_get(n, ptr, yajl_t_any)) != NULL) {
|
|
+ tmp = YAJL_GET_STRING(dev_obj);
|
|
+ if (strcmp(qmp_devname, tmp))
|
|
+ continue;
|
|
+ }
|
|
+ else
|
|
+ continue;
|
|
+
|
|
+ ptr[0] = qblock[QMP_INSERTED]; /* "inserted" */
|
|
+ n = yajl_tree_get(n, ptr, yajl_t_any);
|
|
+ if (n) {
|
|
+ ptr[0] = qblock[QMP_FILE]; /* "file" */
|
|
+ n = yajl_tree_get(n, ptr, yajl_t_any);
|
|
+ if (n && YAJL_IS_STRING(n)) {
|
|
+ tmp = YAJL_GET_STRING(n);
|
|
+ file = malloc(strlen(tmp)+1);
|
|
+ if (file != NULL)
|
|
+ strcpy(file, tmp);
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+done:
|
|
+ yajl_tree_free(info);
|
|
+ return file;
|
|
+}
|
|
+
|
|
+
|
|
+/* Given a QMP device name, lookup the associated xenstore qdisk device id */
|
|
+static void lookup_xenstore_devid(xenstat_node * node, unsigned int domid, char *qmp_devname,
|
|
+ int qfd, unsigned int *dev, unsigned int *sector_size)
|
|
+{
|
|
+ char **dev_ids, *tmp, *ptr, *image, path[80];
|
|
+ unsigned int num_dev_ids;
|
|
+ int i, devid;
|
|
+
|
|
+ /* Get all the qdisk dev IDs associated with the this VM */
|
|
+ snprintf(path, sizeof(path),"/local/domain/0/backend/qdisk/%i", domid);
|
|
+ dev_ids = xs_directory(node->handle->xshandle, XBT_NULL, path, &num_dev_ids);
|
|
+ if (dev_ids == NULL) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Get the filename of the image associated with this QMP device */
|
|
+ image = qmp_get_block_image(node, qmp_devname, qfd);
|
|
+ if (image == NULL) {
|
|
+ free(dev_ids);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Look for a matching image in xenstore */
|
|
+ for (i=0; i<num_dev_ids; i++) {
|
|
+ devid = atoi(dev_ids[i]);
|
|
+ /* Get the xenstore name of the image */
|
|
+ snprintf(path, sizeof(path),"/local/domain/0/backend/qdisk/%i/%i/params", domid, devid);
|
|
+ if ((ptr = xs_read(node->handle->xshandle, XBT_NULL, path, NULL)) == NULL)
|
|
+ continue;
|
|
+
|
|
+ /* Get to actual path in string */
|
|
+ if ((tmp = strchr(ptr, '/')) == NULL)
|
|
+ tmp = ptr;
|
|
+ if (!strcmp(tmp,image)) {
|
|
+ *dev = devid;
|
|
+ free(ptr);
|
|
+
|
|
+ /* Get the xenstore sector size of the image while we're here */
|
|
+ snprintf(path, sizeof(path),"/local/domain/0/backend/qdisk/%i/%i/sector-size", domid, devid);
|
|
+ if ((ptr = xs_read(node->handle->xshandle, XBT_NULL, path, NULL)) != NULL) {
|
|
+ *sector_size = atoi((char *)ptr);
|
|
+ free(ptr);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ free(ptr);
|
|
+ }
|
|
+
|
|
+ free(image);
|
|
+ free(dev_ids);
|
|
+}
|
|
+
|
|
+/* Parse the stats buffer which contains I/O data for all the disks belonging to domid */
|
|
+static void qmp_parse_stats(xenstat_node *node, unsigned int domid, unsigned char *stats_buf, int qfd)
|
|
+{
|
|
+ char *qmp_devname;
|
|
+ static const char *const qstats[] = {
|
|
+ [ QMP_STATS_RETURN ] = "return",
|
|
+ [ QMP_STATS_DEVICE ] = "device",
|
|
+ [ QMP_STATS ] = "stats",
|
|
+ [ QMP_RD_BYTES ] = "rd_bytes",
|
|
+ [ QMP_WR_BYTES ] = "wr_bytes",
|
|
+ [ QMP_RD_OPERATIONS ] = "rd_operations",
|
|
+ [ QMP_WR_OPERATIONS ] = "wr_operations",
|
|
+ };
|
|
+ const char *ptr[] = {0, 0};
|
|
+ yajl_val info, ret_obj, stats_obj, n;
|
|
+ xenstat_vbd vbd;
|
|
+ xenstat_domain *domain;
|
|
+ unsigned int sector_size = 512;
|
|
+ int i, j;
|
|
+
|
|
+ /* Use libyajl version 2.0.3 or newer for the tree parser feature */
|
|
+ if ((info = yajl_tree_parse((char *)stats_buf, NULL, 0)) == NULL)
|
|
+ return;
|
|
+
|
|
+ ptr[0] = qstats[QMP_STATS_RETURN]; /* "return" */
|
|
+ if ((ret_obj = yajl_tree_get(info, ptr, yajl_t_array)) == NULL)
|
|
+ goto done;
|
|
+
|
|
+ /* Array of devices */
|
|
+ for (i=0; i<YAJL_GET_ARRAY(ret_obj)->len; i++) {
|
|
+ memset(&vbd, 0, sizeof(xenstat_vbd));
|
|
+ qmp_devname = NULL;
|
|
+ stats_obj = YAJL_GET_ARRAY(ret_obj)->values[i];
|
|
+
|
|
+ ptr[0] = qstats[QMP_STATS_DEVICE]; /* "device" */
|
|
+ if ((n = yajl_tree_get(stats_obj, ptr, yajl_t_any)) != NULL)
|
|
+ qmp_devname = YAJL_GET_STRING(n);
|
|
+
|
|
+ ptr[0] = qstats[QMP_STATS]; /* "stats" */
|
|
+ stats_obj = yajl_tree_get(stats_obj, ptr, yajl_t_object);
|
|
+ if (stats_obj && YAJL_IS_OBJECT(stats_obj)) {
|
|
+ for (j=3; j<7; j++) {
|
|
+ ptr[0] = qstats[j];
|
|
+ n = yajl_tree_get(stats_obj, ptr, yajl_t_number);
|
|
+ if (n && YAJL_IS_NUMBER(n)) {
|
|
+ switch(j) {
|
|
+ case QMP_RD_BYTES: /* "rd_bytes" */
|
|
+ vbd.rd_sects = YAJL_GET_INTEGER(n) / sector_size;
|
|
+ break;
|
|
+ case QMP_WR_BYTES: /* "wr_bytes" */
|
|
+ vbd.wr_sects = YAJL_GET_INTEGER(n) / sector_size;
|
|
+ break;
|
|
+ case QMP_RD_OPERATIONS: /* "rd_operations" */
|
|
+ vbd.rd_reqs = YAJL_GET_INTEGER(n);
|
|
+ break;
|
|
+ case QMP_WR_OPERATIONS: /* "wr_operations" */
|
|
+ vbd.wr_reqs = YAJL_GET_INTEGER(n);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ /* With the QMP device name, lookup the xenstore qdisk device ID and set vdb.dev */
|
|
+ if (qmp_devname)
|
|
+ lookup_xenstore_devid(node, domid, qmp_devname, qfd, &vbd.dev, §or_size);
|
|
+ if ((domain = xenstat_node_domain(node, domid)) == NULL)
|
|
+ continue;
|
|
+ if ((xenstat_save_vbd(domain, &vbd)) == NULL)
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+done:
|
|
+ yajl_tree_free(info);
|
|
+}
|
|
+
|
|
+/* Write a command via the QMP. Returns number of bytes written */
|
|
+static size_t qmp_write(int qfd, char *cmd, size_t cmd_len)
|
|
+{
|
|
+ size_t pos = 0;
|
|
+ ssize_t res;
|
|
+
|
|
+ while (cmd_len > pos) {
|
|
+ res = write(qfd, cmd + pos, cmd_len - pos);
|
|
+ switch (res) {
|
|
+ case -1:
|
|
+ if (errno == EINTR || errno == EAGAIN)
|
|
+ continue;
|
|
+ return 0;
|
|
+ case 0:
|
|
+ errno = EPIPE;
|
|
+ return pos;
|
|
+ default:
|
|
+ pos += (size_t)res;
|
|
+ }
|
|
+ }
|
|
+ return pos;
|
|
+}
|
|
+
|
|
+/* Read the data sent in response to a QMP execute query. Returns 1 for success */
|
|
+static int qmp_read(int qfd, unsigned char **qstats)
|
|
+{
|
|
+ unsigned char buf[1024], *ptr;
|
|
+ struct pollfd pfd[2];
|
|
+ int n, qsize = 0;
|
|
+
|
|
+ *qstats = NULL;
|
|
+ pfd[0].fd = qfd;
|
|
+ pfd[0].events = POLLIN;
|
|
+ while ((n = poll(pfd, POLLIN, 10)) > 0) {
|
|
+ if (pfd[0].revents & POLLIN) {
|
|
+ if ((n = read(qfd, buf, sizeof(buf))) < 0) {
|
|
+ free(*qstats);
|
|
+ return 0;
|
|
+ }
|
|
+ ptr = realloc(*qstats, qsize+n+1);
|
|
+ if (ptr == NULL) {
|
|
+ free(*qstats);
|
|
+ return 0;
|
|
+ }
|
|
+ memcpy(&ptr[qsize], buf, n);
|
|
+ qsize += n;
|
|
+ ptr[qsize] = 0;
|
|
+ *qstats = ptr;
|
|
+ }
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* With the given cmd, query QMP for requested data. Returns allocated buffer containing data or NULL */
|
|
+static unsigned char *qmp_query(int qfd, char *cmd)
|
|
+{
|
|
+ unsigned char *qstats = NULL;
|
|
+ int n;
|
|
+
|
|
+ n = strlen(cmd);
|
|
+ if (qmp_write(qfd, cmd, n) != n)
|
|
+ return NULL;
|
|
+ if (!qmp_read(qfd, &qstats))
|
|
+ return NULL;
|
|
+ return qstats;
|
|
+}
|
|
+
|
|
+/* Returns a socket connected to the QMP socket. Returns -1 on failure. */
|
|
+static int qmp_connect(char *path)
|
|
+{
|
|
+ struct sockaddr_un sun;
|
|
+ int s;
|
|
+
|
|
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
|
|
+ return -1;
|
|
+ (void)fcntl(s, F_SETFD, 1);
|
|
+
|
|
+ memset(&sun, 0, sizeof(struct sockaddr_un));
|
|
+ sun.sun_family = AF_UNIX;
|
|
+
|
|
+ if (strlen(path) >= sizeof(sun.sun_path)) {
|
|
+ close(s);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ strcpy(sun.sun_path, path);
|
|
+ if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) < 0) {
|
|
+ close(s);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return s;
|
|
+}
|
|
+
|
|
+/* Get up to 1024 active domains */
|
|
+static xc_domaininfo_t *get_domain_ids(int *num_doms)
|
|
+{
|
|
+ xc_domaininfo_t *dominfo;
|
|
+ xc_interface *xc_handle;
|
|
+
|
|
+ dominfo = calloc(1024, sizeof(xc_domaininfo_t));
|
|
+ if (dominfo == NULL)
|
|
+ return NULL;
|
|
+ xc_handle = xc_interface_open(0,0,0);
|
|
+ *num_doms = xc_domain_getinfolist(xc_handle, 0, 1024, dominfo);
|
|
+ xc_interface_close(xc_handle);
|
|
+ return dominfo;
|
|
+}
|
|
+
|
|
+/* Gather the qdisk statistics by querying QMP
|
|
+ Resources: http://wiki.qemu.org/QMP and qmp-commands.hx from the qemu code
|
|
+ QMP Syntax for entering command mode. This command must be issued before
|
|
+ issuing any other command:
|
|
+ In: {"execute": "qmp_capabilities"}
|
|
+ Out: {"return": {}}
|
|
+ QMP Syntax for querying block statistics:
|
|
+ In: { "execute": "query-blockstats" }
|
|
+ Out: {"return": [{
|
|
+ "device": 'str',
|
|
+ "parent": {
|
|
+ "stats": {
|
|
+ "flush_total_time_ns": 'int', "wr_highest_offset": 'int',
|
|
+ "wr_total_time_ns": 'int', "wr_bytes": 'int',
|
|
+ "rd_total_time_ns": 'int', "flush_operations": 'int',
|
|
+ "wr_operations": 'int', "rd_bytes": 'int', "rd_operations": 'int'
|
|
+ }
|
|
+ },
|
|
+ "stats": {
|
|
+ "flush_total_time_ns": 'int', "wr_highest_offset": 'int',
|
|
+ "wr_total_time_ns": 'int', "wr_bytes": 'int',
|
|
+ "rd_total_time_ns": 'int', "flush_operations": 'int',
|
|
+ "wr_operations": 'int', "rd_bytes": 'int', "rd_operations": 'int'
|
|
+ }
|
|
+ }]}
|
|
+*/
|
|
+void read_attributes_qdisk(xenstat_node * node)
|
|
+{
|
|
+ char *cmd_mode = "{ \"execute\": \"qmp_capabilities\" }";
|
|
+ char *query_blockstats_cmd = "{ \"execute\": \"query-blockstats\" }";
|
|
+ xc_domaininfo_t *dominfo = NULL;
|
|
+ unsigned char *qmp_stats, *val;
|
|
+ char path[80];
|
|
+ int i, qfd, num_doms;
|
|
+
|
|
+ dominfo = get_domain_ids(&num_doms);
|
|
+ if (dominfo == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i=0; i<num_doms; i++) {
|
|
+ if (dominfo[i].domain <= 0)
|
|
+ continue;
|
|
+
|
|
+ /* Verify that qdisk disks are used with this VM */
|
|
+ snprintf(path, sizeof(path),"/local/domain/0/backend/qdisk/%i", dominfo[i].domain);
|
|
+ if ((val = xs_read(node->handle->xshandle, XBT_NULL, path, NULL)) == NULL)
|
|
+ continue;
|
|
+ free(val);
|
|
+
|
|
+ /* Connect to this VMs QMP socket */
|
|
+ snprintf(path, sizeof(path), "/var/run/xen/qmp-libxenstat-%i", dominfo[i].domain);
|
|
+ if ((qfd = qmp_connect(path)) < 0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* First enable QMP capabilities so that we can query for data */
|
|
+ if ((qmp_stats = qmp_query(qfd, cmd_mode)) != NULL) {
|
|
+ free(qmp_stats);
|
|
+ /* Query QMP for this VMs blockstats */
|
|
+ if ((qmp_stats = qmp_query(qfd, query_blockstats_cmd)) != NULL) {
|
|
+ qmp_parse_stats(node, dominfo[i].domain, qmp_stats, qfd);
|
|
+ free(qmp_stats);
|
|
+ }
|
|
+ }
|
|
+ close(qfd);
|
|
+ }
|
|
+
|
|
+ free(dominfo);
|
|
+}
|
|
+
|
|
+#else /* !HAVE_YAJL_V2 */
|
|
+
|
|
+/* Statistics gathering for qdisks requires at least yajl v2 */
|
|
+void read_attributes_qdisk(xenstat_node * node)
|
|
+{
|
|
+}
|
|
+
|
|
+#endif /* !HAVE_YAJL_V2 */
|