[Libvir] PATCH: Improve performance on Xen

Daniel P. Berrange berrange at redhat.com
Thu Aug 9 22:13:00 UTC 2007


The Xen implementations of

   virDomainLookupByID
   virDomainLookupByUUID
   virDomainLookupByName
   virDomainGetOSType

all have sub-optimal performance since they speak to XenD. The lookupXXX
functions all basically require 3 pieces of info in the end (name,id,uuid).
They each get given one piece & have to lookup the other piece.

Every running domain has to have an entry in XenStore on /local/domain/N
where 'N' is the id. Under this location there is always a 'name' field.
So if we have the id we can efficiently get the name. I just realized that
the getdomaininfo hypercall struct contains the uuid and id. So for the
ByID and ByUUID calls we can get all the info we need with a hypercall and
a read of xenstore. This is very efficient compared to hitting XenD.

As of Xen 3.1.0 hypervisor the flags in the getdomaininfo hypercall struct
also mark whether the guest is HVM vs paraivrt, so we can also implement
the GetOSType api with a single hypercall.

That just leaves the ByName impl. The only way I can think of doing this
is to scan /local/domain/N  in xenstore until we find it. I'm going to try
this, but I'm not convinced it'll be any significantly than talking to XenD.
I may be surprised though.


Any for the 3 improvements I have done, the 'virsh dominfo' call has improved
when using either ID or UUID:

Create a set of UUIDs & IDs

 # for i in `seq 1 500` ; do echo dominfo 73 ; done > ids.txt
 # for i in `seq 1 500` ; do echo dominfo 8f07fe28-753f-2729-d76d-bdbd892f949a ; done > uuids.txt

Now before measurements:

  # time ./virsh < ids.txt > /dev/null 
  real    0m17.317s
  user    0m0.144s
  sys     0m0.164s

  # time ./virsh < uuids.txt > /dev/null 
  real    0m14.432s
  user    0m0.236s
  sys     0m0.460s

Against the after measurements:

  # time ./virsh < demo.txt > /dev/null 
  real    0m0.259s
  user    0m0.100s
  sys     0m0.072s

  # time ./virsh < demo.txt > /dev/null 
  real    0m0.482s
  user    0m0.324s
  sys     0m0.108s

A pretty good speedup really considering how frequently these calls are
made if you're monitoring domains frequently.

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
-------------- next part --------------
? src/demo.txt
Index: src/xen_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_internal.c,v
retrieving revision 1.90
diff -u -p -r1.90 xen_internal.c
--- src/xen_internal.c	30 Jul 2007 10:15:58 -0000	1.90
+++ src/xen_internal.c	9 Aug 2007 22:01:57 -0000
@@ -27,6 +27,7 @@
 #include <regex.h>
 #include <errno.h>
 #include <sys/utsname.h>
+#include "xs_internal.h"
 
 /* required for dom0_getdomaininfo_t */
 #include <xen/dom0_ops.h>
@@ -229,6 +230,13 @@ typedef union xen_getschedulerid xen_get
       domlist.v2[n].domain :                        \
       domlist.v2d5[n].domain))
 
+#define XEN_GETDOMAININFOLIST_UUID(domlist, n)      \
+    (hypervisor_version < 2 ?                       \
+     domlist.v0[n].handle :                         \
+     (dom_interface_version < 5 ?                   \
+      domlist.v2[n].handle :                        \
+      domlist.v2d5[n].handle))
+
 #define XEN_GETDOMAININFOLIST_DATA(domlist)        \
     (hypervisor_version < 2 ?                      \
      (void*)(domlist->v0) :                        \
@@ -299,6 +307,13 @@ typedef union xen_getschedulerid xen_get
       dominfo.v2.max_pages :                    \
       dominfo.v2d5.max_pages))
 
+#define XEN_GETDOMAININFO_UUID(dominfo)         \
+    (hypervisor_version < 2 ?                   \
+     dominfo.v0.handle :                        \
+     (dom_interface_version < 5 ?               \
+      dominfo.v2.handle :                       \
+      dominfo.v2d5.handle))
+
 
 
 struct xen_v0_getdomaininfolistop {
@@ -626,7 +641,7 @@ struct xenUnifiedDriver xenHypervisorDri
     NULL, /* domainShutdown */
     NULL, /* domainReboot */
     xenHypervisorDestroyDomain, /* domainDestroy */
-    NULL, /* domainGetOSType */
+    xenHypervisorDomainGetOSType, /* domainGetOSType */
     xenHypervisorGetMaxMemory, /* domainGetMaxMemory */
     xenHypervisorSetMaxMemory, /* domainSetMaxMemory */
     NULL, /* domainSetMemory */
@@ -2437,6 +2452,136 @@ xenHypervisorListDomains(virConnectPtr c
     return (nbids);
 }
 
+
+#ifndef PROXY
+char *
+xenHypervisorDomainGetOSType (virDomainPtr dom)
+{
+    xenUnifiedPrivatePtr priv;
+    xen_getdomaininfo dominfo;
+
+    priv = (xenUnifiedPrivatePtr) dom->conn->privateData;
+    if (priv->handle < 0)
+        return (NULL);
+
+    /* HV's earlier than 3.1.0 don't include the HVM flags in guests status*/
+    if (hypervisor_version < 2 ||
+        dom_interface_version < 4)
+        return (NULL);
+
+    XEN_GETDOMAININFO_CLEAR(dominfo);
+
+    if (virXen_getdomaininfo(priv->handle, dom->id, &dominfo) < 0)
+        return (NULL);
+
+    if (XEN_GETDOMAININFO_DOMAIN(dominfo) != dom->id)
+        return (NULL);
+
+    if (XEN_GETDOMAININFO_FLAGS(dominfo) & DOMFLAGS_HVM)
+        return strdup("hvm");
+    return strdup("linux");
+}
+
+virDomainPtr
+xenHypervisorLookupDomainByID(virConnectPtr conn,
+                              int id)
+{
+    xenUnifiedPrivatePtr priv;
+    xen_getdomaininfo dominfo;
+    virDomainPtr ret;
+    char *name;
+
+    priv = (xenUnifiedPrivatePtr) conn->privateData;
+    if (priv->handle < 0)
+        return (NULL);
+
+    XEN_GETDOMAININFO_CLEAR(dominfo);
+
+    if (virXen_getdomaininfo(priv->handle, id, &dominfo) < 0)
+        return (NULL);
+
+    if (XEN_GETDOMAININFO_DOMAIN(dominfo) != id)
+        return (NULL);
+
+
+    if (!(name = xenStoreDomainGetName(conn, id)))
+        return (NULL);
+
+    ret = virGetDomain(conn, name, XEN_GETDOMAININFO_UUID(dominfo));
+    if (ret)
+        ret->id = id;
+    free(name);
+    return ret;
+}
+
+
+virDomainPtr
+xenHypervisorLookupDomainByUUID(virConnectPtr conn,
+                                const unsigned char *uuid)
+{
+    xen_getdomaininfolist dominfos;
+    xenUnifiedPrivatePtr priv;
+    virDomainPtr ret;
+    char *name;
+    int maxids = 100, nids, i, id;
+
+    priv = (xenUnifiedPrivatePtr) conn->privateData;
+    if (priv->handle < 0)
+        return (NULL);
+
+ retry:
+    if (!(XEN_GETDOMAININFOLIST_ALLOC(dominfos, maxids))) {
+        virXenError(VIR_ERR_NO_MEMORY, "allocating %d domain info",
+                    maxids);
+        return(NULL);
+    }
+
+    XEN_GETDOMAININFOLIST_CLEAR(dominfos, maxids);
+
+    nids = virXen_getdomaininfolist(priv->handle, 0, maxids, &dominfos);
+
+    if (nids < 0) {
+        XEN_GETDOMAININFOLIST_FREE(dominfos);
+        return (NULL);
+    }
+
+    /* Can't possibly have more than 65,000 concurrent guests
+     * so limit how many times we try, to avoid increasing
+     * without bound & thus allocating all of system memory !
+     * XXX I'll regret this comment in a few years time ;-)
+     */
+    if (nids == maxids) {
+        XEN_GETDOMAININFOLIST_FREE(dominfos);
+        if (maxids < 65000) {
+            maxids *= 2;
+            goto retry;
+        }
+        return (NULL);
+    }
+
+    id = -1;
+    for (i = 0 ; i < nids ; i++) {
+        if (memcmp(XEN_GETDOMAININFOLIST_UUID(dominfos, i), uuid, VIR_UUID_BUFLEN) == 0) {
+            id = XEN_GETDOMAININFOLIST_DOMAIN(dominfos, i);
+            break;
+        }
+    }
+    XEN_GETDOMAININFOLIST_FREE(dominfos);
+
+    if (id == -1)
+        return (NULL);
+
+    if (!(name = xenStoreDomainGetName(conn, id)))
+        return (NULL);
+
+    ret = virGetDomain(conn, name, uuid);
+    if (ret)
+        ret->id = id;
+    free(name);
+    return ret;
+}
+#endif
+
 /**
  * xenHypervisorGetMaxVcpus:
  *
Index: src/xen_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xen_internal.h,v
retrieving revision 1.21
diff -u -p -r1.21 xen_internal.h
--- src/xen_internal.h	6 Jul 2007 15:11:22 -0000	1.21
+++ src/xen_internal.h	9 Aug 2007 22:01:57 -0000
@@ -20,6 +20,15 @@ int	xenHypervisorInit		(void);
 
 /* The following calls are made directly by the Xen proxy: */
 
+virDomainPtr
+        xenHypervisorLookupDomainByID   (virConnectPtr conn,
+					 int id);
+virDomainPtr
+xenHypervisorLookupDomainByUUID(virConnectPtr conn,
+                                const unsigned char *uuid);
+char *
+        xenHypervisorDomainGetOSType (virDomainPtr dom);
+
 int	xenHypervisorOpen		(virConnectPtr conn,
 					 const char *name,
 					 int flags);
Index: src/xen_unified.c
===================================================================
RCS file: /data/cvs/libvirt/src/xen_unified.c,v
retrieving revision 1.17
diff -u -p -r1.17 xen_unified.c
--- src/xen_unified.c	12 Jul 2007 08:34:51 -0000	1.17
+++ src/xen_unified.c	9 Aug 2007 22:01:57 -0000
@@ -377,6 +377,13 @@ xenUnifiedDomainLookupByID (virConnectPt
      */
     virConnResetLastError (conn);
 
+    /* Try hypervisor/xenstore combo. */
+    if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) {
+        ret = xenHypervisorLookupDomainByID (conn, id);
+        if (ret || conn->err.code != VIR_ERR_OK)
+            return ret;
+    }
+
     /* Try proxy. */
     if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) {
         ret = xenProxyLookupByID (conn, id);
@@ -408,6 +415,13 @@ xenUnifiedDomainLookupByUUID (virConnect
      */
     virConnResetLastError (conn);
 
+    /* Try hypervisor/xenstore combo. */
+    if (priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET]) {
+        ret = xenHypervisorLookupDomainByUUID (conn, uuid);
+        if (ret || conn->err.code != VIR_ERR_OK)
+            return ret;
+    }
+
     /* Try proxy. */
     if (priv->opened[XEN_UNIFIED_PROXY_OFFSET]) {
         ret = xenProxyLookupByUUID (conn, uuid);
Index: src/xs_internal.c
===================================================================
RCS file: /data/cvs/libvirt/src/xs_internal.c,v
retrieving revision 1.46
diff -u -p -r1.46 xs_internal.c
--- src/xs_internal.c	6 Jul 2007 15:11:22 -0000	1.46
+++ src/xs_internal.c	9 Aug 2007 22:01:57 -0000
@@ -875,6 +875,22 @@ xenStoreDomainGetNetworkID(virConnectPtr
     return(ret);
 }
 
+char *xenStoreDomainGetName(virConnectPtr conn,
+                            int id) {
+    char prop[200];
+    xenUnifiedPrivatePtr priv;
+    unsigned int len;
+
+    priv = (xenUnifiedPrivatePtr) conn->privateData;
+    if (priv->xshandle == NULL)
+        return(NULL);
+
+    snprintf(prop, 199, "/local/domain/%d/name", id);
+    prop[199] = 0;
+    return xs_read(priv->xshandle, 0, prop, &len);
+}
+
+
 #endif /* WITH_XEN */
 /*
  * Local variables:
Index: src/xs_internal.h
===================================================================
RCS file: /data/cvs/libvirt/src/xs_internal.h,v
retrieving revision 1.10
diff -u -p -r1.10 xs_internal.h
--- src/xs_internal.h	6 Jul 2007 15:11:22 -0000	1.10
+++ src/xs_internal.h	9 Aug 2007 22:01:57 -0000
@@ -15,6 +15,8 @@
 extern "C" {
 #endif
 
+#include "internal.h"
+
 extern struct xenUnifiedDriver xenStoreDriver;
 int xenStoreInit (void);
 
@@ -48,6 +50,8 @@ char *		xenStoreDomainGetOSTypeID(virCon
 char *		xenStoreDomainGetNetworkID(virConnectPtr conn,
 					 int id,
 					 const char *mac);
+char *          xenStoreDomainGetName(virConnectPtr conn,
+				      int id);
 
 #ifdef __cplusplus
 }


More information about the libvir-list mailing list