]> xenbits.xensource.com Git - freebsd.git/commitdiff
Fix a race in vm_page_dequeue_deferred_free() after r352110.
authormarkj <markj@FreeBSD.org>
Mon, 16 Sep 2019 15:12:49 +0000 (15:12 +0000)
committermarkj <markj@FreeBSD.org>
Mon, 16 Sep 2019 15:12:49 +0000 (15:12 +0000)
This function loaded the page's queue index before setting PGA_DEQUEUE.
In this window the page daemon may have deactivated the page, updating
its queue index.  Make the operation atomic using vm_page_pqstate_cmpset();
the page daemon will not modify the page once it observes that PGA_DEQUEUE
is set.

Reported and tested by: pho
Reviewed by: alc, kib
Sponsored by: Netflix
Differential Revision: https://reviews.freebsd.org/D21639

sys/vm/vm_page.c
sys/vm/vm_page.h

index 092e9ee08ddfa1f8ea82601791ba86c62712f623..bbfc5a7a334c3406977b0213e7f1be9feeeb3658 100644 (file)
@@ -3315,13 +3315,18 @@ vm_page_dequeue_deferred_free(vm_page_t m)
 
        KASSERT(m->ref_count == 0, ("page %p has references", m));
 
-       if ((m->aflags & PGA_DEQUEUE) != 0)
-               return;
-       atomic_thread_fence_acq();
-       if ((queue = m->queue) == PQ_NONE)
-               return;
-       vm_page_aflag_set(m, PGA_DEQUEUE);
-       vm_page_pqbatch_submit(m, queue);
+       for (;;) {
+               if ((m->aflags & PGA_DEQUEUE) != 0)
+                       return;
+               atomic_thread_fence_acq();
+               if ((queue = atomic_load_8(&m->queue)) == PQ_NONE)
+                       return;
+               if (vm_page_pqstate_cmpset(m, queue, queue, PGA_DEQUEUE,
+                   PGA_DEQUEUE)) {
+                       vm_page_pqbatch_submit(m, queue);
+                       break;
+               }
+       }
 }
 
 /*
index 0c3f3a9bade2ea3a94ceac4553d51f2b1b27f2a3..07672d38e59a4075dd73238f9544b208b64b8981 100644 (file)
@@ -783,8 +783,6 @@ vm_page_pqstate_cmpset(vm_page_t m, uint32_t oldq, uint32_t newq,
 {
        uint32_t *addr, nval, oval, qsmask;
 
-       vm_page_assert_locked(m);
-
        fflags <<= VM_PAGE_AFLAG_SHIFT;
        nflags <<= VM_PAGE_AFLAG_SHIFT;
        newq <<= VM_PAGE_QUEUE_SHIFT;