From a9ea09e548882b7b15bcff47509b3d42618815cf Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Tue, 28 Apr 2020 13:51:41 +0000 Subject: [PATCH] Re-check for wirings after busying the page in vm_page_release_locked(). A concurrent unlocked lookup can wire the page after vm_page_release_locked() releases the last wiring, in which case vm_page_release_locked() must not free the page. Once the xbusy lock is acquired, that, the object lock and the fact that the page is unmapped ensure that the wire count cannot increase, so re-check for new wirings after the page is xbusied. Update the comment above vm_page_wired() to reflect the new synchronization rules. Reported by: glebius Reviewed by: alc, jeff, kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D24592 --- sys/vm/vm_page.c | 11 ++++++++++- sys/vm/vm_page.h | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c index 5c7ca6409d73..22eacf423b95 100644 --- a/sys/vm/vm_page.c +++ b/sys/vm/vm_page.c @@ -4165,7 +4165,16 @@ vm_page_release_locked(vm_page_t m, int flags) if ((flags & VPR_TRYFREE) != 0 && (m->object->ref_count == 0 || !pmap_page_is_mapped(m)) && m->dirty == 0 && vm_page_tryxbusy(m)) { - vm_page_free(m); + /* + * An unlocked lookup may have wired the page before the + * busy lock was acquired, in which case the page must + * not be freed. + */ + if (__predict_true(!vm_page_wired(m))) { + vm_page_free(m); + return; + } + vm_page_xunbusy(m); } else { vm_page_release_toq(m, PQ_INACTIVE, flags != 0); } diff --git a/sys/vm/vm_page.h b/sys/vm/vm_page.h index 8436f11acaf9..2ed6840bfece 100644 --- a/sys/vm/vm_page.h +++ b/sys/vm/vm_page.h @@ -958,8 +958,8 @@ vm_page_drop(vm_page_t m, u_int val) * * Perform a racy check to determine whether a reference prevents the page * from being reclaimable. If the page's object is locked, and the page is - * unmapped and unbusied or exclusively busied by the current thread, no - * new wirings may be created. + * unmapped and exclusively busied by the current thread, no new wirings + * may be created. */ static inline bool vm_page_wired(vm_page_t m)