tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: reference counts



On Wed, Apr 15, 2015 at 12:34 PM, Taylor R Campbell
<campbell+netbsd-tech-kern%mumble.net@localhost> wrote:
>    Date: Wed, 15 Apr 2015 11:27:16 +0900
>    From: Ryota Ozaki <ozaki-r%netbsd.org@localhost>
>
>    I'm trying refcount(9) with bridge. It works well for now.
>    (I got kernel freeze once but I'm not sure it's due to refcount(9)
>    because my testing VM sometimes freezes around uvm :-/)
>
>    The diff is here: http://www.netbsd.org/~ozaki-r/bridge-refcount.diff
>
> Looks nice!  I spotted one race which was there to begin with: nothing
> prevents multiple concurrent bridge_delete_member on the same member.
> I think bridge_delete_member should require the caller to hold the
> lock, and drop it to wait for users to drain:
>
> bridge_delete_member(sc, bif)
> {
>
>         KASSERT(BRIDGE_LOCKED(sc));
>         ...
>         LIST_REMOVE(bif, bif_next);
>         BRIDGE_PSZ_PERFORM(sc);
>
>         BRIDGE_UNLOCK(sc);
>         mutex_enter(sc->sc_iflist_intr_lock);
>         refcount_dec_drain(&bif->bif_refcount, &sc->sc_iflist_cv,
>             &sc->sc_iflist_intr_lock);
>         mutex_exit(sc->sc_iflist_intr_lock);
>         ...
>         BRIDGE_LOCK(sc);
> }
>
> bridge_ioctl_del(sc, arg)
> {
>
>         BRIDGE_LOCK(sc);
>         LIST_FOREACH(bif, &sc->sc_iflist, bif_next)
>                 ...
>         bridge_delete_member(sc, bif);
>         BRIDGE_UNLOCK(sc);
>         ...
> }
>
> bridge_clone_destroy(sc)
> {
>
>         ...
>         BRIDGE_LOCK(sc);
>         while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL)
>                 bridge_delete_member(sc, bif);
>         BRIDGE_UNLOCK(sc);
>         ...
> }

Hmm, right. It protected a member between bridge_ioctl_del,
but didn't between bridge_ioctl_del and bridge_clone_destroy.
Your fix makes sense and works for me.

>
>    Two comments to refcount(9) from me:
>    - Can we have cv in struct refcount? I think we don't need to share
>      it with outside refcount unlike mutex.
>
> I'd rather not do that because:
>
> (a) It doesn't hurt to share the condvar for most users -- all
> condvars share sleepqs with some other condvars anyway, and everyone
> must be prepared for spurious wakeups.
>
> (b) Not all users need the condvar, so it would waste space for them.
> Consider, e.g., kauth_cred_t, which would use refcount_dec_local
> instead of refcount_dec_{signal,broadcast}.

I see. I was thinking only my cases.

>
>    - It seems that the API assumes that refcount_inc is not called
>      after refcount_dec_drain as KASSERT(0 < old) in refcount_inc
>      implies. Don't we need to guarantee the assumption somehow?
>      Or users have to guarantee that by themselves? (e.g., by using
>      pserialize like bridge.) If so, we should document about that
>      in refcount.9.
>
> Users should remove the last reference in a table, or mark it dying,
> so that nobody else can get to it, before calling refcount_dec_drain.
> I added a note to the man page -- new draft attached.

LGTM

>
> This also enables a little optimization in refcount_dec_drain: if the
> reference count is 1, it can avoid the atomic.  This means, e.g.,
> emptying a large table (say, the vnode cache) need not issue atomic
> operations for every entry, only for those that are currently in use.

+       /*
+        * Caller guarantees if our reference is exclusive, nobody can
+        * acquire new references.  (If there are other references
+        * already, then they may turn into new references.)
+        */
+       if (refcount->rc_value == 1)
+               refcount->rc_value = 0;

                ^ We have to return here?

+
        if (0 < atomic_dec_uint_nv(&refcount->rc_value))
                do cv_wait(cv, interlock); while (0 < refcount->rc_value);

                ^ otherwise, we accidentally come here and wait forever

  ozaki-r


Home | Main Index | Thread Index | Old Index