提交 04518c36 编写于 作者: D Dawid Zamirski 提交者: John Ferlan

vbox: change how vbox API is initialized.

* 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 uninitialize 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.
上级 b270ef99
......@@ -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;
}
......@@ -114,6 +114,7 @@ int vboxRegister(void)
if (virRegisterConnectDriver(&vboxConnectDriver,
false) < 0)
return -1;
return 0;
}
#endif
......@@ -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)
......
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册