Author: rmacklem
Date: Thu Jun 27 23:10:40 2019
New Revision: 349476
URL: https://svnweb.freebsd.org/changeset/base/349476

Log:
  Add non-blocking trylock variants for the rangelock functions.
  
  A future patch that will add a Linux compatible copy_file_range(2) syscall
  needs to be able to lock the byte ranges of two files concurrently.
  To do this without a risk of deadlock, a non-blocking variant of
  vn_rangelock_rlock() called vn_rangelock_tryrlock() was needed.
  This patch adds this, along with vn_rangelock_trywlock(), in order to
  do this.
  The patch also adds a couple of comments, that I hope clarify how the
  algorithm used in kern_rangelock.c works.
  
  Reviewed by:  kib, asomers (previous version)
  Differential Revision:        https://reviews.freebsd.org/D20645

Modified:
  head/sys/kern/kern_rangelock.c
  head/sys/sys/rangelock.h
  head/sys/sys/vnode.h

Modified: head/sys/kern/kern_rangelock.c
==============================================================================
--- head/sys/kern/kern_rangelock.c      Thu Jun 27 22:50:11 2019        
(r349475)
+++ head/sys/kern/kern_rangelock.c      Thu Jun 27 23:10:40 2019        
(r349476)
@@ -141,15 +141,33 @@ out:
 
 static void
 rangelock_unlock_locked(struct rangelock *lock, struct rl_q_entry *entry,
-    struct mtx *ilk)
+    struct mtx *ilk, bool do_calc_block)
 {
 
        MPASS(lock != NULL && entry != NULL && ilk != NULL);
        mtx_assert(ilk, MA_OWNED);
-       KASSERT(entry != lock->rl_currdep, ("stuck currdep"));
 
+       if (!do_calc_block) {
+               /*
+                * This is the case where rangelock_enqueue() has been called
+                * with trylock == true and just inserted this entry in the
+                * queue.
+                * If rl_currdep is this entry, rl_currdep needs to
+                * be set to the next entry in the rl_waiters list.
+                * However, since this entry is the last entry in the
+                * list, the next entry is NULL.
+                */
+               if (lock->rl_currdep == entry) {
+                       KASSERT(TAILQ_NEXT(lock->rl_currdep, rl_q_link) == NULL,
+                           ("rangelock_enqueue: next entry not NULL"));
+                       lock->rl_currdep = NULL;
+               }
+       } else
+               KASSERT(entry != lock->rl_currdep, ("stuck currdep"));
+
        TAILQ_REMOVE(&lock->rl_waiters, entry, rl_q_link);
-       rangelock_calc_block(lock);
+       if (do_calc_block)
+               rangelock_calc_block(lock);
        mtx_unlock(ilk);
        if (curthread->td_rlqe == NULL)
                curthread->td_rlqe = entry;
@@ -164,7 +182,7 @@ rangelock_unlock(struct rangelock *lock, void *cookie,
        MPASS(lock != NULL && cookie != NULL && ilk != NULL);
 
        mtx_lock(ilk);
-       rangelock_unlock_locked(lock, cookie, ilk);
+       rangelock_unlock_locked(lock, cookie, ilk, true);
 }
 
 /*
@@ -185,7 +203,7 @@ rangelock_unlock_range(struct rangelock *lock, void *c
 
        mtx_lock(ilk);
        if (entry->rl_q_end == end) {
-               rangelock_unlock_locked(lock, cookie, ilk);
+               rangelock_unlock_locked(lock, cookie, ilk, true);
                return (NULL);
        }
        entry->rl_q_end = end;
@@ -196,11 +214,11 @@ rangelock_unlock_range(struct rangelock *lock, void *c
 
 /*
  * Add the lock request to the queue of the pending requests for
- * rangelock.  Sleep until the request can be granted.
+ * rangelock.  Sleep until the request can be granted unless trylock == true.
  */
 static void *
 rangelock_enqueue(struct rangelock *lock, off_t start, off_t end, int mode,
-    struct mtx *ilk)
+    struct mtx *ilk, bool trylock)
 {
        struct rl_q_entry *entry;
        struct thread *td;
@@ -226,11 +244,28 @@ rangelock_enqueue(struct rangelock *lock, off_t start,
         */
 
        TAILQ_INSERT_TAIL(&lock->rl_waiters, entry, rl_q_link);
+       /*
+        * If rl_currdep == NULL, there is no entry waiting for a conflicting
+        * range to be resolved, so set rl_currdep to this entry.  If there is
+        * no conflicting entry for this entry, rl_currdep will be set back to
+        * NULL by rangelock_calc_block().
+        */
        if (lock->rl_currdep == NULL)
                lock->rl_currdep = entry;
        rangelock_calc_block(lock);
-       while (!(entry->rl_q_flags & RL_LOCK_GRANTED))
+       while (!(entry->rl_q_flags & RL_LOCK_GRANTED)) {
+               if (trylock) {
+                       /*
+                        * For this case, the range is not actually locked
+                        * yet, but removal from the list requires the same
+                        * steps, except for not doing a rangelock_calc_block()
+                        * call, since rangelock_calc_block() was called above.
+                        */
+                       rangelock_unlock_locked(lock, entry, ilk, false);
+                       return (NULL);
+               }
                msleep(entry, ilk, 0, "range", 0);
+       }
        mtx_unlock(ilk);
        return (entry);
 }
@@ -239,12 +274,28 @@ void *
 rangelock_rlock(struct rangelock *lock, off_t start, off_t end, struct mtx 
*ilk)
 {
 
-       return (rangelock_enqueue(lock, start, end, RL_LOCK_READ, ilk));
+       return (rangelock_enqueue(lock, start, end, RL_LOCK_READ, ilk, false));
 }
 
 void *
+rangelock_tryrlock(struct rangelock *lock, off_t start, off_t end,
+    struct mtx *ilk)
+{
+
+       return (rangelock_enqueue(lock, start, end, RL_LOCK_READ, ilk, true));
+}
+
+void *
 rangelock_wlock(struct rangelock *lock, off_t start, off_t end, struct mtx 
*ilk)
 {
 
-       return (rangelock_enqueue(lock, start, end, RL_LOCK_WRITE, ilk));
+       return (rangelock_enqueue(lock, start, end, RL_LOCK_WRITE, ilk, false));
+}
+
+void *
+rangelock_trywlock(struct rangelock *lock, off_t start, off_t end,
+    struct mtx *ilk)
+{
+
+       return (rangelock_enqueue(lock, start, end, RL_LOCK_WRITE, ilk, true));
 }

Modified: head/sys/sys/rangelock.h
==============================================================================
--- head/sys/sys/rangelock.h    Thu Jun 27 22:50:11 2019        (r349475)
+++ head/sys/sys/rangelock.h    Thu Jun 27 23:10:40 2019        (r349476)
@@ -75,7 +75,11 @@ void *rangelock_unlock_range(struct rangelock *lock, v
            off_t start, off_t end, struct mtx *ilk);
 void   *rangelock_rlock(struct rangelock *lock, off_t start, off_t end,
            struct mtx *ilk);
+void   *rangelock_tryrlock(struct rangelock *lock, off_t start, off_t end,
+           struct mtx *ilk);
 void   *rangelock_wlock(struct rangelock *lock, off_t start, off_t end,
+           struct mtx *ilk);
+void   *rangelock_trywlock(struct rangelock *lock, off_t start, off_t end,
            struct mtx *ilk);
 void    rlqentry_free(struct rl_q_entry *rlqe);
 

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h        Thu Jun 27 22:50:11 2019        (r349475)
+++ head/sys/sys/vnode.h        Thu Jun 27 23:10:40 2019        (r349476)
@@ -720,8 +720,12 @@ int        vn_io_fault_pgmove(vm_page_t ma[], vm_offset_t 
off
            VI_MTX(vp))
 #define        vn_rangelock_rlock(vp, start, end)                              
\
        rangelock_rlock(&(vp)->v_rl, (start), (end), VI_MTX(vp))
+#define        vn_rangelock_tryrlock(vp, start, end)                           
\
+       rangelock_tryrlock(&(vp)->v_rl, (start), (end), VI_MTX(vp))
 #define        vn_rangelock_wlock(vp, start, end)                              
\
        rangelock_wlock(&(vp)->v_rl, (start), (end), VI_MTX(vp))
+#define        vn_rangelock_trywlock(vp, start, end)                           
\
+       rangelock_trywlock(&(vp)->v_rl, (start), (end), VI_MTX(vp))
 
 int    vfs_cache_lookup(struct vop_lookup_args *ap);
 void   vfs_timestamp(struct timespec *);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to