Index: include/libvirt/libvirt.h.in
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/libvirt.h.in,v
retrieving revision 1.31
diff -u -p -r1.31 libvirt.h.in
--- include/libvirt/libvirt.h.in	18 Jul 2007 10:11:10 -0000	1.31
+++ include/libvirt/libvirt.h.in	20 Aug 2007 15:30:56 -0000
@@ -14,6 +14,9 @@
 #ifndef __VIR_VIRLIB_H__
 #define __VIR_VIRLIB_H__
 
+#include <sys/types.h>
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -197,6 +200,37 @@ int	virDomainSetSchedulerParameters	(vir
 					 virSchedParameterPtr params,
 					 int nparams);
 
+/* Block device stats for virDomainBlockStats.
+ *
+ * Hypervisors may return a field set to (int64_t)-1 which indicates
+ * that the hypervisor does not support that statistic.
+ */
+struct _virDomainBlockStats {
+  int64_t rd_req;
+  int64_t rd_bytes;
+  int64_t wr_req;
+  int64_t wr_bytes;
+  int64_t errs;   // In Xen this returns the mysterious 'oo_req'.
+};
+typedef struct _virDomainBlockStats *virDomainBlockStatsPtr;
+
+/* Network interface stats for virDomainInterfaceStats.
+ *
+ * Hypervisors may return a field set to (int64_t)-1 which indicates
+ * that the hypervisor does not support that statistic.
+ */
+struct _virDomainInterfaceStats {
+  int64_t rx_bytes;
+  int64_t rx_packets;
+  int64_t rx_errs;
+  int64_t rx_drop;
+  int64_t tx_bytes;
+  int64_t tx_packets;
+  int64_t tx_errs;
+  int64_t tx_drop;
+};
+typedef struct _virDomainInterfaceStats *virDomainInterfaceStatsPtr;
+
 /**
  * VIR_NODEINFO_MAXCPUS:
  * @nodeinfo: virNodeInfo instance
@@ -369,6 +403,16 @@ int			virDomainGetMaxVcpus	(virDomainPtr
 char *			virDomainGetXMLDesc	(virDomainPtr domain,
 						 int flags);
 
+int                     virDomainBlockStats     (virDomainPtr dom,
+						 const char *path,
+						 virDomainBlockStatsPtr stats,
+						 size_t size);
+int                     virDomainInterfaceStats (virDomainPtr dom,
+						 const char *path,
+						 virDomainInterfaceStatsPtr stats,
+						 size_t size);
+
+
 /*
  * defined but not running domains
  */
Index: qemud/remote.c
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote.c,v
retrieving revision 1.6
diff -u -p -r1.6 remote.c
--- qemud/remote.c	24 Jul 2007 14:21:03 -0000	1.6
+++ qemud/remote.c	20 Aug 2007 15:30:58 -0000
@@ -703,6 +703,69 @@ remoteDispatchDomainSetSchedulerParamete
 }
 
 static int
+remoteDispatchDomainBlockStats (struct qemud_client *client,
+                                remote_message_header *req,
+                                remote_domain_block_stats_args *args,
+                                remote_domain_block_stats_ret *ret)
+{
+    virDomainPtr dom;
+    char *path;
+    struct _virDomainBlockStats stats;
+    CHECK_CONN (client);
+
+    dom = get_nonnull_domain (client->conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchError (client, req, "domain not found");
+        return -2;
+    }
+    path = args->path;
+
+    if (virDomainBlockStats (dom, path, &stats, sizeof stats) == -1)
+        return -1;
+
+    ret->rd_req = stats.rd_req;
+    ret->rd_bytes = stats.rd_bytes;
+    ret->wr_req = stats.wr_req;
+    ret->wr_bytes = stats.wr_bytes;
+    ret->errs = stats.errs;
+
+    return 0;
+}
+
+static int
+remoteDispatchDomainInterfaceStats (struct qemud_client *client,
+                                    remote_message_header *req,
+                                    remote_domain_interface_stats_args *args,
+                                    remote_domain_interface_stats_ret *ret)
+{
+    virDomainPtr dom;
+    char *path;
+    struct _virDomainInterfaceStats stats;
+    CHECK_CONN (client);
+
+    dom = get_nonnull_domain (client->conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchError (client, req, "domain not found");
+        return -2;
+    }
+    path = args->path;
+
+    if (virDomainInterfaceStats (dom, path, &stats, sizeof stats) == -1)
+        return -1;
+
+    ret->rx_bytes = stats.rx_bytes;
+    ret->rx_packets = stats.rx_packets;
+    ret->rx_errs = stats.rx_errs;
+    ret->rx_drop = stats.rx_drop;
+    ret->tx_bytes = stats.tx_bytes;
+    ret->tx_packets = stats.tx_packets;
+    ret->tx_errs = stats.tx_errs;
+    ret->tx_drop = stats.tx_drop;
+
+    return 0;
+}
+
+static int
 remoteDispatchDomainAttachDevice (struct qemud_client *client,
                                   remote_message_header *req,
                                   remote_domain_attach_device_args *args,
Index: qemud/remote_protocol.x
===================================================================
RCS file: /data/cvs/libvirt/qemud/remote_protocol.x,v
retrieving revision 1.3
diff -u -p -r1.3 remote_protocol.x
--- qemud/remote_protocol.x	26 Jun 2007 11:42:46 -0000	1.3
+++ qemud/remote_protocol.x	20 Aug 2007 15:30:58 -0000
@@ -231,6 +231,35 @@ struct remote_domain_set_scheduler_param
     remote_sched_param params<REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX>;
 };
 
+struct remote_domain_block_stats_args {
+    remote_nonnull_domain dom;
+    remote_nonnull_string path;
+};
+
+struct remote_domain_block_stats_ret {
+    hyper rd_req;
+    hyper rd_bytes;
+    hyper wr_req;
+    hyper wr_bytes;
+    hyper errs;
+};
+
+struct remote_domain_interface_stats_args {
+    remote_nonnull_domain dom;
+    remote_nonnull_string path;
+};
+
+struct remote_domain_interface_stats_ret {
+    hyper rx_bytes;
+    hyper rx_packets;
+    hyper rx_errs;
+    hyper rx_drop;
+    hyper tx_bytes;
+    hyper tx_packets;
+    hyper tx_errs;
+    hyper tx_drop;
+};
+
 struct remote_list_domains_args {
     int maxids;
 };
@@ -610,7 +639,9 @@ enum remote_procedure {
     REMOTE_PROC_DOMAIN_GET_SCHEDULER_TYPE = 56,
     REMOTE_PROC_DOMAIN_GET_SCHEDULER_PARAMETERS = 57,
     REMOTE_PROC_DOMAIN_SET_SCHEDULER_PARAMETERS = 58,
-    REMOTE_PROC_GET_HOSTNAME = 59
+    REMOTE_PROC_GET_HOSTNAME = 59,
+    REMOTE_PROC_DOMAIN_BLOCK_STATS = 60,
+    REMOTE_PROC_DOMAIN_INTERFACE_STATS = 61
 };
 
 /* Custom RPC structure. */
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.32
diff -u -p -r1.32 driver.h
--- src/driver.h	27 Jul 2007 23:23:00 -0000	1.32
+++ src/driver.h	20 Aug 2007 15:30:58 -0000
@@ -181,6 +181,17 @@ typedef int
 					 virSchedParameterPtr params,
 					 int nparams);
 
+typedef int
+    (*virDrvDomainBlockStats)
+                    (virDomainPtr domain,
+                     const char *path,
+                     struct _virDomainBlockStats *stats);
+typedef int
+    (*virDrvDomainInterfaceStats)
+                    (virDomainPtr domain,
+                     const char *path,
+                     struct _virDomainInterfaceStats *stats);
+
 typedef struct _virDriver virDriver;
 typedef virDriver *virDriverPtr;
 
@@ -245,6 +256,8 @@ struct _virDriver {
 	virDrvDomainGetSchedulerType	domainGetSchedulerType;
 	virDrvDomainGetSchedulerParameters domainGetSchedulerParameters;
 	virDrvDomainSetSchedulerParameters domainSetSchedulerParameters;
+    virDrvDomainBlockStats      domainBlockStats;
+    virDrvDomainInterfaceStats  domainInterfaceStats;
 };
 
 typedef int
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.93
diff -u -p -r1.93 libvirt.c
--- src/libvirt.c	9 Aug 2007 20:19:12 -0000	1.93
+++ src/libvirt.c	20 Aug 2007 15:31:00 -0000
@@ -1830,6 +1830,113 @@ virDomainSetSchedulerParameters(virDomai
 }
 
 
+/**
+ * virDomainBlockStats
+ * @dom: pointer to the domain object
+ * @path: path to the block device
+ * @stats: block device stats (returned)
+ * @size: size of stats structure
+ *
+ * This function returns block device (disk) stats for block
+ * devices attached to the domain.
+ *
+ * The path parameter is the name of the block device.  Get this
+ * by calling virDomainGetXMLDesc and finding the <target dev='...'>
+ * attribute within //domain/devices/disk.  (For example, "xvda").
+ *
+ * Domains may have more than one block device.  To get stats for
+ * each you should make multiple calls to this function.
+ *
+ * Individual fields within the stats structure may be returned
+ * as -1, which indicates that the hypervisor does not support
+ * that particular statistic.
+ *
+ * Returns: 0 in case of success or -1 in case of failure.
+ */
+int
+virDomainBlockStats (virDomainPtr dom, const char *path,
+                     virDomainBlockStatsPtr stats, size_t size)
+{
+    virConnectPtr conn;
+    struct _virDomainBlockStats stats2 = { -1, -1, -1, -1, -1 };
+    DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size);
+
+    if (!stats || size > sizeof stats2) {
+        virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+    if (!VIR_IS_CONNECTED_DOMAIN (dom)) {
+        virLibDomainError (dom, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+    conn = dom->conn;
+
+    if (conn->driver->domainBlockStats) {
+        if (conn->driver->domainBlockStats (dom, path, &stats2) == -1)
+            return -1;
+
+        memcpy (stats, &stats2, size);
+        return 0;
+    }
+
+    virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+/**
+ * virDomainInterfaceStats
+ * @dom: pointer to the domain object
+ * @path: path to the interface
+ * @stats: network interface stats (returned)
+ * @size: size of stats structure
+ *
+ * This function returns network interface stats for interfaces
+ * attached to the domain.
+ *
+ * The path parameter is the name of the network interface.
+ *
+ * Domains may have more than network interface.  To get stats for
+ * each you should make multiple calls to this function.
+ *
+ * Individual fields within the stats structure may be returned
+ * as -1, which indicates that the hypervisor does not support
+ * that particular statistic.
+ *
+ * Returns: 0 in case of success or -1 in case of failure.
+ */
+int
+virDomainInterfaceStats (virDomainPtr dom, const char *path,
+                         virDomainInterfaceStatsPtr stats, size_t size)
+{
+    virConnectPtr conn;
+    struct _virDomainInterfaceStats stats2 = { -1, -1, -1, -1,
+                                               -1, -1, -1, -1 };
+    DEBUG("domain=%p, path=%s, stats=%p, size=%zi", dom, path, stats, size);
+
+    if (!stats || size > sizeof stats2) {
+        virLibDomainError (dom, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+    if (!VIR_IS_CONNECTED_DOMAIN (dom)) {
+        virLibDomainError (dom, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+    conn = dom->conn;
+
+    if (conn->driver->domainInterfaceStats) {
+        if (conn->driver->domainInterfaceStats (dom, path, &stats2) == -1)
+            return -1;
+
+        memcpy (stats, &stats2, size);
+        return 0;
+    }
+
+    virLibDomainError (dom, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+
+
 
 /************************************************************************
  *									*
Index: src/libvirt_sym.version
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt_sym.version,v
retrieving revision 1.25
diff -u -p -r1.25 libvirt_sym.version
--- src/libvirt_sym.version	26 Jun 2007 22:56:14 -0000	1.25
+++ src/libvirt_sym.version	20 Aug 2007 15:31:00 -0000
@@ -65,7 +65,8 @@
 	virDomainGetSchedulerType;
 	virDomainGetSchedulerParameters;
 	virDomainSetSchedulerParameters;
-
+	virDomainBlockStats;
+	virDomainInterfaceStats;
 	virDomainAttachDevice;
 	virDomainDetachDevice;
 
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.20
diff -u -p -r1.20 qemu_driver.c
--- src/qemu_driver.c	14 Aug 2007 01:47:24 -0000	1.20
+++ src/qemu_driver.c	20 Aug 2007 15:31:02 -0000
@@ -2661,6 +2661,8 @@ static virDriver qemuDriver = {
     NULL, /* domainGetSchedulerType */
     NULL, /* domainGetSchedulerParameters */
     NULL, /* domainSetSchedulerParameters */
+    NULL, /* domainBlockStats */
+    NULL, /* domainInterfaceStats */
 };
 
 static virNetworkDriver qemuNetworkDriver = {
Index: src/remote_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/remote_internal.c,v
retrieving revision 1.18
diff -u -p -r1.18 remote_internal.c
--- src/remote_internal.c	7 Aug 2007 14:29:45 -0000	1.18
+++ src/remote_internal.c	20 Aug 2007 15:31:04 -0000
@@ -2151,6 +2151,64 @@ remoteDomainSetSchedulerParameters (virD
     return 0;
 }
 
+static int
+remoteDomainBlockStats (virDomainPtr domain, const char *path,
+                        struct _virDomainBlockStats *stats)
+{
+    remote_domain_block_stats_args args;
+    remote_domain_block_stats_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.path = (char *) path;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_BLOCK_STATS,
+              (xdrproc_t) xdr_remote_domain_block_stats_args, (char *) &args,
+              (xdrproc_t) xdr_remote_domain_block_stats_ret, (char *) &ret)
+        == -1)
+        return -1;
+
+    stats->rd_req = ret.rd_req;
+    stats->rd_bytes = ret.rd_bytes;
+    stats->wr_req = ret.wr_req;
+    stats->wr_bytes = ret.wr_bytes;
+    stats->errs = ret.errs;
+
+    return 0;
+}
+
+static int
+remoteDomainInterfaceStats (virDomainPtr domain, const char *path,
+                            struct _virDomainInterfaceStats *stats)
+{
+    remote_domain_interface_stats_args args;
+    remote_domain_interface_stats_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    args.path = (char *) path;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_INTERFACE_STATS,
+              (xdrproc_t) xdr_remote_domain_interface_stats_args,
+                (char *) &args,
+              (xdrproc_t) xdr_remote_domain_interface_stats_ret,
+                (char *) &ret) == -1)
+        return -1;
+
+    stats->rx_bytes = ret.rx_bytes;
+    stats->rx_packets = ret.rx_packets;
+    stats->rx_errs = ret.rx_errs;
+    stats->rx_drop = ret.rx_drop;
+    stats->tx_bytes = ret.tx_bytes;
+    stats->tx_packets = ret.tx_packets;
+    stats->tx_errs = ret.tx_errs;
+    stats->tx_drop = ret.tx_drop;
+
+    return 0;
+}
+
 /*----------------------------------------------------------------------*/
 
 static int
@@ -2941,6 +2999,8 @@ static virDriver driver = {
     .domainGetSchedulerType = remoteDomainGetSchedulerType,
     .domainGetSchedulerParameters = remoteDomainGetSchedulerParameters,
     .domainSetSchedulerParameters = remoteDomainSetSchedulerParameters,
+    .domainBlockStats = remoteDomainBlockStats,
+    .domainInterfaceStats = remoteDomainInterfaceStats,
 };
 
 static virNetworkDriver network_driver = {
Index: src/test.c
===================================================================
RCS file: /data/cvs/libvirt/src/test.c,v
retrieving revision 1.44
diff -u -p -r1.44 test.c
--- src/test.c	9 Aug 2007 20:19:12 -0000	1.44
+++ src/test.c	20 Aug 2007 15:31:05 -0000
@@ -1963,6 +1963,8 @@ static virDriver testDriver = {
     testDomainGetSchedulerType, /* domainGetSchedulerType */
     testDomainGetSchedulerParams, /* domainGetSchedulerParameters */
     testDomainSetSchedulerParams, /* domainSetSchedulerParameters */
+    NULL, /* domainBlockStats */
+    NULL, /* domainInterfaceStats */
 };
 
 static virNetworkDriver testNetworkDriver = {
Index: src/virsh.c
===================================================================
RCS file: /data/cvs/libvirt/src/virsh.c,v
retrieving revision 1.97
diff -u -p -r1.97 virsh.c
--- src/virsh.c	16 Aug 2007 13:21:36 -0000	1.97
+++ src/virsh.c	20 Aug 2007 15:31:08 -0000
@@ -31,6 +31,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <inttypes.h>
 #include <test.h>
 
 #include <libxml/parser.h>
@@ -659,6 +660,129 @@ cmdDomstate(vshControl * ctl, vshCmd * c
     return ret;
 }
 
+/* "domblkstat" command
+ */
+static vshCmdInfo info_domblkstat[] = {
+    {"syntax", "domblkstat <domain> <dev>"},
+    {"help", gettext_noop("get device block stats for a domain")},
+    {"desc", gettext_noop("Get device block stats for a running domain.")},
+    {NULL,NULL}
+};
+
+static vshCmdOptDef opts_domblkstat[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
+    {"device", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("block device")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdDomblkstat (vshControl *ctl, vshCmd *cmd)
+{
+    virDomainPtr dom;
+    char *name, *device;
+    struct _virDomainBlockStats stats;
+
+    if (!vshConnectionUsability (ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", &name)))
+        return FALSE;
+
+    if (!(device = vshCommandOptString (cmd, "device", NULL)))
+        return FALSE;
+
+    if (virDomainBlockStats (dom, device, &stats, sizeof stats) == -1) {
+        vshError (ctl, FALSE, _("Failed to get block stats %s %s"),
+                  name, device);
+        virDomainFree(dom);
+        return FALSE;
+    }
+
+    if (stats.rd_req >= 0)
+        vshPrint (ctl, "%s rd_req %" PRId64 "\n", device, stats.rd_req);
+
+    if (stats.rd_bytes >= 0)
+        vshPrint (ctl, "%s rd_bytes %" PRId64 "\n", device, stats.rd_bytes);
+
+    if (stats.wr_req >= 0)
+        vshPrint (ctl, "%s wr_req %" PRId64 "\n", device, stats.wr_req);
+
+    if (stats.wr_bytes >= 0)
+        vshPrint (ctl, "%s wr_bytes %" PRId64 "\n", device, stats.wr_bytes);
+
+    if (stats.errs >= 0)
+        vshPrint (ctl, "%s errs %" PRId64 "\n", device, stats.errs);
+
+    virDomainFree(dom);
+    return TRUE;
+}
+
+/* "domifstat" command
+ */
+static vshCmdInfo info_domifstat[] = {
+    {"syntax", "domifstat <domain> <dev>"},
+    {"help", gettext_noop("get network interface stats for a domain")},
+    {"desc", gettext_noop("Get network interface stats for a running domain.")},
+    {NULL,NULL}
+};
+
+static vshCmdOptDef opts_domifstat[] = {
+    {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("interface device")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdDomIfstat (vshControl *ctl, vshCmd *cmd)
+{
+    virDomainPtr dom;
+    char *name, *device;
+    struct _virDomainInterfaceStats stats;
+
+    if (!vshConnectionUsability (ctl, ctl->conn, TRUE))
+        return FALSE;
+
+    if (!(dom = vshCommandOptDomain (ctl, cmd, "domain", &name)))
+        return FALSE;
+
+    if (!(device = vshCommandOptString (cmd, "interface", NULL)))
+        return FALSE;
+
+    if (virDomainInterfaceStats (dom, device, &stats, sizeof stats) == -1) {
+        vshError (ctl, FALSE, _("Failed to get interface stats %s %s"),
+                  name, device);
+        virDomainFree(dom);
+        return FALSE;
+    }
+
+    if (stats.rx_bytes >= 0)
+        vshPrint (ctl, "%s rx_bytes %" PRId64 "\n", device, stats.rx_bytes);
+
+    if (stats.rx_packets >= 0)
+        vshPrint (ctl, "%s rx_packets %" PRId64 "\n", device, stats.rx_packets);
+
+    if (stats.rx_errs >= 0)
+        vshPrint (ctl, "%s rx_errs %" PRId64 "\n", device, stats.rx_errs);
+
+    if (stats.rx_drop >= 0)
+        vshPrint (ctl, "%s rx_drop %" PRId64 "\n", device, stats.rx_drop);
+
+    if (stats.tx_bytes >= 0)
+        vshPrint (ctl, "%s tx_bytes %" PRId64 "\n", device, stats.tx_bytes);
+
+    if (stats.tx_packets >= 0)
+        vshPrint (ctl, "%s tx_packets %" PRId64 "\n", device, stats.tx_packets);
+
+    if (stats.tx_errs >= 0)
+        vshPrint (ctl, "%s tx_errs %" PRId64 "\n", device, stats.tx_errs);
+
+    if (stats.tx_drop >= 0)
+        vshPrint (ctl, "%s tx_drop %" PRId64 "\n", device, stats.tx_drop);
+
+    virDomainFree(dom);
+    return TRUE;
+}
+
 /*
  * "suspend" command
  */
@@ -3466,6 +3590,8 @@ static vshCmdDef commands[] = {
     {"dominfo", cmdDominfo, opts_dominfo, info_dominfo},
     {"domname", cmdDomname, opts_domname, info_domname},
     {"domstate", cmdDomstate, opts_domstate, info_domstate},
+    {"domblkstat", cmdDomblkstat, opts_domblkstat, info_domblkstat},
+    {"domifstat", cmdDomIfstat, opts_domifstat, info_domifstat},
     {"dumpxml", cmdDumpXML, opts_dumpxml, info_dumpxml},
     {"hostname", cmdHostname, NULL, info_hostname},
     {"list", cmdList, opts_list, info_list},
Index: src/xen_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_internal.c,v
retrieving revision 1.91
diff -u -p -r1.91 xen_internal.c
--- src/xen_internal.c	10 Aug 2007 18:25:15 -0000	1.91
+++ src/xen_internal.c	20 Aug 2007 15:31:10 -0000
@@ -1306,6 +1306,152 @@ xenHypervisorSetSchedulerParameters(virD
     return 0;
 }
 
+static int64_t
+read_stat (const char *path)
+{
+    char str[64];
+    int64_t r;
+    int i;
+    FILE *fp;
+
+    fp = fopen (path, "r");
+    if (!fp) return -1;
+    /* stupid GCC warning */ i = fread (str, sizeof str, 1, fp);
+    r = strtoll (str, NULL, 10);
+    fclose (fp);
+    return r;
+}
+
+static int64_t
+read_bd_stat (int device, int domid, const char *str)
+{
+    char path[PATH_MAX];
+    int64_t r;
+
+    snprintf (path, sizeof path,
+              "/sys/devices/xen-backend/vbd-%d-%d/statistics/%s_req",
+              domid, device, str);
+    r = read_stat (path);
+    if (r >= 0) return r;
+
+    snprintf (path, sizeof path,
+              "/sys/devices/xen-backend/tap-%d-%d/statistics/%s_req",
+              domid, device, str);
+    r = read_stat (path);
+    return r;
+}
+
+/* Paths have the form "xvd[a-]" and map to paths /sys/devices/xen-backend/
+ * (vbd|tap)-domid-major:minor/statistics/(rd|wr|oo)_req.  The major:minor
+ * is in this case fixed as 202*256 + 16*minor where minor is 0 for xvda,
+ * 1 for xvdb and so on.
+ */
+int
+xenHypervisorDomainBlockStats (virDomainPtr dom,
+                               const char *path,
+                               struct _virDomainBlockStats *stats)
+{
+    int minor, device;
+
+    if (strlen (path) != 4 ||
+        STRNEQLEN (path, "xvd", 3) ||
+        (minor = path[3] - 'a') < 0 ||
+        minor > 26) {
+        virXenErrorFunc (VIR_ERR_INVALID_ARG, __FUNCTION__,
+                         "invalid path, should be xvda, xvdb, etc.", 0);
+        return -1;
+    }
+    device = 202 * 256 + minor;
+
+    stats->rd_req = read_bd_stat (device, dom->id, "rd");
+    stats->wr_req = read_bd_stat (device, dom->id, "wr");
+    stats->errs =   read_bd_stat (device, dom->id, "oo");
+
+    if (stats->rd_req == -1 && stats->wr_req == -1 && stats->errs == -1) {
+        virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__,
+                         "Failed to read any block statistics", dom->id);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* Paths have the form vif<domid>.<n> (this interface checks that
+ * <domid> is the real domain ID and returns an error if not).
+ *
+ * In future we may allow you to query bridge stats (virbrX or
+ * xenbrX), but that will probably be through a separate
+ * virNetwork interface, as yet not decided.
+ *
+ * On Linux we open /proc/net/dev and look for the device
+ * called vif<domid>.<n>.
+ */
+int
+xenHypervisorDomainInterfaceStats (virDomainPtr dom,
+                                   const char *path,
+                                   struct _virDomainInterfaceStats *stats)
+{
+    int rqdomid, device;
+    FILE *fp;
+    char line[256];
+
+    if (sscanf (path, "vif%d.%d", &rqdomid, &device) != 2) {
+        virXenErrorFunc (VIR_ERR_INVALID_ARG, __FUNCTION__,
+                         "invalid path, should be vif<domid>.<n>.", 0);
+        return -1;
+    }
+    if (rqdomid != dom->id) {
+        virXenErrorFunc (VIR_ERR_INVALID_ARG, __FUNCTION__,
+                         "invalid path, vif<domid> should match this domain ID", 0);
+        return -1;
+    }
+
+    fp = fopen ("/proc/net/dev", "r");
+    if (!fp) {
+        virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__,
+                         "/proc/net/dev", errno);
+        return -1;
+    }
+    while (fgets (line, sizeof line, fp)) {
+        int domid, port;
+        long long dummy;
+        long long rx_bytes;
+        long long rx_packets;
+        long long rx_errs;
+        long long rx_drop;
+        long long tx_bytes;
+        long long tx_packets;
+        long long tx_errs;
+        long long tx_drop;
+
+        if (sscanf (line, "vif%d.%d: %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld",
+                    &domid, &port,
+                    &rx_bytes, &rx_packets, &rx_errs, &rx_drop,
+                    &dummy, &dummy, &dummy, &dummy,
+                    &tx_bytes, &tx_packets, &tx_errs, &tx_drop,
+                    &dummy, &dummy, &dummy, &dummy) != 18)
+            continue;
+
+        if (domid == dom->id && port == device) {
+            stats->rx_bytes = rx_bytes;
+            stats->rx_packets = rx_packets;
+            stats->rx_errs = rx_errs;
+            stats->rx_drop = rx_drop;
+            stats->tx_bytes = tx_bytes;
+            stats->tx_packets = tx_packets;
+            stats->tx_errs = tx_errs;
+            stats->tx_drop = tx_drop;
+            fclose (fp);
+            return 0;
+        }
+    }
+    fclose (fp);
+
+    virXenErrorFunc (VIR_ERR_NO_SUPPORT, __FUNCTION__,
+                     "/proc/net/dev: Interface not found", 0);
+    return -1;
+}
+
 /**
  * virXen_pausedomain:
  * @handle: the hypervisor handle
Index: src/xen_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xen_internal.h,v
retrieving revision 1.22
diff -u -p -r1.22 xen_internal.h
--- src/xen_internal.h	10 Aug 2007 18:25:15 -0000	1.22
+++ src/xen_internal.h	20 Aug 2007 15:31:10 -0000
@@ -86,6 +86,13 @@ int	xenHypervisorSetSchedulerParameters	
 					 virSchedParameterPtr params,
 					 int nparams);
 
+int     xenHypervisorDomainBlockStats   (virDomainPtr domain,
+					 const char *path,
+					 struct _virDomainBlockStats *stats);
+int     xenHypervisorDomainInterfaceStats (virDomainPtr domain,
+					 const char *path,
+					 struct _virDomainInterfaceStats *stats);
+
 #ifdef __cplusplus
 }
 #endif
Index: src/xen_unified.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_unified.c,v
retrieving revision 1.18
diff -u -p -r1.18 xen_unified.c
--- src/xen_unified.c	10 Aug 2007 18:25:15 -0000	1.18
+++ src/xen_unified.c	20 Aug 2007 15:31:11 -0000
@@ -962,6 +962,32 @@ xenUnifiedDomainSetSchedulerParameters (
     return(-1);
 }
 
+static int
+xenUnifiedDomainBlockStats (virDomainPtr dom, const char *path,
+                            struct _virDomainBlockStats *stats)
+{
+    GET_PRIVATE (dom->conn);
+
+    if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET])
+        return xenHypervisorDomainBlockStats (dom, path, stats);
+
+    xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
+static int
+xenUnifiedDomainInterfaceStats (virDomainPtr dom, const char *path,
+                                struct _virDomainInterfaceStats *stats)
+{
+    GET_PRIVATE (dom->conn);
+
+    if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET])
+        return xenHypervisorDomainInterfaceStats (dom, path, stats);
+
+    xenUnifiedError (dom->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -1;
+}
+
 /*----- Register with libvirt.c, and initialise Xen drivers. -----*/
 
 #define VERSION ((DOM0_INTERFACE_VERSION >> 24) * 1000000 +         \
@@ -1016,6 +1042,8 @@ static virDriver xenUnifiedDriver = {
     .domainGetSchedulerType	= xenUnifiedDomainGetSchedulerType,
     .domainGetSchedulerParameters	= xenUnifiedDomainGetSchedulerParameters,
     .domainSetSchedulerParameters	= xenUnifiedDomainSetSchedulerParameters,
+    .domainBlockStats	= xenUnifiedDomainBlockStats,
+    .domainInterfaceStats = xenUnifiedDomainInterfaceStats,
 };
 
 /**
