svn commit: r349476 - in head/sys: kern sys

Rick Macklem rmacklem at FreeBSD.org
Thu Jun 27 23:10:42 UTC 2019


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 *);


More information about the svn-src-head mailing list