Fix a race in vm_page_dequeue_deferred_free() after r352110.

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
This commit is contained in:
Mark Johnston 2019-09-16 15:12:49 +00:00
parent 47aef898ea
commit 923da43e7c
2 changed files with 12 additions and 9 deletions

View 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;
}
}
}
/*

View 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;