[libvirt] [PATCH 4/6] qemu: use virObject to manage reference-counting for qemu monitor
Hu Tao
hutao at cn.fujitsu.com
Fri Apr 8 03:07:44 UTC 2011
On Thu, Apr 07, 2011 at 10:38:34AM +0100, Daniel P. Berrange wrote:
> On Wed, Apr 06, 2011 at 03:19:55PM +0800, Hu Tao wrote:
> > ---
> > src/qemu/qemu_domain.c | 30 ++-----------
> > src/qemu/qemu_migration.c | 2 -
> > src/qemu/qemu_monitor.c | 109 ++++++++++++++++++++++++--------------------
> > src/qemu/qemu_monitor.h | 4 +-
> > src/qemu/qemu_process.c | 32 +++++++-------
> > 5 files changed, 81 insertions(+), 96 deletions(-)
> >
> > diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> > index 3a3c953..d11dc5f 100644
> > --- a/src/qemu/qemu_domain.c
> > +++ b/src/qemu/qemu_domain.c
> > @@ -560,9 +560,8 @@ void qemuDomainObjEnterMonitor(virDomainObjPtr obj)
> > {
> > qemuDomainObjPrivatePtr priv = obj->privateData;
> >
> > - qemuMonitorLock(priv->mon);
> > - qemuMonitorRef(priv->mon);
> > virDomainObjUnlock(obj);
> > + qemuMonitorLock(priv->mon);
> > }
> >
> >
> > @@ -573,18 +572,9 @@ void qemuDomainObjEnterMonitor(virDomainObjPtr obj)
> > void qemuDomainObjExitMonitor(virDomainObjPtr obj)
> > {
> > qemuDomainObjPrivatePtr priv = obj->privateData;
> > - int refs;
> > -
> > - refs = qemuMonitorUnref(priv->mon);
> > -
> > - if (refs > 0)
> > - qemuMonitorUnlock(priv->mon);
> >
> > + qemuMonitorUnlock(priv->mon);
> > virDomainObjLock(obj);
> > -
> > - if (refs == 0) {
> > - priv->mon = NULL;
> > - }
> > }
> >
> >
> > @@ -601,10 +591,8 @@ void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver,
> > {
> > qemuDomainObjPrivatePtr priv = obj->privateData;
> >
> > - qemuMonitorLock(priv->mon);
> > - qemuMonitorRef(priv->mon);
> > virDomainObjUnlock(obj);
> > - qemuDriverUnlock(driver);
> > + qemuMonitorLock(priv->mon);
> > }
> >
> >
> > @@ -617,19 +605,9 @@ void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver,
> > virDomainObjPtr obj)
> > {
> > qemuDomainObjPrivatePtr priv = obj->privateData;
> > - int refs;
> > -
> > - refs = qemuMonitorUnref(priv->mon);
> > -
> > - if (refs > 0)
> > - qemuMonitorUnlock(priv->mon);
> >
> > - qemuDriverLock(driver);
> > + qemuMonitorUnlock(priv->mon);
> > virDomainObjLock(obj);
> > -
> > - if (refs == 0) {
> > - priv->mon = NULL;
> > - }
> > }
>
> This means that the 'driver' lock is now whenever any QEMU
> monitor command is runing, which blocks the entire driver.
qemuDomainObjEnterMonitorWithDriver is now called without holding qemu
driver lock.
>
> >
> > void qemuDomainObjEnterRemoteWithDriver(struct qemud_driver *driver,
> > diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
> > index 462e6be..6af2e24 100644
> > --- a/src/qemu/qemu_migration.c
> > +++ b/src/qemu/qemu_migration.c
> > @@ -224,11 +224,9 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm)
> > }
> >
> > virDomainObjUnlock(vm);
> > - qemuDriverUnlock(driver);
> >
> > nanosleep(&ts, NULL);
> >
> > - qemuDriverLock(driver);
> > virDomainObjLock(vm);
> > }
>
> Holding the 'driver' lock while sleeping blocks the entire
> QEMU driver.
Now qemuMigrationWaitForCompletion should be called without holding qemu
driver lock.
>
> > diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
> > index 244b22a..4b9087f 100644
> > --- a/src/qemu/qemu_process.c
> > +++ b/src/qemu/qemu_process.c
> > @@ -107,7 +107,6 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> >
> > VIR_DEBUG("Received EOF on %p '%s'", vm, vm->def->name);
> >
> > - qemuDriverLock(driver);
> > virDomainObjLock(vm);
> >
> > if (!virDomainObjIsActive(vm)) {
> > @@ -133,15 +132,17 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
> > qemuProcessStop(driver, vm, 0);
> > qemuAuditDomainStop(vm, hasError ? "failed" : "shutdown");
> >
> > - if (!vm->persistent)
> > + if (!vm->persistent) {
> > + qemuDriverLock(driver);
> > virDomainRemoveInactive(&driver->domains, vm);
> > - else
> > - virDomainObjUnlock(vm);
> > + qemuDriverUnlock(driver);
> > + }
> > +
> > + virDomainObjUnlock(vm);
> >
> > if (event) {
> > qemuDomainEventQueue(driver, event);
> > }
> > - qemuDriverUnlock(driver);
> > }
>
> This violates the lock ordering rules. The 'driver' lock *must* be
> obtained *before* any 'vm' lock is held.
Excepting for introducing virObject for reference-counting, this series
also simplifies the usage of lock: if you want to read/write qemu
driver data, it is enough to first acquire qemu driver lock only; if you
want to read/write virDomainObj data, it is enough to first acquire
virDomainObj lock only; same for others. And we'd better to avoid
acquiring two locks at the same time.
So yes, the code here is problematic, it should be ideally like this:
virDomainObjLock(vm);
if (!vm->persistent) {
lock_hashtable(doms); /* hashtable's own lock to protect itself */
virDomainRemoveInactive(doms, vm);
unlock_hashtable(doms);
}
virDomainObjUnlock(vm);
But it lacks hashtable lock, how about change the code like this:
virDomainObjLock(vm);
persistent = vm->persistent;
virDomainObjUnlock(vm);
/* chances that others change vm->persistent and we remove
vm mistakenly :( */
if (!persistent) {
qemuDriverLock(driver);
virDomainRemoveInactive(doms, vm);
qemuDriverUnlock(driver);
}
Or is there a better way?
>
> Now we have some places in the code which do
>
> lock(vm)
> lock(driver)
>
> and other places which do
>
> lock(driver)
> lock(vm)
>
> so 2 threads can trivially deadlock waiting for each other
>
> Daniel
> --
> |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :|
> |: http://libvirt.org -o- http://virt-manager.org :|
> |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
> |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
More information about the libvir-list
mailing list