Author: kib
Date: Sun Nov  2 13:10:31 2014
New Revision: 273966
URL: https://svnweb.freebsd.org/changeset/base/273966

Log:
  Fix two issues with lockmgr(9) LK_CAN_SHARE() test, which determines
  whether the shared request for already shared-locked lock could be
  granted.  Both problems result in the exclusive locker starvation.
  
  The concurrent exclusive request is indicated by either
  LK_EXCLUSIVE_WAITERS or LK_EXCLUSIVE_SPINNERS flags.  The reverse
  condition, i.e. no exclusive waiters, must check that both flags are
  cleared.
  
  Add a flag LK_NODDLKTREAT for shared lock request to indicate that
  current thread guarantees that it does not own the lock in shared
  mode.  This turns back the exclusive lock starvation avoidance code;
  see man page update for detailed description.
  
  Use LK_NODDLKTREAT when doing lookup(9).
  
  Reported and tested by:       pho
  No objections from:   attilio
  Sponsored by: The FreeBSD Foundation
  MFC after:    2 weeks

Modified:
  head/share/man/man9/lock.9
  head/sys/kern/kern_lock.c
  head/sys/kern/vfs_lookup.c
  head/sys/sys/lockmgr.h

Modified: head/share/man/man9/lock.9
==============================================================================
--- head/share/man/man9/lock.9  Sun Nov  2 11:47:40 2014        (r273965)
+++ head/share/man/man9/lock.9  Sun Nov  2 13:10:31 2014        (r273966)
@@ -26,7 +26,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd October 6, 2013
+.Dd November 2, 2014
 .Dt LOCK 9
 .Os
 .Sh NAME
@@ -145,7 +145,7 @@ Their arguments are:
 A pointer to the lock to manipulate.
 .It Fa flags
 Flags indicating what action is to be taken.
-.Bl -tag -width ".Dv LK_CANRECURSE"
+.Bl -tag -width ".Dv LK_NODDLKTREAT"
 .It Dv LK_SHARED
 Acquire a shared lock.
 If an exclusive lock is currently held,
@@ -199,6 +199,29 @@ Allow recursion on an exclusive lock.
 For every lock there must be a release.
 .It Dv LK_INTERLOCK
 Unlock the interlock (which should be locked already).
+.It Dv LK_NODDLKTREAT
+Normally,
+.Fn lockmgr
+postpones serving further shared requests for shared-locked lock if there is
+exclusive waiter, to avoid exclusive lock starvation.
+But, if the thread requesting the shared lock already owns a shared lockmgr
+lock, the request is granted even in presence of the parallel exclusive lock
+request, which is done to avoid deadlocks with recursive shared acquisition.
+.Pp
+The
+.Dv LK_NODDLKTREAT
+flag can only be used by code which requests shared non-recursive lock.
+The flag allows exclusive requests to preempt the current shared request
+even if the current thread owns shared locks.
+This is safe since shared lock is guaranteed to not recurse, and is used
+when thread is known to held unrelated shared locks, to not cause
+unneccessary starvation.  An example is
+.Dv vp
+locking in VFS
+.Xr lookup 9 ,
+when
+.Dv dvp
+is already locked.
 .El
 .It Fa ilk
 An interlock mutex for controlling group access to the lock.

Modified: head/sys/kern/kern_lock.c
==============================================================================
--- head/sys/kern/kern_lock.c   Sun Nov  2 11:47:40 2014        (r273965)
+++ head/sys/kern/kern_lock.c   Sun Nov  2 13:10:31 2014        (r273966)
@@ -115,10 +115,11 @@ CTASSERT(LK_UNLOCKED == (LK_UNLOCKED &
        }                                                               \
 } while (0)
 
-#define        LK_CAN_SHARE(x)                                                 
\
-       (((x) & LK_SHARE) && (((x) & LK_EXCLUSIVE_WAITERS) == 0 ||      \
-       ((x) & LK_EXCLUSIVE_SPINNERS) == 0 ||                           \
-       curthread->td_lk_slocks || (curthread->td_pflags & TDP_DEADLKTREAT)))
+#define        LK_CAN_SHARE(x, flags)                                          
\
+       (((x) & LK_SHARE) &&                                            \
+       (((x) & (LK_EXCLUSIVE_WAITERS | LK_EXCLUSIVE_SPINNERS)) == 0 || \
+       (curthread->td_lk_slocks != 0 && !(flags & LK_NODDLKTREAT)) ||  \
+       (curthread->td_pflags & TDP_DEADLKTREAT)))
 #define        LK_TRYOP(x)                                                     
\
        ((x) & LK_NOWAIT)
 
@@ -530,7 +531,7 @@ __lockmgr_args(struct lock *lk, u_int fl
                         * waiters, if we fail to acquire the shared lock
                         * loop back and retry.
                         */
-                       if (LK_CAN_SHARE(x)) {
+                       if (LK_CAN_SHARE(x, flags)) {
                                if (atomic_cmpset_acq_ptr(&lk->lk_lock, x,
                                    x + LK_ONE_SHARER))
                                        break;
@@ -635,7 +636,7 @@ __lockmgr_args(struct lock *lk, u_int fl
                         * if the lock can be acquired in shared mode, try
                         * again.
                         */
-                       if (LK_CAN_SHARE(x)) {
+                       if (LK_CAN_SHARE(x, flags)) {
                                sleepq_release(&lk->lock_object);
                                continue;
                        }

Modified: head/sys/kern/vfs_lookup.c
==============================================================================
--- head/sys/kern/vfs_lookup.c  Sun Nov  2 11:47:40 2014        (r273965)
+++ head/sys/kern/vfs_lookup.c  Sun Nov  2 13:10:31 2014        (r273966)
@@ -390,6 +390,7 @@ compute_cn_lkflags(struct mount *mp, int
                lkflags &= ~LK_SHARED;
                lkflags |= LK_EXCLUSIVE;
        }
+       lkflags |= LK_NODDLKTREAT;
        return (lkflags);
 }
 

Modified: head/sys/sys/lockmgr.h
==============================================================================
--- head/sys/sys/lockmgr.h      Sun Nov  2 11:47:40 2014        (r273965)
+++ head/sys/sys/lockmgr.h      Sun Nov  2 13:10:31 2014        (r273966)
@@ -158,6 +158,7 @@ _lockmgr_args_rw(struct lock *lk, u_int 
 #define        LK_RETRY        0x000400
 #define        LK_SLEEPFAIL    0x000800
 #define        LK_TIMELOCK     0x001000
+#define        LK_NODDLKTREAT  0x002000
 
 /*
  * Operations for lockmgr().
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to