svn commit: r358025 - head/sys/vm

Mark Johnston markj at FreeBSD.org
Mon Feb 17 15:10:42 UTC 2020


Author: markj
Date: Mon Feb 17 15:10:41 2020
New Revision: 358025
URL: https://svnweb.freebsd.org/changeset/base/358025

Log:
  Fix a swap block allocation race.
  
  putpages' allocation of swap blocks is done under the global sw_dev
  lock.  Previously it would drop that lock before inserting the allocated
  blocks into the object's trie, creating a window in which swap blocks
  are allocated but are not visible to swapoff.  This can cause
  swp_pager_strategy() to fail and panic the system.
  
  Fix the problem bluntly, by allocating swap blocks under the object
  lock.
  
  Reviewed by:	jeff, kib
  Tested by:	pho
  MFC after:	2 weeks
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D23665

Modified:
  head/sys/vm/swap_pager.c

Modified: head/sys/vm/swap_pager.c
==============================================================================
--- head/sys/vm/swap_pager.c	Mon Feb 17 15:09:40 2020	(r358024)
+++ head/sys/vm/swap_pager.c	Mon Feb 17 15:10:41 2020	(r358025)
@@ -1453,18 +1453,6 @@ swap_pager_putpages(vm_object_t object, vm_page_t *ma,
 		/* Maximum I/O size is limited by maximum swap block size. */
 		n = min(count - i, nsw_cluster_max);
 
-		/* Get a block of swap of size up to size n. */
-		blk = swp_pager_getswapspace(&n, 4);
-		if (blk == SWAPBLK_NONE) {
-			for (j = 0; j < n; ++j)
-				rtvals[i + j] = VM_PAGER_FAIL;
-			continue;
-		}
-
-		/*
-		 * All I/O parameters have been satisfied.  Build the I/O
-		 * request and assign the swap space.
-		 */
 		if (async) {
 			mtx_lock(&swbuf_mtx);
 			while (nsw_wcount_async == 0)
@@ -1473,19 +1461,20 @@ swap_pager_putpages(vm_object_t object, vm_page_t *ma,
 			nsw_wcount_async--;
 			mtx_unlock(&swbuf_mtx);
 		}
-		bp = uma_zalloc(swwbuf_zone, M_WAITOK);
-		if (async)
-			bp->b_flags = B_ASYNC;
-		bp->b_flags |= B_PAGING;
-		bp->b_iocmd = BIO_WRITE;
 
-		bp->b_rcred = crhold(thread0.td_ucred);
-		bp->b_wcred = crhold(thread0.td_ucred);
-		bp->b_bcount = PAGE_SIZE * n;
-		bp->b_bufsize = PAGE_SIZE * n;
-		bp->b_blkno = blk;
-
+		/* Get a block of swap of size up to size n. */
 		VM_OBJECT_WLOCK(object);
+		blk = swp_pager_getswapspace(&n, 4);
+		if (blk == SWAPBLK_NONE) {
+			VM_OBJECT_WUNLOCK(object);
+			mtx_lock(&swbuf_mtx);
+			if (++nsw_wcount_async == 1)
+				wakeup(&nsw_wcount_async);
+			mtx_unlock(&swbuf_mtx);
+			for (j = 0; j < n; ++j)
+				rtvals[i + j] = VM_PAGER_FAIL;
+			continue;
+		}
 		for (j = 0; j < n; ++j) {
 			mreq = ma[i + j];
 			vm_page_aflag_clear(mreq, PGA_SWAP_FREE);
@@ -1496,10 +1485,24 @@ swap_pager_putpages(vm_object_t object, vm_page_t *ma,
 				    addr);
 			MPASS(mreq->dirty == VM_PAGE_BITS_ALL);
 			mreq->oflags |= VPO_SWAPINPROG;
-			bp->b_pages[j] = mreq;
 		}
 		VM_OBJECT_WUNLOCK(object);
+
+		bp = uma_zalloc(swwbuf_zone, M_WAITOK);
+		if (async)
+			bp->b_flags = B_ASYNC;
+		bp->b_flags |= B_PAGING;
+		bp->b_iocmd = BIO_WRITE;
+
+		bp->b_rcred = crhold(thread0.td_ucred);
+		bp->b_wcred = crhold(thread0.td_ucred);
+		bp->b_bcount = PAGE_SIZE * n;
+		bp->b_bufsize = PAGE_SIZE * n;
+		bp->b_blkno = blk;
+		for (j = 0; j < n; j++)
+			bp->b_pages[j] = ma[i + j];
 		bp->b_npages = n;
+
 		/*
 		 * Must set dirty range for NFS to work.
 		 */
@@ -2059,7 +2062,7 @@ allocated:
 	 * Free the swblk if we end up with the empty page run.
 	 */
 	if (swapblk == SWAPBLK_NONE)
-	    swp_pager_free_empty_swblk(object, sb);
+		swp_pager_free_empty_swblk(object, sb);
 	return (prev_swapblk);
 }
 


More information about the svn-src-head mailing list