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 carn...@suse.com
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;
+}
+