Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-12 Thread Michal Privoznik
On 02/09/2018 02:00 PM, John Ferlan wrote:
> 
> 
> On 02/09/2018 03:41 AM, Michal Privoznik wrote:
>> On 02/08/2018 04:06 PM, John Ferlan wrote:
>>> [...]
>>>
>>> +static void
>>> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
>>> +{
>>> +virObjectRWUnlock(obj);
>>> +virObjectRWLockWrite(obj);
>>> +}
>>
>> How can this not deadlock? This will work only if @obj is locked exactly
>> once. But since we allow recursive locks any lock() that happens in the
>> 2nd level must deadlock with this. On the other hand, there's no such
>> case in the code currently. But that doesn't stop anybody from calling
>> PromoteWrite() without understanding its limitations.
>>
>> Maybe the fact that we need recursive lock for NWFilterObj means it's
>> not a good candidate for virObjectRWLocable? It is still a good
>> candidate for virObject though.
>>
>> Or if you want to spend extra time on this, maybe introducing
>> virObjectRecursiveLockable may be worth it (terrible name, I know).
>>
>>
>
> I dunno, worked for me. The helper is being called from a thread that
> has taken out a READ lock at most one time and needs to get the WRITE
> lock in order to change things. If something else has the READ lock that
> thread waits until the other thread releases the READ lock as far as I
> understand things.

 Yes, I can see that. It's just that since the original lock is recursive
 I expected things to get recursive and thus I've been thinking how would
 this work there, nested in 2nd, 3rd, .. level. But as noted earlier, the
 places where lock promoting is used are kind of safe since all the
 locking is done at the first level.

>>>
>>> The reason I chose rwlocks and taking a read lock by default in the
>>> FindByUUID and FindByName (as opposed to other vir*FindBy* API's which
>>> return a write locked object) is because I know there's the potential
>>> for other code to be doing Reads and rwlocks allowed for that without
>>> the need to create any sort of recursive lockable object or any sort of
>>> API to perform "trylock"'s (all things I tried until the epiphany to use
>>> rwlocks was reached when you added them for other drivers and their list
>>> protection).
>>>
>>> In the long/short run I figured that having a common way to get the lock
>>> and then deciding to change it from Read to Write as necessary would
>>> work just fine for the purpose that was needed.
>>
>> This is where I disagree. I don't think it's safe to promote a lock by
>> unlocking it. The moment the lock is unlocked another thread will lock
>> the object and all the assumptions we made/checked about the object we
>> made when we had the object locked are gone. So we would need to
>> reiterate the decision making process.
>>
> 
> OK - so let's think about where/why/when we promote the lock and what
> the checks/balances/consequences are. 

I know. I am saying that all along that with the current situation it is
safe to have lock promoting. But in general (and possibly to save future
us from nasty deadlocks) it is not. And as I say in comment for 2/3 this
code is guarded by nwfilter driver lock anyway. So there is no race
possible anyway. However, I think I have some patches ready that
implement what is needed here. I'm gonna post them shortly and give you
all the credit - they are heavily based on your work.

Michal

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-09 Thread John Ferlan


On 02/09/2018 03:41 AM, Michal Privoznik wrote:
> On 02/08/2018 04:06 PM, John Ferlan wrote:
>> [...]
>>
>> +static void
>> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
>> +{
>> +virObjectRWUnlock(obj);
>> +virObjectRWLockWrite(obj);
>> +}
>
> How can this not deadlock? This will work only if @obj is locked exactly
> once. But since we allow recursive locks any lock() that happens in the
> 2nd level must deadlock with this. On the other hand, there's no such
> case in the code currently. But that doesn't stop anybody from calling
> PromoteWrite() without understanding its limitations.
>
> Maybe the fact that we need recursive lock for NWFilterObj means it's
> not a good candidate for virObjectRWLocable? It is still a good
> candidate for virObject though.
>
> Or if you want to spend extra time on this, maybe introducing
> virObjectRecursiveLockable may be worth it (terrible name, I know).
>
>

 I dunno, worked for me. The helper is being called from a thread that
 has taken out a READ lock at most one time and needs to get the WRITE
 lock in order to change things. If something else has the READ lock that
 thread waits until the other thread releases the READ lock as far as I
 understand things.
>>>
>>> Yes, I can see that. It's just that since the original lock is recursive
>>> I expected things to get recursive and thus I've been thinking how would
>>> this work there, nested in 2nd, 3rd, .. level. But as noted earlier, the
>>> places where lock promoting is used are kind of safe since all the
>>> locking is done at the first level.
>>>
>>
>> The reason I chose rwlocks and taking a read lock by default in the
>> FindByUUID and FindByName (as opposed to other vir*FindBy* API's which
>> return a write locked object) is because I know there's the potential
>> for other code to be doing Reads and rwlocks allowed for that without
>> the need to create any sort of recursive lockable object or any sort of
>> API to perform "trylock"'s (all things I tried until the epiphany to use
>> rwlocks was reached when you added them for other drivers and their list
>> protection).
>>
>> In the long/short run I figured that having a common way to get the lock
>> and then deciding to change it from Read to Write as necessary would
>> work just fine for the purpose that was needed.
> 
> This is where I disagree. I don't think it's safe to promote a lock by
> unlocking it. The moment the lock is unlocked another thread will lock
> the object and all the assumptions we made/checked about the object we
> made when we had the object locked are gone. So we would need to
> reiterate the decision making process.
> 

OK - so let's think about where/why/when we promote the lock and what
the checks/balances/consequences are.  There are 3 things being
protected by the lock in _virNWFilterObj (@wantRemoved, @def, and
@newDef). Each of promotion opportunities occur because the
virNWFilterTriggerVMFilterRebuild must be called when we attempt to
update or remove a filter.

1. virNWFilterObjTestUnassignDef

Changes the obj->wantRemoved flag. This is the only API that sets that
flag. The flag is checked by the virNWFilterObjWantRemoved that checked
as part of the virNWFilterObjListFindInstantiateFilter processing that
would would fail if it was determined the flag was set.

This essentially allows/disallows the nwfilterUndefine code to proceed
or not. The nwfilterUndefine code is further protected by a series of
locks that ensures one Undefine at a time. IOW: It's not possible to
have two threads mucking with this (from the comments in Undefine):

/* Serialization of *one* Undefine consumer is extremely important
 * as it relates to virNWFilterObjListFindInstantiateFilter processing
 * via virNWFilterTriggerVMFilterRebuild that occurs during
 * virNWFilterObjTestUnassignDef */
nwfilterDriverLock();
virNWFilterWriteLockFilterUpdates();
virNWFilterCallbackDriversLock();

2. virNWFilterObjListAssignDef

Changes to the obj->def/obj->newDef are made here.

Calls to the function are made from:

  2a. nwfilterDefineXML

Which not surprisingly has the following similar sequence:

/* Serialization of *one* DefineXML consumer is extremely important
 * as it relates to virNWFilterObjListFindInstantiateFilter processing
 * via virNWFilterTriggerVMFilterRebuild that occurs during
 * virNWFilterObjListAssignDef */
nwfilterDriverLock();
virNWFilterWriteLockFilterUpdates();
virNWFilterCallbackDriversLock();


  2b. virNWFilterObjListLoadConfig via virNWFilterObjListLoadAllConfigs

  Code path is from StateInitialization or StateReload. For Initialize I
think/hope we can agree - it's one path, no ability for define/undefine
to impede.

  For the Reload path, our similar sequence:

/* Serialization of virNWFilterObjListLoadAllConfigs is extremely
 * important as it relates to virNWFilterObjListFindInstantiateFilter
 * processing via 

Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-09 Thread Michal Privoznik
On 02/08/2018 04:06 PM, John Ferlan wrote:
> [...]
> 
> +static void
> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
> +{
> +virObjectRWUnlock(obj);
> +virObjectRWLockWrite(obj);
> +}

 How can this not deadlock? This will work only if @obj is locked exactly
 once. But since we allow recursive locks any lock() that happens in the
 2nd level must deadlock with this. On the other hand, there's no such
 case in the code currently. But that doesn't stop anybody from calling
 PromoteWrite() without understanding its limitations.

 Maybe the fact that we need recursive lock for NWFilterObj means it's
 not a good candidate for virObjectRWLocable? It is still a good
 candidate for virObject though.

 Or if you want to spend extra time on this, maybe introducing
 virObjectRecursiveLockable may be worth it (terrible name, I know).


>>>
>>> I dunno, worked for me. The helper is being called from a thread that
>>> has taken out a READ lock at most one time and needs to get the WRITE
>>> lock in order to change things. If something else has the READ lock that
>>> thread waits until the other thread releases the READ lock as far as I
>>> understand things.
>>
>> Yes, I can see that. It's just that since the original lock is recursive
>> I expected things to get recursive and thus I've been thinking how would
>> this work there, nested in 2nd, 3rd, .. level. But as noted earlier, the
>> places where lock promoting is used are kind of safe since all the
>> locking is done at the first level.
>>
> 
> The reason I chose rwlocks and taking a read lock by default in the
> FindByUUID and FindByName (as opposed to other vir*FindBy* API's which
> return a write locked object) is because I know there's the potential
> for other code to be doing Reads and rwlocks allowed for that without
> the need to create any sort of recursive lockable object or any sort of
> API to perform "trylock"'s (all things I tried until the epiphany to use
> rwlocks was reached when you added them for other drivers and their list
> protection).
> 
> In the long/short run I figured that having a common way to get the lock
> and then deciding to change it from Read to Write as necessary would
> work just fine for the purpose that was needed.

This is where I disagree. I don't think it's safe to promote a lock by
unlocking it. The moment the lock is unlocked another thread will lock
the object and all the assumptions we made/checked about the object we
made when we had the object locked are gone. So we would need to
reiterate the decision making process.

> 
>>>
>>> Back when I first did this I had quite a lot of debug code and I have a
>>> vague recollection of seeing output from this particular path and seeing
>>> a couple of unlocks before the WRITE was taken while running the avocado
>>> tests.
>>>
>>> I have zero interest in spending more time on this. That ship sailed. If
>>> the decision is to drop the patches, then fine. I tried.  I disagree
>>> that NWFilterObj is not a candidate for RWLockable - in fact it's quite
>>> the opposite *because* of the recursive locks.
>>
>> Well, there's a difference between recursive locks and rwlocks. And it's
>> exactly in what happens in recursion. With recursive locks we don't need
>> any lock promoting and thus it's safe to do read/write after lock().
>> With lock promoting (esp. done in unlock()+lockWrite() manner) we get
>> TOCTOU bugs.
>>
> 
> If we had a real lock manager available this wouldn't be a problem ;-)
> [sorry just my OpenVMS roots and bias showing].  Still if you read up a
> bit on "dlm" you'll get an idea of what I mean.
> 
> There's so many locks in the nwfilter code it's a wonder it all works.

Totally agreed.

> 
> It's truly unfortunate that the wrlock mechanism is undefined for
> promoting a read lock to write leaving the consumer with few options.

I believe pthread developers tried to avoid having any lock promoting
because it is hard to do right. It might break the definition of mutual
exclusion of readers and writers. I mean, imagine one thread would do:

lockRead();
lockRead();
lockPromote();

There are two possible solutions:
a) both read locks are promoted to write locks
b) only the second lock is promoted.

At any rate, either the lock is locked two times for writing, or for
reading and writing at the same time. On the other hand, since this is
done in one thread I guess it doesn't matter that much.


> 
> Since we can limit the "updating" of the fields in @obj and getting a
> write lock to very specific code paths, hopefully we can limit damage
> and/or deadlock type and/or TOCTOU issues

I'm working on what I suggested earlier - making this
virObjectRecursiveLockable. Let's see how that works out.

Michal

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread Stefan Berger

On 02/08/2018 08:30 AM, Daniel P. Berrangé wrote:

On Thu, Feb 08, 2018 at 02:13:58PM +0100, Michal Privoznik wrote:

On 02/06/2018 08:20 PM, John Ferlan wrote:

Unlike it's counterparts, the nwfilter object code needs to be able
to support recursive read locks while processing and checking new
filters against the existing environment. Thus instead of using a
virObjectLockable which uses pure mutexes, use the virObjectRWLockable
and primarily use RWLockRead when obtaining the object lock since
RWLockRead locks can be recursively obtained (up to a limit) as long
as there's a corresponding unlock.

Since all the object management is within the virnwfilterobj code, we
can limit the number of Write locks on the object to very small areas
of code to ensure we don't run into deadlock's with other threads and
domain code that will check/use the filters (it's a very delicate
balance). This limits the write locks to AssignDef and Remove processing.

This patch will introduce a new API virNWFilterObjEndAPI to unlock
and deref the object.

This patch introduces two helpers to promote/demote the read/write lock.

Signed-off-by: John Ferlan 
---
  src/conf/virnwfilterobj.c  | 193 +++--
  src/conf/virnwfilterobj.h  |   9 +-
  src/libvirt_private.syms   |   3 +-
  src/nwfilter/nwfilter_driver.c |  15 +--
  src/nwfilter/nwfilter_gentech_driver.c |  11 +-
  5 files changed, 153 insertions(+), 78 deletions(-)

diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
index 87d7e7270..cd52706ec 100644
--- a/src/conf/virnwfilterobj.c
+++ b/src/conf/virnwfilterobj.c
@@ -34,7 +34,7 @@
  VIR_LOG_INIT("conf.virnwfilterobj");
  
  struct _virNWFilterObj {

-virMutex lock;
+virObjectRWLockable parent;
  
  bool wantRemoved;
  
@@ -47,27 +47,69 @@ struct _virNWFilterObjList {

  virNWFilterObjPtr *objs;
  };
  
+static virClassPtr virNWFilterObjClass;

+static void virNWFilterObjDispose(void *opaque);
+
+
+static int
+virNWFilterObjOnceInit(void)
+{
+if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
+"virNWFilterObj",
+sizeof(virNWFilterObj),
+virNWFilterObjDispose)))
+return -1;
+
+return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
+
  
  static virNWFilterObjPtr

  virNWFilterObjNew(void)
  {
  virNWFilterObjPtr obj;
  
-if (VIR_ALLOC(obj) < 0)

+if (virNWFilterObjInitialize() < 0)
  return NULL;
  
-if (virMutexInitRecursive(>lock) < 0) {

-virReportError(VIR_ERR_INTERNAL_ERROR,
-   "%s", _("cannot initialize mutex"));
-VIR_FREE(obj);
+if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
  return NULL;
-}
  
-virNWFilterObjLock(obj);

+virObjectRWLockWrite(obj);
  return obj;
  }
  
  
+static void

+virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
+{
+virObjectRWUnlock(obj);
+virObjectRWLockWrite(obj);
+}

How can this not deadlock? This will work only if @obj is locked exactly
once. But since we allow recursive locks any lock() that happens in the
2nd level must deadlock with this. On the other hand, there's no such
case in the code currently. But that doesn't stop anybody from calling
PromoteWrite() without understanding its limitations.

Maybe the fact that we need recursive lock for NWFilterObj means it's
not a good candidate for virObjectRWLocable? It is still a good
candidate for virObject though.

Or if you want to spend extra time on this, maybe introducing
virObjectRecursiveLockable may be worth it (terrible name, I know).

I can't remember exactly call trace scenarios that meant we required
recursive locking. I vaguely recall is was related to fact that we
have an alternative entry point into the nwfilter code that's triggered
by the virt drivers. I'm thus vaguely hoping that when we split nwfilter
off into a separate daemon from virt driver, the need for recursive
lockingn would magically disappear. I could be completely wrong though :-)


The nwfilter code tries to detect circular references of filters, like 
a->b->c->a, and while testing for that needs to grab a read lock twice.


   Stefan




Regards,
Daniel



--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread John Ferlan
[...]

 +static void
 +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
 +{
 +virObjectRWUnlock(obj);
 +virObjectRWLockWrite(obj);
 +}
>>>
>>> How can this not deadlock? This will work only if @obj is locked exactly
>>> once. But since we allow recursive locks any lock() that happens in the
>>> 2nd level must deadlock with this. On the other hand, there's no such
>>> case in the code currently. But that doesn't stop anybody from calling
>>> PromoteWrite() without understanding its limitations.
>>>
>>> Maybe the fact that we need recursive lock for NWFilterObj means it's
>>> not a good candidate for virObjectRWLocable? It is still a good
>>> candidate for virObject though.
>>>
>>> Or if you want to spend extra time on this, maybe introducing
>>> virObjectRecursiveLockable may be worth it (terrible name, I know).
>>>
>>>
>>
>> I dunno, worked for me. The helper is being called from a thread that
>> has taken out a READ lock at most one time and needs to get the WRITE
>> lock in order to change things. If something else has the READ lock that
>> thread waits until the other thread releases the READ lock as far as I
>> understand things.
> 
> Yes, I can see that. It's just that since the original lock is recursive
> I expected things to get recursive and thus I've been thinking how would
> this work there, nested in 2nd, 3rd, .. level. But as noted earlier, the
> places where lock promoting is used are kind of safe since all the
> locking is done at the first level.
> 

The reason I chose rwlocks and taking a read lock by default in the
FindByUUID and FindByName (as opposed to other vir*FindBy* API's which
return a write locked object) is because I know there's the potential
for other code to be doing Reads and rwlocks allowed for that without
the need to create any sort of recursive lockable object or any sort of
API to perform "trylock"'s (all things I tried until the epiphany to use
rwlocks was reached when you added them for other drivers and their list
protection).

In the long/short run I figured that having a common way to get the lock
and then deciding to change it from Read to Write as necessary would
work just fine for the purpose that was needed.

>>
>> Back when I first did this I had quite a lot of debug code and I have a
>> vague recollection of seeing output from this particular path and seeing
>> a couple of unlocks before the WRITE was taken while running the avocado
>> tests.
>>
>> I have zero interest in spending more time on this. That ship sailed. If
>> the decision is to drop the patches, then fine. I tried.  I disagree
>> that NWFilterObj is not a candidate for RWLockable - in fact it's quite
>> the opposite *because* of the recursive locks.
> 
> Well, there's a difference between recursive locks and rwlocks. And it's
> exactly in what happens in recursion. With recursive locks we don't need
> any lock promoting and thus it's safe to do read/write after lock().
> With lock promoting (esp. done in unlock()+lockWrite() manner) we get
> TOCTOU bugs.
> 

If we had a real lock manager available this wouldn't be a problem ;-)
[sorry just my OpenVMS roots and bias showing].  Still if you read up a
bit on "dlm" you'll get an idea of what I mean.

There's so many locks in the nwfilter code it's a wonder it all works.

It's truly unfortunate that the wrlock mechanism is undefined for
promoting a read lock to write leaving the consumer with few options.

Since we can limit the "updating" of the fields in @obj and getting a
write lock to very specific code paths, hopefully we can limit damage
and/or deadlock type and/or TOCTOU issues

>>
>> Additionally, because of the recursive locks we cannot use the
>> virObjectLockable and quite frankly I see zero benefit from going down
>> the path of just virObject. As they say, patches welcome.
>>
>> Somewhere along the line, I recall trying to post patches that were
>> essentially virObjectRecursiveLockable and got NACK'd.
> 
> Why is that? IUUC the aim here is to unify all the vir*ObjectList
> implementations so that we can drop code duplication. And so far what
> I've seen only a couple of virObject* functions are needed
> (virObjectRef, virObjectUnref, virObjectLock and virObjectUnlock). So if
> we have virObjectRecursiveLockable class then we can still use those 4
> functions safely and unify the code here. If you're not interested, I
> can cook the patches.
> 

Yes, reduction of duplication of vir*ObjectList is/was a goal. How to
get there is where opinions start to vary - my code was essentially
NACK'd, but getting to a point where someone else could try something
different I figured would at least be helpful. This nwfilter code is the
black sheep of the libvirt family in more ways than one.

BTW: We're still not at a point where common List mgmt code could be
written because the domain list mgmt varies greatly between the various
hypervisor's. I started writing some patches to work through those
issues, but have 

Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread Michal Privoznik
On 02/08/2018 02:32 PM, John Ferlan wrote:
> 
> 
> On 02/08/2018 08:13 AM, Michal Privoznik wrote:
>> On 02/06/2018 08:20 PM, John Ferlan wrote:
>>> Unlike it's counterparts, the nwfilter object code needs to be able
>>> to support recursive read locks while processing and checking new
>>> filters against the existing environment. Thus instead of using a
>>> virObjectLockable which uses pure mutexes, use the virObjectRWLockable
>>> and primarily use RWLockRead when obtaining the object lock since
>>> RWLockRead locks can be recursively obtained (up to a limit) as long
>>> as there's a corresponding unlock.
>>>
>>> Since all the object management is within the virnwfilterobj code, we
>>> can limit the number of Write locks on the object to very small areas
>>> of code to ensure we don't run into deadlock's with other threads and
>>> domain code that will check/use the filters (it's a very delicate
>>> balance). This limits the write locks to AssignDef and Remove processing.
>>>
>>> This patch will introduce a new API virNWFilterObjEndAPI to unlock
>>> and deref the object.
>>>
>>> This patch introduces two helpers to promote/demote the read/write lock.
>>>
>>> Signed-off-by: John Ferlan 
>>> ---
>>>  src/conf/virnwfilterobj.c  | 193 
>>> +++--
>>>  src/conf/virnwfilterobj.h  |   9 +-
>>>  src/libvirt_private.syms   |   3 +-
>>>  src/nwfilter/nwfilter_driver.c |  15 +--
>>>  src/nwfilter/nwfilter_gentech_driver.c |  11 +-
>>>  5 files changed, 153 insertions(+), 78 deletions(-)
>>>
>>> diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
>>> index 87d7e7270..cd52706ec 100644
>>> --- a/src/conf/virnwfilterobj.c
>>> +++ b/src/conf/virnwfilterobj.c
>>> @@ -34,7 +34,7 @@
>>>  VIR_LOG_INIT("conf.virnwfilterobj");
>>>  
>>>  struct _virNWFilterObj {
>>> -virMutex lock;
>>> +virObjectRWLockable parent;
>>>  
>>>  bool wantRemoved;
>>>  
>>> @@ -47,27 +47,69 @@ struct _virNWFilterObjList {
>>>  virNWFilterObjPtr *objs;
>>>  };
>>>  
>>> +static virClassPtr virNWFilterObjClass;
>>> +static void virNWFilterObjDispose(void *opaque);
>>> +
>>> +
>>> +static int
>>> +virNWFilterObjOnceInit(void)
>>> +{
>>> +if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
>>> +"virNWFilterObj",
>>> +sizeof(virNWFilterObj),
>>> +virNWFilterObjDispose)))
>>> +return -1;
>>> +
>>> +return 0;
>>> +}
>>> +
>>> +VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
>>> +
>>>  
>>>  static virNWFilterObjPtr
>>>  virNWFilterObjNew(void)
>>>  {
>>>  virNWFilterObjPtr obj;
>>>  
>>> -if (VIR_ALLOC(obj) < 0)
>>> +if (virNWFilterObjInitialize() < 0)
>>>  return NULL;
>>>  
>>> -if (virMutexInitRecursive(>lock) < 0) {
>>> -virReportError(VIR_ERR_INTERNAL_ERROR,
>>> -   "%s", _("cannot initialize mutex"));
>>> -VIR_FREE(obj);
>>> +if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
>>>  return NULL;
>>> -}
>>>  
>>> -virNWFilterObjLock(obj);
>>> +virObjectRWLockWrite(obj);
>>>  return obj;
>>>  }
>>>  
>>>  
>>> +static void
>>> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
>>> +{
>>> +virObjectRWUnlock(obj);
>>> +virObjectRWLockWrite(obj);
>>> +}
>>
>> How can this not deadlock? This will work only if @obj is locked exactly
>> once. But since we allow recursive locks any lock() that happens in the
>> 2nd level must deadlock with this. On the other hand, there's no such
>> case in the code currently. But that doesn't stop anybody from calling
>> PromoteWrite() without understanding its limitations.
>>
>> Maybe the fact that we need recursive lock for NWFilterObj means it's
>> not a good candidate for virObjectRWLocable? It is still a good
>> candidate for virObject though.
>>
>> Or if you want to spend extra time on this, maybe introducing
>> virObjectRecursiveLockable may be worth it (terrible name, I know).
>>
>>
> 
> I dunno, worked for me. The helper is being called from a thread that
> has taken out a READ lock at most one time and needs to get the WRITE
> lock in order to change things. If something else has the READ lock that
> thread waits until the other thread releases the READ lock as far as I
> understand things.

Yes, I can see that. It's just that since the original lock is recursive
I expected things to get recursive and thus I've been thinking how would
this work there, nested in 2nd, 3rd, .. level. But as noted earlier, the
places where lock promoting is used are kind of safe since all the
locking is done at the first level.

> 
> Back when I first did this I had quite a lot of debug code and I have a
> vague recollection of seeing output from this particular path and seeing
> a couple of unlocks before the WRITE was taken while running the avocado
> tests.

Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread John Ferlan
[...]


>>>  }
>>>  
>>>  
>>> +static void
>>> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
>>> +{
>>> +virObjectRWUnlock(obj);
>>> +virObjectRWLockWrite(obj);
>>> +}
>>
>> How can this not deadlock? This will work only if @obj is locked exactly
>> once. But since we allow recursive locks any lock() that happens in the
>> 2nd level must deadlock with this. On the other hand, there's no such
>> case in the code currently. But that doesn't stop anybody from calling
>> PromoteWrite() without understanding its limitations.
>>
>> Maybe the fact that we need recursive lock for NWFilterObj means it's
>> not a good candidate for virObjectRWLocable? It is still a good
>> candidate for virObject though.
>>
>> Or if you want to spend extra time on this, maybe introducing
>> virObjectRecursiveLockable may be worth it (terrible name, I know).
> 
> I can't remember exactly call trace scenarios that meant we required
> recursive locking. I vaguely recall is was related to fact that we
> have an alternative entry point into the nwfilter code that's triggered
> by the virt drivers. I'm thus vaguely hoping that when we split nwfilter
> off into a separate daemon from virt driver, the need for recursive
> lockingn would magically disappear. I could be completely wrong though :-)
> 
> 

There's multiple recursive locks. I tried to deal only with the
NWFilterObj locks.

There some "magical" entry points with domain start/stop via
nwfilterInstantiateFilter and nwfilterTeardownFilter There's also
interactions via libvirtd start/stop and of course whatever magical
entry points for firewalld. Lot's of chances for issues when the
virNWFilter{ReadLock|Unlock}FilterUpdates calls are made. Finally
there's an @updateMutex in nwfilter_gentech_driver.c which truly brings
great joy and happiness to debugging lock issues.

I have this piece of paper which I tried to keep track of various locks
and paths - suffice to say it got messy very quickly.

John

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread John Ferlan


On 02/08/2018 08:13 AM, Michal Privoznik wrote:
> On 02/06/2018 08:20 PM, John Ferlan wrote:
>> Unlike it's counterparts, the nwfilter object code needs to be able
>> to support recursive read locks while processing and checking new
>> filters against the existing environment. Thus instead of using a
>> virObjectLockable which uses pure mutexes, use the virObjectRWLockable
>> and primarily use RWLockRead when obtaining the object lock since
>> RWLockRead locks can be recursively obtained (up to a limit) as long
>> as there's a corresponding unlock.
>>
>> Since all the object management is within the virnwfilterobj code, we
>> can limit the number of Write locks on the object to very small areas
>> of code to ensure we don't run into deadlock's with other threads and
>> domain code that will check/use the filters (it's a very delicate
>> balance). This limits the write locks to AssignDef and Remove processing.
>>
>> This patch will introduce a new API virNWFilterObjEndAPI to unlock
>> and deref the object.
>>
>> This patch introduces two helpers to promote/demote the read/write lock.
>>
>> Signed-off-by: John Ferlan 
>> ---
>>  src/conf/virnwfilterobj.c  | 193 
>> +++--
>>  src/conf/virnwfilterobj.h  |   9 +-
>>  src/libvirt_private.syms   |   3 +-
>>  src/nwfilter/nwfilter_driver.c |  15 +--
>>  src/nwfilter/nwfilter_gentech_driver.c |  11 +-
>>  5 files changed, 153 insertions(+), 78 deletions(-)
>>
>> diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
>> index 87d7e7270..cd52706ec 100644
>> --- a/src/conf/virnwfilterobj.c
>> +++ b/src/conf/virnwfilterobj.c
>> @@ -34,7 +34,7 @@
>>  VIR_LOG_INIT("conf.virnwfilterobj");
>>  
>>  struct _virNWFilterObj {
>> -virMutex lock;
>> +virObjectRWLockable parent;
>>  
>>  bool wantRemoved;
>>  
>> @@ -47,27 +47,69 @@ struct _virNWFilterObjList {
>>  virNWFilterObjPtr *objs;
>>  };
>>  
>> +static virClassPtr virNWFilterObjClass;
>> +static void virNWFilterObjDispose(void *opaque);
>> +
>> +
>> +static int
>> +virNWFilterObjOnceInit(void)
>> +{
>> +if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
>> +"virNWFilterObj",
>> +sizeof(virNWFilterObj),
>> +virNWFilterObjDispose)))
>> +return -1;
>> +
>> +return 0;
>> +}
>> +
>> +VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
>> +
>>  
>>  static virNWFilterObjPtr
>>  virNWFilterObjNew(void)
>>  {
>>  virNWFilterObjPtr obj;
>>  
>> -if (VIR_ALLOC(obj) < 0)
>> +if (virNWFilterObjInitialize() < 0)
>>  return NULL;
>>  
>> -if (virMutexInitRecursive(>lock) < 0) {
>> -virReportError(VIR_ERR_INTERNAL_ERROR,
>> -   "%s", _("cannot initialize mutex"));
>> -VIR_FREE(obj);
>> +if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
>>  return NULL;
>> -}
>>  
>> -virNWFilterObjLock(obj);
>> +virObjectRWLockWrite(obj);
>>  return obj;
>>  }
>>  
>>  
>> +static void
>> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
>> +{
>> +virObjectRWUnlock(obj);
>> +virObjectRWLockWrite(obj);
>> +}
> 
> How can this not deadlock? This will work only if @obj is locked exactly
> once. But since we allow recursive locks any lock() that happens in the
> 2nd level must deadlock with this. On the other hand, there's no such
> case in the code currently. But that doesn't stop anybody from calling
> PromoteWrite() without understanding its limitations.
> 
> Maybe the fact that we need recursive lock for NWFilterObj means it's
> not a good candidate for virObjectRWLocable? It is still a good
> candidate for virObject though.
> 
> Or if you want to spend extra time on this, maybe introducing
> virObjectRecursiveLockable may be worth it (terrible name, I know).
> 
> 

I dunno, worked for me. The helper is being called from a thread that
has taken out a READ lock at most one time and needs to get the WRITE
lock in order to change things. If something else has the READ lock that
thread waits until the other thread releases the READ lock as far as I
understand things.

Back when I first did this I had quite a lot of debug code and I have a
vague recollection of seeing output from this particular path and seeing
a couple of unlocks before the WRITE was taken while running the avocado
tests.

I have zero interest in spending more time on this. That ship sailed. If
the decision is to drop the patches, then fine. I tried.  I disagree
that NWFilterObj is not a candidate for RWLockable - in fact it's quite
the opposite *because* of the recursive locks.

Additionally, because of the recursive locks we cannot use the
virObjectLockable and quite frankly I see zero benefit from going down
the path of just virObject. As they say, patches welcome.

Somewhere along the line, I recall trying 

Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread Daniel P . Berrangé
On Thu, Feb 08, 2018 at 02:13:58PM +0100, Michal Privoznik wrote:
> On 02/06/2018 08:20 PM, John Ferlan wrote:
> > Unlike it's counterparts, the nwfilter object code needs to be able
> > to support recursive read locks while processing and checking new
> > filters against the existing environment. Thus instead of using a
> > virObjectLockable which uses pure mutexes, use the virObjectRWLockable
> > and primarily use RWLockRead when obtaining the object lock since
> > RWLockRead locks can be recursively obtained (up to a limit) as long
> > as there's a corresponding unlock.
> > 
> > Since all the object management is within the virnwfilterobj code, we
> > can limit the number of Write locks on the object to very small areas
> > of code to ensure we don't run into deadlock's with other threads and
> > domain code that will check/use the filters (it's a very delicate
> > balance). This limits the write locks to AssignDef and Remove processing.
> > 
> > This patch will introduce a new API virNWFilterObjEndAPI to unlock
> > and deref the object.
> > 
> > This patch introduces two helpers to promote/demote the read/write lock.
> > 
> > Signed-off-by: John Ferlan 
> > ---
> >  src/conf/virnwfilterobj.c  | 193 
> > +++--
> >  src/conf/virnwfilterobj.h  |   9 +-
> >  src/libvirt_private.syms   |   3 +-
> >  src/nwfilter/nwfilter_driver.c |  15 +--
> >  src/nwfilter/nwfilter_gentech_driver.c |  11 +-
> >  5 files changed, 153 insertions(+), 78 deletions(-)
> > 
> > diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
> > index 87d7e7270..cd52706ec 100644
> > --- a/src/conf/virnwfilterobj.c
> > +++ b/src/conf/virnwfilterobj.c
> > @@ -34,7 +34,7 @@
> >  VIR_LOG_INIT("conf.virnwfilterobj");
> >  
> >  struct _virNWFilterObj {
> > -virMutex lock;
> > +virObjectRWLockable parent;
> >  
> >  bool wantRemoved;
> >  
> > @@ -47,27 +47,69 @@ struct _virNWFilterObjList {
> >  virNWFilterObjPtr *objs;
> >  };
> >  
> > +static virClassPtr virNWFilterObjClass;
> > +static void virNWFilterObjDispose(void *opaque);
> > +
> > +
> > +static int
> > +virNWFilterObjOnceInit(void)
> > +{
> > +if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
> > +"virNWFilterObj",
> > +sizeof(virNWFilterObj),
> > +virNWFilterObjDispose)))
> > +return -1;
> > +
> > +return 0;
> > +}
> > +
> > +VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
> > +
> >  
> >  static virNWFilterObjPtr
> >  virNWFilterObjNew(void)
> >  {
> >  virNWFilterObjPtr obj;
> >  
> > -if (VIR_ALLOC(obj) < 0)
> > +if (virNWFilterObjInitialize() < 0)
> >  return NULL;
> >  
> > -if (virMutexInitRecursive(>lock) < 0) {
> > -virReportError(VIR_ERR_INTERNAL_ERROR,
> > -   "%s", _("cannot initialize mutex"));
> > -VIR_FREE(obj);
> > +if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
> >  return NULL;
> > -}
> >  
> > -virNWFilterObjLock(obj);
> > +virObjectRWLockWrite(obj);
> >  return obj;
> >  }
> >  
> >  
> > +static void
> > +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
> > +{
> > +virObjectRWUnlock(obj);
> > +virObjectRWLockWrite(obj);
> > +}
> 
> How can this not deadlock? This will work only if @obj is locked exactly
> once. But since we allow recursive locks any lock() that happens in the
> 2nd level must deadlock with this. On the other hand, there's no such
> case in the code currently. But that doesn't stop anybody from calling
> PromoteWrite() without understanding its limitations.
> 
> Maybe the fact that we need recursive lock for NWFilterObj means it's
> not a good candidate for virObjectRWLocable? It is still a good
> candidate for virObject though.
> 
> Or if you want to spend extra time on this, maybe introducing
> virObjectRecursiveLockable may be worth it (terrible name, I know).

I can't remember exactly call trace scenarios that meant we required
recursive locking. I vaguely recall is was related to fact that we
have an alternative entry point into the nwfilter code that's triggered
by the virt drivers. I'm thus vaguely hoping that when we split nwfilter
off into a separate daemon from virt driver, the need for recursive
lockingn would magically disappear. I could be completely wrong though :-)


Regards,
Daniel
-- 
|: https://berrange.com  -o-https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o-https://fstop138.berrange.com :|
|: https://entangle-photo.org-o-https://www.instagram.com/dberrange :|

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-08 Thread Michal Privoznik
On 02/06/2018 08:20 PM, John Ferlan wrote:
> Unlike it's counterparts, the nwfilter object code needs to be able
> to support recursive read locks while processing and checking new
> filters against the existing environment. Thus instead of using a
> virObjectLockable which uses pure mutexes, use the virObjectRWLockable
> and primarily use RWLockRead when obtaining the object lock since
> RWLockRead locks can be recursively obtained (up to a limit) as long
> as there's a corresponding unlock.
> 
> Since all the object management is within the virnwfilterobj code, we
> can limit the number of Write locks on the object to very small areas
> of code to ensure we don't run into deadlock's with other threads and
> domain code that will check/use the filters (it's a very delicate
> balance). This limits the write locks to AssignDef and Remove processing.
> 
> This patch will introduce a new API virNWFilterObjEndAPI to unlock
> and deref the object.
> 
> This patch introduces two helpers to promote/demote the read/write lock.
> 
> Signed-off-by: John Ferlan 
> ---
>  src/conf/virnwfilterobj.c  | 193 
> +++--
>  src/conf/virnwfilterobj.h  |   9 +-
>  src/libvirt_private.syms   |   3 +-
>  src/nwfilter/nwfilter_driver.c |  15 +--
>  src/nwfilter/nwfilter_gentech_driver.c |  11 +-
>  5 files changed, 153 insertions(+), 78 deletions(-)
> 
> diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
> index 87d7e7270..cd52706ec 100644
> --- a/src/conf/virnwfilterobj.c
> +++ b/src/conf/virnwfilterobj.c
> @@ -34,7 +34,7 @@
>  VIR_LOG_INIT("conf.virnwfilterobj");
>  
>  struct _virNWFilterObj {
> -virMutex lock;
> +virObjectRWLockable parent;
>  
>  bool wantRemoved;
>  
> @@ -47,27 +47,69 @@ struct _virNWFilterObjList {
>  virNWFilterObjPtr *objs;
>  };
>  
> +static virClassPtr virNWFilterObjClass;
> +static void virNWFilterObjDispose(void *opaque);
> +
> +
> +static int
> +virNWFilterObjOnceInit(void)
> +{
> +if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
> +"virNWFilterObj",
> +sizeof(virNWFilterObj),
> +virNWFilterObjDispose)))
> +return -1;
> +
> +return 0;
> +}
> +
> +VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
> +
>  
>  static virNWFilterObjPtr
>  virNWFilterObjNew(void)
>  {
>  virNWFilterObjPtr obj;
>  
> -if (VIR_ALLOC(obj) < 0)
> +if (virNWFilterObjInitialize() < 0)
>  return NULL;
>  
> -if (virMutexInitRecursive(>lock) < 0) {
> -virReportError(VIR_ERR_INTERNAL_ERROR,
> -   "%s", _("cannot initialize mutex"));
> -VIR_FREE(obj);
> +if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
>  return NULL;
> -}
>  
> -virNWFilterObjLock(obj);
> +virObjectRWLockWrite(obj);
>  return obj;
>  }
>  
>  
> +static void
> +virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
> +{
> +virObjectRWUnlock(obj);
> +virObjectRWLockWrite(obj);
> +}

How can this not deadlock? This will work only if @obj is locked exactly
once. But since we allow recursive locks any lock() that happens in the
2nd level must deadlock with this. On the other hand, there's no such
case in the code currently. But that doesn't stop anybody from calling
PromoteWrite() without understanding its limitations.

Maybe the fact that we need recursive lock for NWFilterObj means it's
not a good candidate for virObjectRWLocable? It is still a good
candidate for virObject though.

Or if you want to spend extra time on this, maybe introducing
virObjectRecursiveLockable may be worth it (terrible name, I know).


> @@ -347,26 +426,39 @@ virNWFilterObjListAssignDef(virNWFilterObjListPtr 
> nwfilters,
>  }
>  
>  
> +/* Get a READ lock and immediately promote to WRITE while we adjust
> + * data within. */
>  if ((obj = virNWFilterObjListFindByName(nwfilters, def->name))) {
>  
>  objdef = obj->def;
>  if (virNWFilterDefEqual(def, objdef)) {
> +virNWFilterObjPromoteToWrite(obj);
>  virNWFilterDefFree(objdef);
>  obj->def = def;
> +virNWFilterObjDemoteFromWrite(obj);
>  return obj;
>  }

What is the idea behind this if()? I don't understand it. There doesn't
seem to be any fields in @objdef or

>  
> +/* Set newDef and run the trigger with a demoted lock since it may 
> need
> + * to grab a read lock on this object and promote before returning. 
> */
> +virNWFilterObjPromoteToWrite(obj);
>  obj->newDef = def;
> +virNWFilterObjDemoteFromWrite(obj);
> +
>  /* trigger the update on VMs referencing the filter */
>  if (virNWFilterTriggerVMFilterRebuild() < 0) {
> +virNWFilterObjPromoteToWrite(obj);
>  obj->newDef = NULL;

[libvirt] [PATCH v5 1/3] nwfilter: Convert _virNWFilterObj to use virObjectRWLockable

2018-02-06 Thread John Ferlan
Unlike it's counterparts, the nwfilter object code needs to be able
to support recursive read locks while processing and checking new
filters against the existing environment. Thus instead of using a
virObjectLockable which uses pure mutexes, use the virObjectRWLockable
and primarily use RWLockRead when obtaining the object lock since
RWLockRead locks can be recursively obtained (up to a limit) as long
as there's a corresponding unlock.

Since all the object management is within the virnwfilterobj code, we
can limit the number of Write locks on the object to very small areas
of code to ensure we don't run into deadlock's with other threads and
domain code that will check/use the filters (it's a very delicate
balance). This limits the write locks to AssignDef and Remove processing.

This patch will introduce a new API virNWFilterObjEndAPI to unlock
and deref the object.

This patch introduces two helpers to promote/demote the read/write lock.

Signed-off-by: John Ferlan 
---
 src/conf/virnwfilterobj.c  | 193 +++--
 src/conf/virnwfilterobj.h  |   9 +-
 src/libvirt_private.syms   |   3 +-
 src/nwfilter/nwfilter_driver.c |  15 +--
 src/nwfilter/nwfilter_gentech_driver.c |  11 +-
 5 files changed, 153 insertions(+), 78 deletions(-)

diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c
index 87d7e7270..cd52706ec 100644
--- a/src/conf/virnwfilterobj.c
+++ b/src/conf/virnwfilterobj.c
@@ -34,7 +34,7 @@
 VIR_LOG_INIT("conf.virnwfilterobj");
 
 struct _virNWFilterObj {
-virMutex lock;
+virObjectRWLockable parent;
 
 bool wantRemoved;
 
@@ -47,27 +47,69 @@ struct _virNWFilterObjList {
 virNWFilterObjPtr *objs;
 };
 
+static virClassPtr virNWFilterObjClass;
+static void virNWFilterObjDispose(void *opaque);
+
+
+static int
+virNWFilterObjOnceInit(void)
+{
+if (!(virNWFilterObjClass = virClassNew(virClassForObjectRWLockable(),
+"virNWFilterObj",
+sizeof(virNWFilterObj),
+virNWFilterObjDispose)))
+return -1;
+
+return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virNWFilterObj)
+
 
 static virNWFilterObjPtr
 virNWFilterObjNew(void)
 {
 virNWFilterObjPtr obj;
 
-if (VIR_ALLOC(obj) < 0)
+if (virNWFilterObjInitialize() < 0)
 return NULL;
 
-if (virMutexInitRecursive(>lock) < 0) {
-virReportError(VIR_ERR_INTERNAL_ERROR,
-   "%s", _("cannot initialize mutex"));
-VIR_FREE(obj);
+if (!(obj = virObjectRWLockableNew(virNWFilterObjClass)))
 return NULL;
-}
 
-virNWFilterObjLock(obj);
+virObjectRWLockWrite(obj);
 return obj;
 }
 
 
+static void
+virNWFilterObjPromoteToWrite(virNWFilterObjPtr obj)
+{
+virObjectRWUnlock(obj);
+virObjectRWLockWrite(obj);
+}
+
+
+static void
+virNWFilterObjDemoteFromWrite(virNWFilterObjPtr obj)
+{
+virObjectRWUnlock(obj);
+virObjectRWLockRead(obj);
+}
+
+
+void
+virNWFilterObjEndAPI(virNWFilterObjPtr *obj)
+{
+if (!*obj)
+return;
+
+virObjectRWUnlock(*obj);
+virObjectUnref(*obj);
+*obj = NULL;
+}
+
+
 virNWFilterDefPtr
 virNWFilterObjGetDef(virNWFilterObjPtr obj)
 {
@@ -90,17 +132,15 @@ virNWFilterObjWantRemoved(virNWFilterObjPtr obj)
 
 
 static void
-virNWFilterObjFree(virNWFilterObjPtr obj)
+virNWFilterObjDispose(void *opaque)
 {
+virNWFilterObjPtr obj = opaque;
+
 if (!obj)
 return;
 
 virNWFilterDefFree(obj->def);
 virNWFilterDefFree(obj->newDef);
-
-virMutexDestroy(>lock);
-
-VIR_FREE(obj);
 }
 
 
@@ -109,7 +149,7 @@ virNWFilterObjListFree(virNWFilterObjListPtr nwfilters)
 {
 size_t i;
 for (i = 0; i < nwfilters->count; i++)
-virNWFilterObjFree(nwfilters->objs[i]);
+virObjectUnref(nwfilters->objs[i]);
 VIR_FREE(nwfilters->objs);
 VIR_FREE(nwfilters);
 }
@@ -132,22 +172,32 @@ virNWFilterObjListRemove(virNWFilterObjListPtr nwfilters,
 {
 size_t i;
 
-virNWFilterObjUnlock(obj);
+virObjectRWUnlock(obj);
 
 for (i = 0; i < nwfilters->count; i++) {
-virNWFilterObjLock(nwfilters->objs[i]);
+virObjectRWLockWrite(nwfilters->objs[i]);
 if (nwfilters->objs[i] == obj) {
-virNWFilterObjUnlock(nwfilters->objs[i]);
-virNWFilterObjFree(nwfilters->objs[i]);
+virObjectRWUnlock(nwfilters->objs[i]);
+virObjectUnref(nwfilters->objs[i]);
 
 VIR_DELETE_ELEMENT(nwfilters->objs, i, nwfilters->count);
 break;
 }
-virNWFilterObjUnlock(nwfilters->objs[i]);
+virObjectRWUnlock(nwfilters->objs[i]);
 }
 }
 
 
+/**
+ * virNWFilterObjListFindByUUID
+ * @nwfilters: Pointer to filter list
+ * @uuid: UUID to use to lookup the object
+ *
+ * Search for the object by uuidstr in the hash table and return a read
+ * locked copy of the object.
+ *
+ * Returns: A