From 2465d17539e28e66f97d1beeaff391f8e280b66c48f9b4a9b83892a221593d16 Mon Sep 17 00:00:00 2001 From: Charles Arnold Date: Mon, 16 Mar 2015 17:03:34 +0000 Subject: [PATCH] - Enable spice support in qemu - bnc#921842 - Xentop doesn't display disk statistics for VMs using qdisks xentop-add-support-for-qdisk.patch OBS-URL: https://build.opensuse.org/package/show/Virtualization/xen?expand=0&rev=348 --- qemu-xen-enable-spice-support.patch | 13 + xen.changes | 12 + xen.spec | 7 + xentop-add-support-for-qdisk.patch | 495 ++++++++++++++++++++++++++++ 4 files changed, 527 insertions(+) create mode 100644 qemu-xen-enable-spice-support.patch create mode 100644 xentop-add-support-for-qdisk.patch diff --git a/qemu-xen-enable-spice-support.patch b/qemu-xen-enable-spice-support.patch new file mode 100644 index 0000000..d2e9426 --- /dev/null +++ b/qemu-xen-enable-spice-support.patch @@ -0,0 +1,13 @@ +Index: xen-4.5.0-testing/tools/Makefile +=================================================================== +--- xen-4.5.0-testing.orig/tools/Makefile ++++ xen-4.5.0-testing/tools/Makefile +@@ -222,6 +222,8 @@ subdir-all-qemu-xen-dir: qemu-xen-dir-fi + --datadir=$(SHAREDIR)/qemu-xen \ + --localstatedir=$(localstatedir) \ + --disable-kvm \ ++ --enable-spice \ ++ --enable-usb-redir \ + --disable-docs \ + --disable-guest-agent \ + --python=$(PYTHON) \ diff --git a/xen.changes b/xen.changes index e6f6cb5..d3b4e95 100644 --- a/xen.changes +++ b/xen.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Mon Mar 16 10:14:15 MDT 2015 - carnold@suse.com + +- Enable spice support in qemu + +------------------------------------------------------------------- +Wed Mar 11 13:15:07 MDT 2015 - carnold@suse.com + +- bnc#921842 - Xentop doesn't display disk statistics for VMs using + qdisks + xentop-add-support-for-qdisk.patch + ------------------------------------------------------------------- Tue Feb 24 16:22:45 UTC 2015 - meissner@suse.com diff --git a/xen.spec b/xen.spec index c13fd96..1858197 100644 --- a/xen.spec +++ b/xen.spec @@ -95,9 +95,12 @@ BuildRequires: glib2-devel BuildRequires: libaio-devel BuildRequires: libbz2-devel BuildRequires: libpixman-1-0-devel +BuildRequires: libspice-server-devel BuildRequires: libuuid-devel BuildRequires: libxml2-devel BuildRequires: libyajl-devel +BuildRequires: spice-protocol-devel +BuildRequires: usbredir-devel %if %{?with_qemu_traditional}0 BuildRequires: SDL-devel BuildRequires: pciutils-devel @@ -214,6 +217,7 @@ Patch311: xl-coredump-file-location.patch Patch330: suspend_evtchn_lock.patch Patch331: xenpaging.doc.patch Patch332: local_attach_support_for_phy.patch +Patch333: xentop-add-support-for-qdisk.patch # Qemu traditional Patch350: blktap.patch Patch351: cdrom-removable.patch @@ -267,6 +271,7 @@ Patch464: set-mtu-from-bridge-for-tap-interface.patch Patch466: aarch64-rename-PSR_MODE_ELxx-to-match-linux-headers.patch Patch467: libxl.add-option-to-disable-disk-cache-flushes-in-qdisk.patch Patch470: qemu-xen-upstream-qdisk-cache-unsafe.patch +Patch471: qemu-xen-enable-spice-support.patch Patch472: tigervnc-long-press.patch # Hypervisor and PV driver Patches Patch501: x86-ioapic-ack-default.patch @@ -507,6 +512,7 @@ Authors: %patch330 -p1 %patch331 -p1 %patch332 -p1 +%patch333 -p1 # Qemu traditional %patch350 -p1 %patch351 -p1 @@ -560,6 +566,7 @@ Authors: %patch466 -p1 %patch467 -p1 %patch470 -p1 +%patch471 -p1 %patch472 -p1 # Hypervisor and PV driver Patches %patch501 -p1 diff --git a/xentop-add-support-for-qdisk.patch b/xentop-add-support-for-qdisk.patch new file mode 100644 index 0000000..6e59d31 --- /dev/null +++ b/xentop-add-support-for-qdisk.patch @@ -0,0 +1,495 @@ +Index: xen-4.5.0-testing/tools/xenstat/libxenstat/Makefile +=================================================================== +--- xen-4.5.0-testing.orig/tools/xenstat/libxenstat/Makefile ++++ xen-4.5.0-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 +Index: xen-4.5.0-testing/tools/xenstat/xentop/Makefile +=================================================================== +--- xen-4.5.0-testing.orig/tools/xenstat/xentop/Makefile ++++ xen-4.5.0-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.0-testing/tools/xenstat/libxenstat/src/xenstat_priv.h +=================================================================== +--- xen-4.5.0-testing.orig/tools/xenstat/libxenstat/src/xenstat_priv.h ++++ xen-4.5.0-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.0-testing/tools/xenstat/libxenstat/src/xenstat.c +=================================================================== +--- xen-4.5.0-testing.orig/tools/xenstat/libxenstat/src/xenstat.c ++++ xen-4.5.0-testing/tools/xenstat/libxenstat/src/xenstat.c +@@ -657,6 +657,24 @@ static void xenstat_uninit_xen_version(x + * VBD functions + */ + ++/* Save VBD information */ ++xenstat_vbd *xenstat_save_vbd(xenstat_domain *domain, xenstat_vbd *vbd) ++{ ++ 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) ++ 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.0-testing/tools/xenstat/libxenstat/src/xenstat_linux.c +=================================================================== +--- xen-4.5.0-testing.orig/tools/xenstat/libxenstat/src/xenstat_linux.c ++++ xen-4.5.0-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.0-testing/tools/xenstat/libxenstat/src/xenstat_qmp.c +=================================================================== +--- /dev/null ++++ xen-4.5.0-testing/tools/xenstat/libxenstat/src/xenstat_qmp.c +@@ -0,0 +1,387 @@ ++/* 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "yajl/yajl_tree.h" ++ ++#include ++ ++#include "xenstat_priv.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 */ ++static char *qmp_get_block_image(xenstat_node *node, char *qmp_devname, int qfd) ++{ ++ char errbuf[1024], *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.1.x or newer for the tree parser feature with bug fixes */ ++ if ((info = yajl_tree_parse((char *)qmp_stats, errbuf, sizeof(errbuf))) == 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; ilen; 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, find the associated xenstore qdisk device id */ ++static void get_xs_devid_from_qmp_devname(xenstat_node * node, unsigned int domid, char *qmp_devname, ++ unsigned int *dev, unsigned int *sector_size, int qfd) ++{ ++ 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; ihandle->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, errbuf[1024]; ++ 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, errbuf, sizeof(errbuf))) == 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; ilen; 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) ++ get_xs_devid_from_qmp_devname(node, domid, qmp_devname, &vbd.dev, §or_size, qfd); ++ 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 */ ++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 = NULL; ++ struct pollfd pfd[2]; ++ int n, qsize = 0; ++ ++ 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) { ++ return 0; ++ } ++ if (ptr == NULL) ++ ptr = malloc(n+1); ++ else ++ ptr = realloc(ptr, qsize+n+1); ++ if (ptr == NULL) ++ 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 all the 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 */ ++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; ihandle->xshandle, XBT_NULL, path, NULL)) == NULL) ++ continue; ++ free(val); ++ ++ /* Connect to this VMs QMP socket */ ++ snprintf(path, sizeof(path), "/var/run/xen/qmp-libxl-%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); ++} ++