提交 79533da1 编写于 作者: D Daniel P. Berrange

Fix crash when deleting monitor while a command is in progress

If QEMU shuts down while we're in the middle of processing a
monitor command, the monitor will be freed, and upon cleaning
up we attempt to do  qemuMonitorUnlock(priv->mon) when priv->mon
is NULL.

To address this we introduce proper reference counting into
the qemuMonitorPtr object, and hold an extra reference whenever
executing a command.

* src/qemu/qemu_driver.c: Hold a reference on the monitor while
  executing commands, and only NULL-ify the priv->mon field when
  the last reference is released
* src/qemu/qemu_monitor.h, src/qemu/qemu_monitor.c: Add reference
  counting to handle safe deletion of monitor objects
上级 1f604116
...@@ -272,6 +272,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) ...@@ -272,6 +272,7 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj)
qemuDomainObjPrivatePtr priv = obj->privateData; qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); qemuMonitorLock(priv->mon);
qemuMonitorRef(priv->mon);
virDomainObjUnlock(obj); virDomainObjUnlock(obj);
} }
...@@ -283,9 +284,19 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) ...@@ -283,9 +284,19 @@ static void qemuDomainObjEnterMonitor(virDomainObjPtr obj)
static void qemuDomainObjExitMonitor(virDomainObjPtr obj) static void qemuDomainObjExitMonitor(virDomainObjPtr obj)
{ {
qemuDomainObjPrivatePtr priv = obj->privateData; qemuDomainObjPrivatePtr priv = obj->privateData;
int refs;
refs = qemuMonitorUnref(priv->mon);
if (refs > 0)
qemuMonitorUnlock(priv->mon);
qemuMonitorUnlock(priv->mon);
virDomainObjLock(obj); virDomainObjLock(obj);
if (refs == 0) {
virDomainObjUnref(obj);
priv->mon = NULL;
}
} }
...@@ -302,6 +313,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir ...@@ -302,6 +313,7 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir
qemuDomainObjPrivatePtr priv = obj->privateData; qemuDomainObjPrivatePtr priv = obj->privateData;
qemuMonitorLock(priv->mon); qemuMonitorLock(priv->mon);
qemuMonitorRef(priv->mon);
virDomainObjUnlock(obj); virDomainObjUnlock(obj);
qemuDriverUnlock(driver); qemuDriverUnlock(driver);
} }
...@@ -315,10 +327,20 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir ...@@ -315,10 +327,20 @@ static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, vir
static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj)
{ {
qemuDomainObjPrivatePtr priv = obj->privateData; qemuDomainObjPrivatePtr priv = obj->privateData;
int refs;
refs = qemuMonitorUnref(priv->mon);
if (refs > 0)
qemuMonitorUnlock(priv->mon);
qemuMonitorUnlock(priv->mon);
qemuDriverLock(driver); qemuDriverLock(driver);
virDomainObjLock(obj); virDomainObjLock(obj);
if (refs == 0) {
virDomainObjUnref(obj);
priv->mon = NULL;
}
} }
...@@ -653,6 +675,10 @@ qemuConnectMonitor(virDomainObjPtr vm) ...@@ -653,6 +675,10 @@ qemuConnectMonitor(virDomainObjPtr vm)
{ {
qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjPrivatePtr priv = vm->privateData;
/* Hold an extra reference because we can't allow 'vm' to be
* deleted while the monitor is active */
virDomainObjRef(vm);
if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) { if ((priv->mon = qemuMonitorOpen(vm, qemuHandleMonitorEOF)) == NULL) {
VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name);
return -1; return -1;
...@@ -2400,8 +2426,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, ...@@ -2400,8 +2426,9 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
_("Failed to send SIGTERM to %s (%d)"), _("Failed to send SIGTERM to %s (%d)"),
vm->def->name, vm->pid); vm->def->name, vm->pid);
if (priv->mon) { if (priv->mon &&
qemuMonitorClose(priv->mon); qemuMonitorClose(priv->mon) == 0) {
virDomainObjUnref(vm);
priv->mon = NULL; priv->mon = NULL;
} }
......
...@@ -42,7 +42,7 @@ struct _qemuMonitor { ...@@ -42,7 +42,7 @@ struct _qemuMonitor {
virMutex lock; virMutex lock;
virCond notify; virCond notify;
virDomainObjPtr dom; int refs;
int fd; int fd;
int watch; int watch;
...@@ -67,12 +67,14 @@ struct _qemuMonitor { ...@@ -67,12 +67,14 @@ struct _qemuMonitor {
* the next monitor msg */ * the next monitor msg */
int lastErrno; int lastErrno;
/* If the monitor callback is currently active */ /* If the monitor EOF callback is currently active (stops more commands being run) */
unsigned eofcb: 1; unsigned eofcb: 1;
/* If the monitor callback should free the closed monitor */ /* If the monitor is in process of shutting down */
unsigned closed: 1; unsigned closed: 1;
}; };
void qemuMonitorLock(qemuMonitorPtr mon) void qemuMonitorLock(qemuMonitorPtr mon)
{ {
virMutexLock(&mon->lock); virMutexLock(&mon->lock);
...@@ -84,21 +86,34 @@ void qemuMonitorUnlock(qemuMonitorPtr mon) ...@@ -84,21 +86,34 @@ void qemuMonitorUnlock(qemuMonitorPtr mon)
} }
static void qemuMonitorFree(qemuMonitorPtr mon, int lockDomain) static void qemuMonitorFree(qemuMonitorPtr mon)
{ {
VIR_DEBUG("mon=%p, lockDomain=%d", mon, lockDomain); VIR_DEBUG("mon=%p", mon);
if (mon->vm) {
if (lockDomain)
virDomainObjLock(mon->vm);
if (!virDomainObjUnref(mon->vm) && lockDomain)
virDomainObjUnlock(mon->vm);
}
if (virCondDestroy(&mon->notify) < 0) if (virCondDestroy(&mon->notify) < 0)
{} {}
virMutexDestroy(&mon->lock); virMutexDestroy(&mon->lock);
VIR_FREE(mon); VIR_FREE(mon);
} }
int qemuMonitorRef(qemuMonitorPtr mon)
{
mon->refs++;
return mon->refs;
}
int qemuMonitorUnref(qemuMonitorPtr mon)
{
mon->refs--;
if (mon->refs == 0) {
qemuMonitorUnlock(mon);
qemuMonitorFree(mon);
return 0;
}
return mon->refs;
}
static int static int
qemuMonitorOpenUnix(const char *monitor) qemuMonitorOpenUnix(const char *monitor)
...@@ -348,6 +363,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { ...@@ -348,6 +363,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) {
int quit = 0, failed = 0; int quit = 0, failed = 0;
qemuMonitorLock(mon); qemuMonitorLock(mon);
qemuMonitorRef(mon);
VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events); VIR_DEBUG("Monitor %p I/O on watch %d fd %d events %d", mon, watch, fd, events);
if (mon->fd != fd || mon->watch != watch) { if (mon->fd != fd || mon->watch != watch) {
...@@ -407,23 +423,17 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { ...@@ -407,23 +423,17 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) {
* but is this safe ? I think it is, because the callback * but is this safe ? I think it is, because the callback
* will try to acquire the virDomainObjPtr mutex next */ * will try to acquire the virDomainObjPtr mutex next */
if (failed || quit) { if (failed || quit) {
qemuMonitorEOFNotify eofCB = mon->eofCB;
virDomainObjPtr vm = mon->vm;
/* Make sure anyone waiting wakes up now */ /* Make sure anyone waiting wakes up now */
virCondSignal(&mon->notify); virCondSignal(&mon->notify);
mon->eofcb = 1; if (qemuMonitorUnref(mon) > 0)
qemuMonitorUnlock(mon);
VIR_DEBUG("Triggering EOF callback error? %d", failed);
mon->eofCB(mon, mon->vm, failed);
qemuMonitorLock(mon);
if (mon->closed) {
qemuMonitorUnlock(mon); qemuMonitorUnlock(mon);
VIR_DEBUG("Delayed free of monitor %p", mon); VIR_DEBUG("Triggering EOF callback error? %d", failed);
qemuMonitorFree(mon, 1); (eofCB)(mon, vm, failed);
} else {
qemuMonitorUnlock(mon);
}
} else { } else {
qemuMonitorUnlock(mon); if (qemuMonitorUnref(mon) > 0)
qemuMonitorUnlock(mon);
} }
} }
...@@ -453,10 +463,10 @@ qemuMonitorOpen(virDomainObjPtr vm, ...@@ -453,10 +463,10 @@ qemuMonitorOpen(virDomainObjPtr vm,
return NULL; return NULL;
} }
mon->fd = -1; mon->fd = -1;
mon->refs = 1;
mon->vm = vm; mon->vm = vm;
mon->eofCB = eofCB; mon->eofCB = eofCB;
qemuMonitorLock(mon); qemuMonitorLock(mon);
virDomainObjRef(vm);
switch (vm->monitor_chr->type) { switch (vm->monitor_chr->type) {
case VIR_DOMAIN_CHR_TYPE_UNIX: case VIR_DOMAIN_CHR_TYPE_UNIX:
...@@ -512,10 +522,14 @@ cleanup: ...@@ -512,10 +522,14 @@ cleanup:
} }
void qemuMonitorClose(qemuMonitorPtr mon) int qemuMonitorClose(qemuMonitorPtr mon)
{ {
int refs;
if (!mon) if (!mon)
return; return 0;
VIR_DEBUG("mon=%p", mon);
qemuMonitorLock(mon); qemuMonitorLock(mon);
if (!mon->closed) { if (!mon->closed) {
...@@ -523,18 +537,17 @@ void qemuMonitorClose(qemuMonitorPtr mon) ...@@ -523,18 +537,17 @@ void qemuMonitorClose(qemuMonitorPtr mon)
virEventRemoveHandle(mon->watch); virEventRemoveHandle(mon->watch);
if (mon->fd != -1) if (mon->fd != -1)
close(mon->fd); close(mon->fd);
/* NB: don't reset fd / watch fields, since active /* NB: ordinarily one might immediately set mon->watch to -1
* callback may still want them */ * and mon->fd to -1, but there may be a callback active
* that is still relying on these fields being valid. So
* we merely close them, but not clear their values and
* use this explicit 'closed' flag to track this state */
mon->closed = 1; mon->closed = 1;
} }
if (mon->eofcb) { if ((refs = qemuMonitorUnref(mon)) > 0)
VIR_DEBUG("Mark monitor to be deleted %p", mon);
qemuMonitorUnlock(mon); qemuMonitorUnlock(mon);
} else { return refs;
VIR_DEBUG("Delete monitor now %p", mon);
qemuMonitorFree(mon, 0);
}
} }
...@@ -552,7 +565,6 @@ int qemuMonitorSend(qemuMonitorPtr mon, ...@@ -552,7 +565,6 @@ int qemuMonitorSend(qemuMonitorPtr mon,
if (mon->eofcb) { if (mon->eofcb) {
msg->lastErrno = EIO; msg->lastErrno = EIO;
qemuMonitorUnlock(mon);
return -1; return -1;
} }
......
...@@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon, ...@@ -78,11 +78,14 @@ typedef int (*qemuMonitorDiskSecretLookup)(qemuMonitorPtr mon,
qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm,
qemuMonitorEOFNotify eofCB); qemuMonitorEOFNotify eofCB);
void qemuMonitorClose(qemuMonitorPtr mon); int qemuMonitorClose(qemuMonitorPtr mon);
void qemuMonitorLock(qemuMonitorPtr mon); void qemuMonitorLock(qemuMonitorPtr mon);
void qemuMonitorUnlock(qemuMonitorPtr mon); void qemuMonitorUnlock(qemuMonitorPtr mon);
int qemuMonitorRef(qemuMonitorPtr mon);
int qemuMonitorUnref(qemuMonitorPtr mon);
void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon, void qemuMonitorRegisterDiskSecretLookup(qemuMonitorPtr mon,
qemuMonitorDiskSecretLookup secretCB); qemuMonitorDiskSecretLookup secretCB);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册