[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [RFC PATCH] xentop: add support for qdisks



Now that Xen uses qdisks by default and qemu does not write out
statistics to sysfs this patch queries the QMP for disk statistics.

Signed-off-by: Charles Arnold <carnold@xxxxxxxx>

diff --git a/tools/xenstat/libxenstat/src/xenstat_linux.c 
b/tools/xenstat/libxenstat/src/xenstat_linux.c
index 7fdf70a..885d089 100644
--- a/tools/xenstat/libxenstat/src/xenstat_linux.c
+++ b/tools/xenstat/libxenstat/src/xenstat_linux.c
@@ -24,11 +24,15 @@
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/un.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <regex.h>
+#include <errno.h>
 
 #include "xenstat_priv.h"
 
@@ -398,6 +402,258 @@ static int read_attributes_vbd(const char *vbd_directory, 
const char *what, char
        return num_read;
 }
 
+/* Given an object name look up its value in the buffer */
+static void qmp_lookup_object(unsigned char *buf, char *name, unsigned long 
long *value)
+{
+       unsigned char *ptr;
+       int len;
+
+       *value = 0;
+       len = strlen((char *)name);
+       for (ptr=buf; *ptr; ptr++) {
+               if ( !strncmp((char *)ptr, name, len) ) {
+                       ptr += len;
+                       while (*ptr == ' ')
+                               ++ptr;
+                       (void)sscanf((char *)ptr, "%llu", value);
+                       break;
+               }
+       }
+}
+
+/* Starting at the opening brace of an object, go until the closing is found */
+static unsigned char *qmp_skip_object(unsigned char *obj)
+{
+       unsigned char *end;
+       int brace = 0;
+
+       if ( *obj != '{' )
+               return obj;
+       for ( end=obj; *end; end++ ) {
+               if ( *end == '{' )
+                       ++brace;
+               else if ( *end == '}' )
+                       --brace;
+               if ( brace <= 0 ) {
+                       ++end;
+                       break;
+               }
+       }
+       return end;
+}
+
+/*
+  Parse disk statistics from QMP
+  A single query-blockstats QMP call will get all disks belonging to the VM.
+  We need to parse that data and get the stats for the vbd we care about.
+*/
+static void qmp_parse(unsigned char *stats, unsigned int domid,
+       unsigned char *vbdname, unsigned int sector_size, xenstat_vbd *vbd)
+{
+       unsigned long long bytes;
+       unsigned char *ptr, *tmp;
+       int vbdlen, vbdfound, tmplen;
+       unsigned char buf[512];
+
+       vbdlen = (int)strlen((const char *)vbdname);
+       vbdfound = 0;
+       /* Example: {"return": [{"device": "xvda", "parent": {..., "stats": 
{... */
+       for (ptr=stats; *ptr; ptr++) {
+               if ( *ptr == '{' ) {
+                       if ( !vbdfound ) {
+                               if ( !strncmp((const char *)ptr, 
"{\"device\":", 10) ) {
+                                       ptr += 10;
+                                       while (*ptr == ' ' || *ptr == '"')
+                                               ++ptr;
+                                       if ( !strncmp((const char *)ptr, (const 
char *)vbdname, vbdlen) ) {
+                                               vbdfound = 1;
+                                               ptr += vbdlen;
+                                       }
+                               }
+                       }
+                       else {
+                               ptr = qmp_skip_object(ptr);
+                       }
+               }
+               else if ( vbdfound && !strncmp((const char *)ptr, " 
\"stats\":", 9) ) {
+                       ptr += 10;
+                       tmp = ptr;
+                       ptr = qmp_skip_object(ptr);
+                       tmplen = (int)(ptr-tmp);
+                       strncpy((char *)buf, (char *)tmp, tmplen);
+                       buf[tmplen] = 0;
+                       qmp_lookup_object(buf, "\"rd_bytes\":", &bytes);
+                       vbd->rd_sects = bytes / sector_size;
+                       qmp_lookup_object(buf, "\"wr_bytes\":", &bytes);
+                       vbd->wr_sects = bytes / sector_size;
+                       qmp_lookup_object(buf, "\"rd_operations\":", 
&vbd->rd_reqs);
+                       qmp_lookup_object(buf, "\"wr_operations\":", 
&vbd->wr_reqs);
+                       break;
+               }
+       }
+}
+
+/* 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 ( qstats )
+                       {
+                               if (ptr == NULL)
+                                       ptr = malloc(n);
+                               else
+                                       ptr = realloc(ptr, qsize+n);
+                               if (ptr == NULL)
+                                       return 0;
+
+                               memcpy(&ptr[qsize], buf, n);
+                               qsize += n;
+                               ptr[qsize] = 0;
+                               *qstats = ptr;
+                       }
+               }
+       }
+       return 1;
+}
+
+/* Query through QMP for block statistics data. Returns allocated buffer 
containing stats or NULL */
+static unsigned char *qmp_query(int qfd)
+{
+       char *cmd_mode = "{ \"execute\": \"qmp_capabilities\" }";
+       char *query_stats_cmd = "{ \"execute\": \"query-blockstats\" }";
+       unsigned char *qstats = NULL;
+       int n;
+
+       /* Enter QMP command mode */
+       n = strlen(cmd_mode);
+       if (qmp_write(qfd, cmd_mode, n) != n)
+               return NULL;
+       if (!qmp_read(qfd, NULL))
+               return NULL;
+
+       /* Query block statistics */
+       n = strlen(query_stats_cmd);
+       if (qmp_write(qfd, query_stats_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;
+}
+
+/* Based on the domid and devid, get the vbd name from xenstore and then
+   query via QMP the statistics for the vbd. Return 1 on success */
+static int read_attributes_qdisk(xenstat_node * node, unsigned int domid, 
xenstat_vbd *vbd)
+{
+       unsigned char *vbdname, *ssize, *qmp_stats;
+       static char path[80];
+       unsigned int sector_size = 512;
+       int qfd, ret = 0;
+
+       vbd->oo_reqs = vbd->rd_reqs = vbd->wr_reqs = vbd->rd_sects = 
vbd->wr_sects = 0;
+       snprintf(path, sizeof(path),"/local/domain/0/backend/qdisk/%i/%i/dev", 
domid, vbd->dev);
+       if ((vbdname = xs_read(node->handle->xshandle, XBT_NULL, path, NULL)) 
!= NULL) {
+               snprintf(path, 
sizeof(path),"/local/domain/0/backend/qdisk/%i/%i/sector-size", domid, 
vbd->dev);
+               if ((ssize = xs_read(node->handle->xshandle, XBT_NULL, path, 
NULL)) != NULL) {
+                       sector_size = atoi((char *)ssize);
+                       free(ssize);
+               }
+
+               snprintf(path, sizeof(path), "/var/run/xen/qmp-libxl-%u", 
domid);
+               if ((qfd = qmp_connect(path)) < 0) {
+                       free(vbdname);
+                       return 0;
+               }
+
+               if ((qmp_stats = qmp_query(qfd)) != NULL) {
+                       qmp_parse(qmp_stats, domid, vbdname, sector_size, vbd);
+                       free(qmp_stats);
+                       ret = 1;
+               }
+               close(qfd);
+               free(vbdname);
+       }
+       return ret;
+}
+
+/* Save this vbd in the domains array of vbds */
+static xenstat_vbd *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;
+}
+
 /* Collect information about VBDs */
 int xenstat_collect_vbds(xenstat_node * node)
 {
@@ -428,13 +684,19 @@ int xenstat_collect_vbds(xenstat_node * node)
                char buf[256];
 
                ret = sscanf(dp->d_name, "%3s-%u-%u", buf, &domid, &vbd.dev);
-               if (ret != 3)
-                       continue;
+               if (ret != 3) {
+                       /* Try again looking for 'qdisk' */
+                       ret = sscanf(dp->d_name, "%5s-%u-%u", buf, &domid, 
&vbd.dev);
+                       if (ret != 3)
+                               continue;
+               }
 
                if (strcmp(buf,"vbd") == 0)
                        vbd.back_type = 1;
                else if (strcmp(buf,"tap") == 0)
                        vbd.back_type = 2;
+               else if (strcmp(buf,"qdisk") == 0)
+                       vbd.back_type = 3;
                else
                        continue;
 
@@ -447,6 +709,15 @@ int xenstat_collect_vbds(xenstat_node * node)
                        continue;
                }
 
+               /* Use QMP to get statistics for a qdisk */
+               if (vbd.back_type == 3) {
+                       if (read_attributes_qdisk(node, domid, &vbd)>0) {
+                               if ((save_vbd(domain, &vbd)) == NULL)
+                                       return 0;
+                       }
+                       continue;
+               }
+
                if((read_attributes_vbd(dp->d_name, "statistics/oo_req", buf, 
256)<=0)
                   || ((ret = sscanf(buf, "%llu", &vbd.oo_reqs)) != 1))
                {
@@ -477,18 +748,10 @@ int xenstat_collect_vbds(xenstat_node * 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 ((save_vbd(domain, &vbd)) == NULL) {
+                       perror("Allocation error");
                        return 0;
-               domain->vbds[domain->num_vbds - 1] = vbd;
+               }
        }
 
        return 1;       


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.