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);
 }
 
_______________________________________________
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