|
[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 |