Author: markj
Date: Wed Jun 14 03:45:26 2017
New Revision: 319932
URL: https://svnweb.freebsd.org/changeset/base/319932

Log:
  Fix handling of subpage BIO_WRITE and BIO_DELETE requests on swap MDs.
  
  Such requests would previously mark the entire page as valid, which was
  incorrect since nothing guaranteed that the page's contents had been
  initialized. This change also modifies subpage BIO_DELETEs so that the
  entire page is marked dirty, rather than only a subrange. There is no
  benefit to creating partially dirty swap pages.
  
  Reviewed by:  alc, kib (previous version)
  MFC after:    3 days

Modified:
  head/sys/dev/md/md.c

Modified: head/sys/dev/md/md.c
==============================================================================
--- head/sys/dev/md/md.c        Wed Jun 14 02:46:38 2017        (r319931)
+++ head/sys/dev/md/md.c        Wed Jun 14 03:45:26 2017        (r319932)
@@ -972,6 +972,16 @@ unmapped_step:
        return (error);
 }
 
+static void
+md_swap_page_free(vm_page_t m)
+{
+
+       vm_page_xunbusy(m);
+       vm_page_lock(m);
+       vm_page_free(m);
+       vm_page_unlock(m);
+}
+
 static int
 mdstart_swap(struct md_s *sc, struct bio *bp)
 {
@@ -1044,15 +1054,17 @@ mdstart_swap(struct md_s *sc, struct bio *bp)
                                cpu_flush_dcache(p, len);
                        }
                } else if (bp->bio_cmd == BIO_WRITE) {
-                       if (len != PAGE_SIZE && m->valid != VM_PAGE_BITS_ALL)
+                       if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL)
+                               rv = VM_PAGER_OK;
+                       else
                                rv = vm_pager_get_pages(sc->object, &m, 1,
                                    NULL, NULL);
-                       else
-                               rv = VM_PAGER_OK;
                        if (rv == VM_PAGER_ERROR) {
                                vm_page_xunbusy(m);
                                break;
-                       }
+                       } else if (rv == VM_PAGER_FAIL)
+                               pmap_zero_page(m);
+
                        if ((bp->bio_flags & BIO_UNMAPPED) != 0) {
                                pmap_copy_pages(bp->bio_ma, ma_offs, &m,
                                    offs, len);
@@ -1062,34 +1074,40 @@ mdstart_swap(struct md_s *sc, struct bio *bp)
                        } else {
                                physcopyin(p, VM_PAGE_TO_PHYS(m) + offs, len);
                        }
+
                        m->valid = VM_PAGE_BITS_ALL;
+                       vm_page_dirty(m);
+                       vm_pager_page_unswapped(m);
                } else if (bp->bio_cmd == BIO_DELETE) {
-                       if (len != PAGE_SIZE && m->valid != VM_PAGE_BITS_ALL)
+                       if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL)
+                               rv = VM_PAGER_OK;
+                       else
                                rv = vm_pager_get_pages(sc->object, &m, 1,
                                    NULL, NULL);
-                       else
-                               rv = VM_PAGER_OK;
                        if (rv == VM_PAGER_ERROR) {
                                vm_page_xunbusy(m);
                                break;
-                       }
-                       if (len != PAGE_SIZE) {
-                               pmap_zero_page_area(m, offs, len);
-                               vm_page_clear_dirty(m, offs, len);
-                               m->valid = VM_PAGE_BITS_ALL;
-                       } else
+                       } else if (rv == VM_PAGER_FAIL) {
+                               md_swap_page_free(m);
+                               m = NULL;
+                       } else {
+                               /* Page is valid. */
+                               if (len != PAGE_SIZE) {
+                                       pmap_zero_page_area(m, offs, len);
+                                       vm_page_dirty(m);
+                               }
                                vm_pager_page_unswapped(m);
+                               if (len == PAGE_SIZE) {
+                                       md_swap_page_free(m);
+                                       m = NULL;
+                               }
+                       }
                }
-               vm_page_xunbusy(m);
-               vm_page_lock(m);
-               if (bp->bio_cmd == BIO_DELETE && len == PAGE_SIZE)
-                       vm_page_free(m);
-               else
+               if (m != NULL) {
+                       vm_page_xunbusy(m);
+                       vm_page_lock(m);
                        vm_page_activate(m);
-               vm_page_unlock(m);
-               if (bp->bio_cmd == BIO_WRITE) {
-                       vm_page_dirty(m);
-                       vm_pager_page_unswapped(m);
+                       vm_page_unlock(m);
                }
 
                /* Actions on further pages start at offset 0 */
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to