[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |