mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-03-20 07:59:00 +00:00
Only add the timer when a callback is registered
The lifetime of the virDomainEventState object is tied to the lifetime of the driver, which in stateless drivers is tied to the lifetime of the virConnectPtr. If we add & remove a timer when allocating/freeing the virDomainEventState object, we can get a situation where the timer still triggers once after virDomainEventState has been freed. The timeout callback can't keep a ref on the event state though, since that would be a circular reference. The trick is to only register the timer when a callback is registered with the event state & remove the timer when the callback is unregistered. The demo for the bug is to run while true ; do date ; ../tools/virsh -q -c test:///default 'shutdown test; undefine test; dominfo test' ; done prior to this fix, it will frequently hang and / or crash, or corrupt memory
This commit is contained in:
parent
34ad13536e
commit
707781fe12
@ -632,13 +632,9 @@ virDomainEventTimer(int timer ATTRIBUTE_UNUSED, void *opaque)
|
||||
|
||||
/**
|
||||
* virDomainEventStateNew:
|
||||
* @requireTimer: If true, return an error if registering the timer fails.
|
||||
* This is fatal for drivers that sit behind the daemon
|
||||
* (qemu, lxc), since there should always be a event impl
|
||||
* registered.
|
||||
*/
|
||||
virDomainEventStatePtr
|
||||
virDomainEventStateNew(bool requireTimer)
|
||||
virDomainEventStateNew(void)
|
||||
{
|
||||
virDomainEventStatePtr state = NULL;
|
||||
|
||||
@ -659,23 +655,10 @@ virDomainEventStateNew(bool requireTimer)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(state->queue = virDomainEventQueueNew())) {
|
||||
if (!(state->queue = virDomainEventQueueNew()))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((state->timer = virEventAddTimeout(-1,
|
||||
virDomainEventTimer,
|
||||
state,
|
||||
NULL)) < 0) {
|
||||
if (requireTimer == false) {
|
||||
VIR_DEBUG("virEventAddTimeout failed: No addTimeoutImpl defined. "
|
||||
"continuing without events.");
|
||||
} else {
|
||||
eventReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not initialize domain event timer"));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
state->timer = -1;
|
||||
|
||||
return state;
|
||||
|
||||
@ -1314,7 +1297,6 @@ virDomainEventStateFlush(virDomainEventStatePtr state)
|
||||
state->queue->count = 0;
|
||||
state->queue->events = NULL;
|
||||
virEventUpdateTimeout(state->timer, -1);
|
||||
virDomainEventStateUnlock(state);
|
||||
|
||||
virDomainEventQueueDispatch(&tempQueue,
|
||||
state->callbacks,
|
||||
@ -1322,7 +1304,6 @@ virDomainEventStateFlush(virDomainEventStatePtr state)
|
||||
state);
|
||||
|
||||
/* Purge any deleted callbacks */
|
||||
virDomainEventStateLock(state);
|
||||
virDomainEventCallbackListPurgeMarked(state->callbacks);
|
||||
|
||||
state->isDispatching = false;
|
||||
@ -1350,10 +1331,32 @@ virDomainEventStateRegister(virConnectPtr conn,
|
||||
void *opaque,
|
||||
virFreeCallback freecb)
|
||||
{
|
||||
int ret;
|
||||
int ret = -1;
|
||||
|
||||
virDomainEventStateLock(state);
|
||||
|
||||
if ((state->callbacks->count == 0) &&
|
||||
(state->timer == -1) &&
|
||||
(state->timer = virEventAddTimeout(-1,
|
||||
virDomainEventTimer,
|
||||
state,
|
||||
NULL)) < 0) {
|
||||
eventReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not initialize domain event timer"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = virDomainEventCallbackListAdd(conn, state->callbacks,
|
||||
callback, opaque, freecb);
|
||||
|
||||
if (ret == -1 &&
|
||||
state->callbacks->count == 0 &&
|
||||
state->timer != -1) {
|
||||
virEventRemoveTimeout(state->timer);
|
||||
state->timer = -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
virDomainEventStateUnlock(state);
|
||||
return ret;
|
||||
}
|
||||
@ -1384,11 +1387,33 @@ virDomainEventStateRegisterID(virConnectPtr conn,
|
||||
virFreeCallback freecb,
|
||||
int *callbackID)
|
||||
{
|
||||
int ret;
|
||||
int ret = -1;
|
||||
|
||||
virDomainEventStateLock(state);
|
||||
|
||||
if ((state->callbacks->count == 0) &&
|
||||
(state->timer == -1) &&
|
||||
(state->timer = virEventAddTimeout(-1,
|
||||
virDomainEventTimer,
|
||||
state,
|
||||
NULL)) < 0) {
|
||||
eventReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("could not initialize domain event timer"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = virDomainEventCallbackListAddID(conn, state->callbacks,
|
||||
dom, eventID, cb, opaque, freecb,
|
||||
callbackID);
|
||||
|
||||
if (ret == -1 &&
|
||||
state->callbacks->count == 0 &&
|
||||
state->timer != -1) {
|
||||
virEventRemoveTimeout(state->timer);
|
||||
state->timer = -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
virDomainEventStateUnlock(state);
|
||||
return ret;
|
||||
}
|
||||
@ -1418,6 +1443,13 @@ virDomainEventStateDeregister(virConnectPtr conn,
|
||||
state->callbacks, callback);
|
||||
else
|
||||
ret = virDomainEventCallbackListRemove(conn, state->callbacks, callback);
|
||||
|
||||
if (state->callbacks->count == 0 &&
|
||||
state->timer != -1) {
|
||||
virEventRemoveTimeout(state->timer);
|
||||
state->timer = -1;
|
||||
}
|
||||
|
||||
virDomainEventStateUnlock(state);
|
||||
return ret;
|
||||
}
|
||||
@ -1448,6 +1480,13 @@ virDomainEventStateDeregisterID(virConnectPtr conn,
|
||||
else
|
||||
ret = virDomainEventCallbackListRemoveID(conn,
|
||||
state->callbacks, callbackID);
|
||||
|
||||
if (state->callbacks->count == 0 &&
|
||||
state->timer != -1) {
|
||||
virEventRemoveTimeout(state->timer);
|
||||
state->timer = -1;
|
||||
}
|
||||
|
||||
virDomainEventStateUnlock(state);
|
||||
return ret;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ void virDomainEventFree(virDomainEventPtr event);
|
||||
|
||||
void virDomainEventStateFree(virDomainEventStatePtr state);
|
||||
virDomainEventStatePtr
|
||||
virDomainEventStateNew(bool requireTimer);
|
||||
virDomainEventStateNew(void);
|
||||
|
||||
void
|
||||
virDomainEventStateQueue(virDomainEventStatePtr state,
|
||||
|
@ -928,7 +928,7 @@ libxlStartup(int privileged) {
|
||||
}
|
||||
VIR_FREE(log_file);
|
||||
|
||||
libxl_driver->domainEventState = virDomainEventStateNew(true);
|
||||
libxl_driver->domainEventState = virDomainEventStateNew();
|
||||
if (!libxl_driver->domainEventState)
|
||||
goto error;
|
||||
|
||||
|
@ -2418,7 +2418,7 @@ static int lxcStartup(int privileged)
|
||||
if (virDomainObjListInit(&lxc_driver->domains) < 0)
|
||||
goto cleanup;
|
||||
|
||||
lxc_driver->domainEventState = virDomainEventStateNew(true);
|
||||
lxc_driver->domainEventState = virDomainEventStateNew();
|
||||
if (!lxc_driver->domainEventState)
|
||||
goto cleanup;
|
||||
|
||||
|
@ -431,7 +431,7 @@ qemudStartup(int privileged) {
|
||||
goto out_of_memory;
|
||||
|
||||
/* Init domain events */
|
||||
qemu_driver->domainEventState = virDomainEventStateNew(true);
|
||||
qemu_driver->domainEventState = virDomainEventStateNew();
|
||||
if (!qemu_driver->domainEventState)
|
||||
goto error;
|
||||
|
||||
|
@ -726,7 +726,7 @@ doRemoteOpen (virConnectPtr conn,
|
||||
}
|
||||
}
|
||||
|
||||
if (!(priv->domainEventState = virDomainEventStateNew(false)))
|
||||
if (!(priv->domainEventState = virDomainEventStateNew()))
|
||||
goto failed;
|
||||
|
||||
/* Successful. */
|
||||
|
@ -1137,7 +1137,7 @@ static virDrvOpenStatus testOpen(virConnectPtr conn,
|
||||
privconn = conn->privateData;
|
||||
testDriverLock(privconn);
|
||||
|
||||
privconn->domainEventState = virDomainEventStateNew(false);
|
||||
privconn->domainEventState = virDomainEventStateNew();
|
||||
if (!privconn->domainEventState) {
|
||||
testDriverUnlock(privconn);
|
||||
testClose(conn);
|
||||
|
@ -413,7 +413,7 @@ umlStartup(int privileged)
|
||||
if (virDomainObjListInit(¨_driver->domains) < 0)
|
||||
goto error;
|
||||
|
||||
uml_driver->domainEventState = virDomainEventStateNew(true);
|
||||
uml_driver->domainEventState = virDomainEventStateNew();
|
||||
if (!uml_driver->domainEventState)
|
||||
goto error;
|
||||
|
||||
|
@ -1034,7 +1034,7 @@ static virDrvOpenStatus vboxOpen(virConnectPtr conn,
|
||||
|
||||
#else /* !(VBOX_API_VERSION == 2002) */
|
||||
|
||||
if (!(data->domainEvents = virDomainEventStateNew(true))) {
|
||||
if (!(data->domainEvents = virDomainEventStateNew())) {
|
||||
vboxUninitialize(data);
|
||||
return VIR_DRV_OPEN_ERROR;
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ xenUnifiedOpen (virConnectPtr conn, virConnectAuthPtr auth, unsigned int flags)
|
||||
return VIR_DRV_OPEN_ERROR;
|
||||
}
|
||||
|
||||
if (!(priv->domainEvents = virDomainEventStateNew(true))) {
|
||||
if (!(priv->domainEvents = virDomainEventStateNew())) {
|
||||
virMutexDestroy(&priv->lock);
|
||||
VIR_FREE(priv);
|
||||
return VIR_DRV_OPEN_ERROR;
|
||||
|
Loading…
x
Reference in New Issue
Block a user