[libvirt] [PATCH v3 1/2] vbox: change how vbox API is initialized.

Dawid Zamirski dzamirski at datto.com
Wed Nov 23 19:01:10 UTC 2016


* add vboxDriver object to serve as a singleton global object that
  holds references to IVirtualBox and ISession to be shared among
  multiple connections. The vbox_driver is instantiated only once in
  the first call vboxGetDriverConnection function that is guarded by
  a mutex.

* call vbox API initialize only when the first connection is
  established, and likewise unitialize when last connection
  disconnects. The prevents each subsequent connection from overwriting
  IVirtualBox/ISession instances of any other active connection that
  led to libvirtd segfaults. The virConnectOpen and virConnectClose
  implementations are guarded by mutex on the global vbox_driver_lock
  where the global vbox_driver object counts connectios and decides
  when it's safe to call vbox's init/uninit routines.

* add IVirutalBoxClient to vboxDriver and use it to in tandem with newer
  pfnClientInitialize/pfnClientUninitalize APIs for vbox versions that
  support it, to avoid usage of the old pfnComInitialize/Uninitialize.
---
 src/vbox/vbox_common.c        | 397 ++++++++++++++++++++++++------------------
 src/vbox/vbox_driver.c        |   1 +
 src/vbox/vbox_tmpl.c          |  59 +++++--
 src/vbox/vbox_uniformed_api.h |  38 +++-
 4 files changed, 311 insertions(+), 184 deletions(-)

diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c
index 7848e39..912c716 100644
--- a/src/vbox/vbox_common.c
+++ b/src/vbox/vbox_common.c
@@ -57,6 +57,231 @@ VIR_LOG_INIT("vbox.vbox_common");
 /* global vbox API, used for all common codes. */
 static vboxUniformedAPI gVBoxAPI;
 
+static virClassPtr vboxDriverClass;
+static virMutex vbox_driver_lock = VIR_MUTEX_INITIALIZER;
+static vboxDriverPtr vbox_driver;
+static vboxDriverPtr vboxDriverObjNew(void);
+
+static virDomainDefParserConfig vboxDomainDefParserConfig = {
+    .macPrefix = { 0x08, 0x00, 0x27 },
+    .features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
+};
+
+static virCapsPtr
+vboxCapsInit(void)
+{
+    virCapsPtr caps;
+    virCapsGuestPtr guest;
+
+    if ((caps = virCapabilitiesNew(virArchFromHost(),
+                                   false, false)) == NULL)
+        goto no_memory;
+
+    if (nodeCapsInitNUMA(caps) < 0)
+        goto no_memory;
+
+    if ((guest = virCapabilitiesAddGuest(caps,
+                                         VIR_DOMAIN_OSTYPE_HVM,
+                                         caps->host.arch,
+                                         NULL,
+                                         NULL,
+                                         0,
+                                         NULL)) == NULL)
+        goto no_memory;
+
+    if (virCapabilitiesAddGuestDomain(guest,
+                                      VIR_DOMAIN_VIRT_VBOX,
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      NULL) == NULL)
+        goto no_memory;
+
+    return caps;
+
+ no_memory:
+    virObjectUnref(caps);
+    return NULL;
+}
+
+static void
+vboxDriverDispose(void *obj)
+{
+    vboxDriverPtr driver = obj;
+
+    virObjectUnref(driver->caps);
+    virObjectUnref(driver->xmlopt);
+    if (gVBoxAPI.domainEventCallbacks)
+        virObjectUnref(driver->domainEventState);
+}
+
+static int
+vboxDriverOnceInit(void)
+{
+    if (!(vboxDriverClass = virClassNew(virClassForObjectLockable(),
+                                        "vboxDriver",
+                                        sizeof(vboxDriver),
+                                        vboxDriverDispose)))
+        return -1;
+
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(vboxDriver);
+
+static vboxDriverPtr
+vboxDriverObjNew(void)
+{
+    vboxDriverPtr driver;
+
+    if (vboxDriverInitialize() < 0)
+        return NULL;
+
+    if (!(driver = virObjectLockableNew(vboxDriverClass)))
+        return NULL;
+
+    if (!(driver->caps = vboxCapsInit()) ||
+        !(driver->xmlopt = virDomainXMLOptionNew(&vboxDomainDefParserConfig,
+                                                 NULL, NULL)))
+        goto cleanup;
+
+    if (gVBoxAPI.domainEventCallbacks &&
+        !(driver->domainEventState = virObjectEventStateNew()))
+        goto cleanup;
+
+    return driver;
+
+ cleanup:
+    virObjectUnref(driver);
+    return NULL;
+}
+
+static int
+vboxExtractVersion(void)
+{
+    int ret = -1;
+    PRUnichar *versionUtf16 = NULL;
+    char *vboxVersion = NULL;
+    nsresult rc;
+
+    if (vbox_driver->version > 0)
+        return 0;
+
+    rc = gVBoxAPI.UIVirtualBox.GetVersion(vbox_driver->vboxObj, &versionUtf16);
+    if (NS_FAILED(rc))
+        goto failed;
+
+    gVBoxAPI.UPFN.Utf16ToUtf8(vbox_driver->pFuncs, versionUtf16, &vboxVersion);
+
+    if (virParseVersionString(vboxVersion, &vbox_driver->version, false) >= 0)
+        ret = 0;
+
+    gVBoxAPI.UPFN.Utf8Free(vbox_driver->pFuncs, vboxVersion);
+    gVBoxAPI.UPFN.ComUnallocMem(vbox_driver->pFuncs, versionUtf16);
+    vboxVersion = NULL;
+    versionUtf16 = NULL;
+
+ failed:
+    if (ret != 0)
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Could not extract VirtualBox version"));
+
+    return ret;
+}
+
+static int
+vboxSdkInitialize(void)
+{
+    /* vbox API was already initialized by first connection */
+    if (vbox_driver->connectionCount > 0)
+        return 0;
+
+    if (gVBoxAPI.UPFN.Initialize(vbox_driver) != 0)
+        return -1;
+
+    if (gVBoxAPI.domainEventCallbacks &&
+        gVBoxAPI.initializeDomainEvent(vbox_driver) != 0)
+        return -1;
+
+    if (vbox_driver->vboxObj == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("IVirtualBox object is null"));
+        return -1;
+    }
+
+    if (vbox_driver->vboxSession == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("ISession object is null"));
+        return -1;
+    }
+
+    return 0;
+}
+
+static void
+vboxSdkUninitialize(void)
+{
+    /* do not uninitialize, when there are still connection using it */
+    if (vbox_driver->connectionCount > 0)
+        return;
+
+    gVBoxAPI.UPFN.Uninitialize(vbox_driver);
+}
+
+static vboxDriverPtr
+vboxGetDriverConnection(void)
+{
+    virMutexLock(&vbox_driver_lock);
+
+    if (vbox_driver) {
+        virObjectRef(vbox_driver);
+    } else {
+        vbox_driver = vboxDriverObjNew();
+
+        if (!vbox_driver) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Failed to create vbox driver object."));
+            return NULL;
+        }
+    }
+
+    if (vboxSdkInitialize() < 0 || vboxExtractVersion() < 0) {
+        gVBoxAPI.UPFN.Uninitialize(vbox_driver);
+        /* make sure to clear the pointer when last reference was released */
+        if (!virObjectUnref(vbox_driver))
+            vbox_driver = NULL;
+
+        virMutexUnlock(&vbox_driver_lock);
+
+        return NULL;
+    }
+
+    vbox_driver->connectionCount++;
+
+    virMutexUnlock(&vbox_driver_lock);
+
+    return vbox_driver;
+}
+
+static void
+vboxDestroyDriverConnection(void)
+{
+    virMutexLock(&vbox_driver_lock);
+
+    if (!vbox_driver)
+        goto cleanup;
+
+    vbox_driver->connectionCount--;
+
+    vboxSdkUninitialize();
+
+    if (!virObjectUnref(vbox_driver))
+        vbox_driver = NULL;
+
+ cleanup:
+    virMutexUnlock(&vbox_driver_lock);
+}
+
 static int openSessionForMachine(vboxGlobalData *data, const unsigned char *dom_uuid, vboxIIDUnion *iid,
                                  IMachine **machine, bool checkflag)
 {
@@ -251,153 +476,13 @@ static char *vboxGenerateMediumName(PRUint32 storageBus,
     return name;
 }
 
-static int
-vboxDomainDefPostParse(virDomainDefPtr def ATTRIBUTE_UNUSED,
-                       virCapsPtr caps ATTRIBUTE_UNUSED,
-                       unsigned int parseFlags ATTRIBUTE_UNUSED,
-                       void *opaque ATTRIBUTE_UNUSED,
-                       void *parseOpaque ATTRIBUTE_UNUSED)
-{
-    return 0;
-}
-
-static int
-vboxDomainDeviceDefPostParse(virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
-                             const virDomainDef *def ATTRIBUTE_UNUSED,
-                             virCapsPtr caps ATTRIBUTE_UNUSED,
-                             unsigned int parseFlags ATTRIBUTE_UNUSED,
-                             void *opaque ATTRIBUTE_UNUSED,
-                             void *parseOpaque ATTRIBUTE_UNUSED)
-{
-    return 0;
-}
-
-static virDomainDefParserConfig vboxDomainDefParserConfig = {
-    .macPrefix = { 0x08, 0x00, 0x27 },
-    .devicesPostParseCallback = vboxDomainDeviceDefPostParse,
-    .domainPostParseCallback = vboxDomainDefPostParse,
-    .features = VIR_DOMAIN_DEF_FEATURE_NAME_SLASH,
-};
-
-static virDomainXMLOptionPtr
-vboxXMLConfInit(void)
-{
-    return virDomainXMLOptionNew(&vboxDomainDefParserConfig,
-                                 NULL, NULL);
-}
-
-static int vboxInitialize(vboxGlobalData *data)
-{
-    if (gVBoxAPI.UPFN.Initialize(data) != 0)
-        goto cleanup;
-
-    if (gVBoxAPI.domainEventCallbacks && gVBoxAPI.initializeDomainEvent(data) != 0)
-        goto cleanup;
-
-    if (data->vboxObj == NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("IVirtualBox object is null"));
-        goto cleanup;
-    }
-
-    if (data->vboxSession == NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("ISession object is null"));
-        goto cleanup;
-    }
-
-    return 0;
-
- cleanup:
-    return -1;
-}
-
-static virCapsPtr vboxCapsInit(void)
-{
-    virCapsPtr caps;
-    virCapsGuestPtr guest;
-
-    if ((caps = virCapabilitiesNew(virArchFromHost(),
-                                   false, false)) == NULL)
-        goto no_memory;
-
-    if (nodeCapsInitNUMA(caps) < 0)
-        goto no_memory;
-
-    if ((guest = virCapabilitiesAddGuest(caps,
-                                         VIR_DOMAIN_OSTYPE_HVM,
-                                         caps->host.arch,
-                                         NULL,
-                                         NULL,
-                                         0,
-                                         NULL)) == NULL)
-        goto no_memory;
-
-    if (virCapabilitiesAddGuestDomain(guest,
-                                      VIR_DOMAIN_VIRT_VBOX,
-                                      NULL,
-                                      NULL,
-                                      0,
-                                      NULL) == NULL)
-        goto no_memory;
-
-    return caps;
-
- no_memory:
-    virObjectUnref(caps);
-    return NULL;
-}
-
-static int vboxExtractVersion(vboxGlobalData *data)
-{
-    int ret = -1;
-    PRUnichar *versionUtf16 = NULL;
-    char *vboxVersion = NULL;
-    nsresult rc;
-
-    if (data->version > 0)
-        return 0;
-
-    rc = gVBoxAPI.UIVirtualBox.GetVersion(data->vboxObj, &versionUtf16);
-    if (NS_FAILED(rc))
-        goto failed;
-
-    VBOX_UTF16_TO_UTF8(versionUtf16, &vboxVersion);
-
-    if (virParseVersionString(vboxVersion, &data->version, false) >= 0)
-        ret = 0;
-
-    VBOX_UTF8_FREE(vboxVersion);
-    VBOX_COM_UNALLOC_MEM(versionUtf16);
- failed:
-    if (ret != 0)
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Could not extract VirtualBox version"));
-
-    return ret;
-}
-
-static void vboxUninitialize(vboxGlobalData *data)
-{
-    if (!data)
-        return;
-
-    gVBoxAPI.UPFN.Uninitialize(data);
-
-    virObjectUnref(data->caps);
-    virObjectUnref(data->xmlopt);
-    if (gVBoxAPI.domainEventCallbacks)
-        virObjectUnref(data->domainEvents);
-    VIR_FREE(data);
-}
-
 static virDrvOpenStatus
 vboxConnectOpen(virConnectPtr conn,
                 virConnectAuthPtr auth ATTRIBUTE_UNUSED,
                 virConfPtr conf ATTRIBUTE_UNUSED,
                 unsigned int flags)
 {
-    vboxGlobalData *data = NULL;
+    vboxDriverPtr driver = NULL;
     uid_t uid = geteuid();
 
     virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
@@ -435,30 +520,11 @@ vboxConnectOpen(virConnectPtr conn,
         }
     }
 
-    if (VIR_ALLOC(data) < 0)
-        return VIR_DRV_OPEN_ERROR;
-
-    if (!(data->caps = vboxCapsInit()) ||
-        vboxInitialize(data) < 0 ||
-        vboxExtractVersion(data) < 0 ||
-        !(data->xmlopt = vboxXMLConfInit())) {
-        vboxUninitialize(data);
+    if (!(driver = vboxGetDriverConnection()))
         return VIR_DRV_OPEN_ERROR;
-    }
 
-    if (gVBoxAPI.domainEventCallbacks) {
-        if (!(data->domainEvents = virObjectEventStateNew())) {
-            vboxUninitialize(data);
-            return VIR_DRV_OPEN_ERROR;
-        }
-
-        data->conn = conn;
-    }
+    conn->privateData = virObjectRef(driver);
 
-    if (gVBoxAPI.hasStaticGlobalData)
-        gVBoxAPI.registerGlobalData(data);
-
-    conn->privateData = data;
     VIR_DEBUG("in vboxOpen");
 
     return VIR_DRV_OPEN_SUCCESS;
@@ -466,11 +532,10 @@ vboxConnectOpen(virConnectPtr conn,
 
 static int vboxConnectClose(virConnectPtr conn)
 {
-    vboxGlobalData *data = conn->privateData;
     VIR_DEBUG("%s: in vboxClose", conn->driver->name);
 
-    vboxUninitialize(data);
-    conn->privateData = NULL;
+    virObjectUnref(conn->privateData);
+    vboxDestroyDriverConnection();
 
     return 0;
 }
@@ -7869,6 +7934,8 @@ virHypervisorDriverPtr vboxGetHypervisorDriver(uint32_t uVersion)
                  uVersion);
         return NULL;
     }
+
     updateDriver();
+
     return &vboxCommonDriver;
 }
diff --git a/src/vbox/vbox_driver.c b/src/vbox/vbox_driver.c
index b3b4ee6..147a328 100644
--- a/src/vbox/vbox_driver.c
+++ b/src/vbox/vbox_driver.c
@@ -114,6 +114,7 @@ int vboxRegister(void)
     if (virRegisterConnectDriver(&vboxConnectDriver,
                                  false) < 0)
         return -1;
+
     return 0;
 }
 #endif
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 6892cc7..d44b58b 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -1978,21 +1978,8 @@ _registerDomainEvent(virHypervisorDriverPtr driver)
 
 #endif /* !(VBOX_API_VERSION == 2002000 || VBOX_API_VERSION >= 4000000) */
 
-static int _pfnInitialize(vboxGlobalData *data)
-{
-    data->pFuncs = g_pfnGetFunctions(VBOX_XPCOMC_VERSION);
-    if (data->pFuncs == NULL)
-        return -1;
-#if VBOX_XPCOMC_VERSION == 0x00010000U
-    data->pFuncs->pfnComInitialize(&data->vboxObj, &data->vboxSession);
-#else  /* !(VBOX_XPCOMC_VERSION == 0x00010000U) */
-    data->pFuncs->pfnComInitialize(IVIRTUALBOX_IID_STR, &data->vboxObj, ISESSION_IID_STR, &data->vboxSession);
-#endif /* !(VBOX_XPCOMC_VERSION == 0x00010000U) */
-    return 0;
-}
-
 static int
-_initializeDomainEvent(vboxGlobalData *data ATTRIBUTE_UNUSED)
+_initializeDomainEvent(vboxDriverPtr data ATTRIBUTE_UNUSED)
 {
 #if VBOX_API_VERSION <= 2002000 || VBOX_API_VERSION >= 4000000
     /* No event queue functionality in 2.2.* and 4.* as of now */
@@ -2631,10 +2618,50 @@ _detachFloppy(IMachine *machine ATTRIBUTE_UNUSED)
 
 #endif  /* VBOX_API_VERSION >= 3001000 */
 
-static void _pfnUninitialize(vboxGlobalData *data)
+static int _pfnInitialize(vboxDriverPtr driver)
 {
-    if (data->pFuncs)
+    if (!(driver->pFuncs = g_pfnGetFunctions(VBOX_XPCOMC_VERSION)))
+        return -1;
+#if VBOX_API_VERSION == 4002020 || VBOX_API_VERSION >= 4004004
+    nsresult rc;
+
+    rc = driver->pFuncs->pfnClientInitialize(IVIRTUALBOXCLIENT_IID_STR,
+                                             &driver->vboxClient);
+
+    if (NS_FAILED(rc)) {
+        return -1;
+    } else {
+        driver->vboxClient->vtbl->GetVirtualBox(driver->vboxClient, &driver->vboxObj);
+        driver->vboxClient->vtbl->GetSession(driver->vboxClient, &driver->vboxSession);
+    }
+
+#else
+
+# if VBOX_XPCOMC_VERSION == 0x00010000U
+    driver->pFuncs->pfnComInitialize(&driver->vboxObj, &driver->vboxSession);
+# else  /* !(VBOX_XPCOMC_VERSION == 0x00010000U) */
+    driver->pFuncs->pfnComInitialize(IVIRTUALBOX_IID_STR, &driver->vboxObj,
+                                     ISESSION_IID_STR, &driver->vboxSession);
+# endif /* !(VBOX_XPCOMC_VERSION == 0x00010000U) */
+
+#endif
+
+    return 0;
+}
+
+static void _pfnUninitialize(vboxDriverPtr data)
+{
+    if (data->pFuncs) {
+#if VBOX_API_VERSION == 4002020 || VBOX_API_VERSION >= 4003004
+        VBOX_RELEASE(data->vboxObj);
+        VBOX_RELEASE(data->vboxSession);
+        VBOX_RELEASE(data->vboxClient);
+
+        data->pFuncs->pfnClientUninitialize();
+#else
         data->pFuncs->pfnComUninitialize();
+#endif
+    }
 }
 
 static void _pfnComUnallocMem(PCVBOXXPCOM pFuncs, void *pv)
diff --git a/src/vbox/vbox_uniformed_api.h b/src/vbox/vbox_uniformed_api.h
index 8ec1533..075d4f6 100644
--- a/src/vbox/vbox_uniformed_api.h
+++ b/src/vbox/vbox_uniformed_api.h
@@ -136,14 +136,46 @@ typedef struct {
 
 } vboxGlobalData;
 
+struct _vboxDriver {
+    virObjectLockable parent;
+
+    virCapsPtr caps;
+    virDomainXMLOptionPtr xmlopt;
+    virObjectEventStatePtr domainEventState;
+
+    /* vbox API initialization members */
+    PCVBOXXPCOM pFuncs;
+    IVirtualBox *vboxObj;
+    ISession *vboxSession;
+# if VBOX_API_VERSION == 4002020 || VBOX_API_VERSION >= 4003004
+    IVirtualBoxClient *vboxClient;
+# endif
+
+    int fdWatch;
+# if VBOX_API_VERSION > 2002000 && VBOX_API_VERSION < 4000000
+    IVirtualBoxCallback *vboxCallback;
+    nsIEventQueue *vboxQueue;
+# else
+    void *vboxCallback;
+    void *vboxQueue;
+# endif
+    unsigned long version;
+
+    /* reference counting of vbox connections */
+    int volatile connectionCount;
+};
+
+typedef struct _vboxDriver vboxDriver;
+typedef struct _vboxDriver *vboxDriverPtr;
+
 /* vboxUniformedAPI gives vbox_common.c a uniformed layer to see
  * vbox API.
  */
 
 /* Functions for pFuncs */
 typedef struct {
-    int (*Initialize)(vboxGlobalData *data);
-    void (*Uninitialize)(vboxGlobalData *data);
+    int (*Initialize)(vboxDriverPtr driver);
+    void (*Uninitialize)(vboxDriverPtr driver);
     void (*ComUnallocMem)(PCVBOXXPCOM pFuncs, void *pv);
     void (*Utf16Free)(PCVBOXXPCOM pFuncs, PRUnichar *pwszString);
     void (*Utf8Free)(PCVBOXXPCOM pFuncs, char *pszString);
@@ -554,7 +586,7 @@ typedef struct {
     uint32_t APIVersion;
     uint32_t XPCOMCVersion;
     /* vbox APIs */
-    int (*initializeDomainEvent)(vboxGlobalData *data);
+    int (*initializeDomainEvent)(vboxDriverPtr driver);
     void (*registerGlobalData)(vboxGlobalData *data);
     void (*detachDevices)(vboxGlobalData *data, IMachine *machine, PRUnichar *hddcnameUtf16);
     nsresult (*unregisterMachine)(vboxGlobalData *data, vboxIIDUnion *iidu, IMachine **machine);
-- 
2.9.3




More information about the libvir-list mailing list