Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/uvm PR kern/54783: t_mmap crahes the kernel
details: https://anonhg.NetBSD.org/src/rev/223fafc12fd7
branches: trunk
changeset: 847335:223fafc12fd7
user: ad <ad%NetBSD.org@localhost>
date: Wed Dec 18 20:38:14 2019 +0000
description:
PR kern/54783: t_mmap crahes the kernel
- Fix various locking & sequencing errors with breaking loans.
- Don't call uvm_pageremove_tree() while holding pg->interlock as radixtree
can take further locks when freeing nodes.
diffstat:
sys/uvm/uvm_loan.c | 91 ++++++++++++++++++++++++++---------------------------
sys/uvm/uvm_page.c | 72 +++++++++++++++++-------------------------
2 files changed, 73 insertions(+), 90 deletions(-)
diffs (truncated from 312 to 300 lines):
diff -r a9c1fa49eea5 -r 223fafc12fd7 sys/uvm/uvm_loan.c
--- a/sys/uvm/uvm_loan.c Wed Dec 18 19:40:34 2019 +0000
+++ b/sys/uvm/uvm_loan.c Wed Dec 18 20:38:14 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_loan.c,v 1.91 2019/12/15 21:11:35 ad Exp $ */
+/* $NetBSD: uvm_loan.c,v 1.92 2019/12/18 20:38:14 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.91 2019/12/15 21:11:35 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.92 2019/12/18 20:38:14 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -1121,7 +1121,7 @@
* force a reload of the old page by clearing it from all
* pmaps.
* transfer dirtiness of the old page to the new page.
- * then lock pg->interlock to rename the pages.
+ * then rename the pages.
*/
uvm_pagecopy(uobjpage, pg); /* old -> new */
@@ -1143,14 +1143,6 @@
UVM_PAGE_OWN(uobjpage, NULL);
/*
- * replace uobjpage with new page.
- */
-
- mutex_enter(&uobjpage->interlock);
- uvm_pagereplace(uobjpage, pg);
- mutex_exit(&uobjpage->interlock);
-
- /*
* if the page is no longer referenced by
* an anon (i.e. we are breaking an O->K
* loan), then remove it from any pageq's.
@@ -1159,6 +1151,12 @@
uvm_pagedequeue(uobjpage);
/*
+ * replace uobjpage with new page.
+ */
+
+ uvm_pagereplace(uobjpage, pg);
+
+ /*
* at this point we have absolutely no
* control over uobjpage
*/
@@ -1176,59 +1174,58 @@
int
uvm_loanbreak_anon(struct vm_anon *anon, struct uvm_object *uobj)
{
- struct vm_page *pg, *dequeuepg;
+ struct vm_page *newpg, *oldpg;
KASSERT(mutex_owned(anon->an_lock));
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
/* get new un-owned replacement page */
- pg = uvm_pagealloc(NULL, 0, NULL, 0);
- if (pg == NULL) {
+ newpg = uvm_pagealloc(NULL, 0, NULL, 0);
+ if (newpg == NULL) {
return ENOMEM;
}
- /* copy old -> new */
- uvm_pagecopy(anon->an_page, pg);
-
- /* force reload */
- pmap_page_protect(anon->an_page, VM_PROT_NONE);
- if (pg < anon->an_page) {
- mutex_enter(&pg->interlock);
- mutex_enter(&anon->an_page->interlock);
- } else {
- mutex_enter(&anon->an_page->interlock);
- mutex_enter(&pg->interlock);
- }
- anon->an_page->uanon = NULL;
- /* in case we owned */
- anon->an_page->flags &= ~PG_ANON;
-
- if (uobj) {
- /* if we were receiver of loan */
- anon->an_page->loan_count--;
- dequeuepg = NULL;
- } else {
+ oldpg = anon->an_page;
+ if (uobj == NULL) {
/*
* we were the lender (A->K); need to remove the page from
* pageq's.
*/
- dequeuepg = anon->an_page;
+ uvm_pagedequeue(oldpg);
+ }
+
+ /* copy old -> new */
+ uvm_pagecopy(oldpg, newpg);
+
+ /* force reload */
+ pmap_page_protect(oldpg, VM_PROT_NONE);
+ if (newpg < oldpg) {
+ mutex_enter(&newpg->interlock);
+ mutex_enter(&oldpg->interlock);
+ } else {
+ mutex_enter(&oldpg->interlock);
+ mutex_enter(&newpg->interlock);
+ }
+ oldpg->uanon = NULL;
+ /* in case we owned */
+ oldpg->flags &= ~PG_ANON;
+
+ if (uobj) {
+ /* if we were receiver of loan */
+ oldpg->loan_count--;
}
/* install new page in anon */
- anon->an_page = pg;
- pg->uanon = anon;
- pg->flags |= PG_ANON;
+ anon->an_page = newpg;
+ newpg->uanon = anon;
+ newpg->flags |= PG_ANON;
- mutex_exit(&pg->interlock);
- mutex_exit(&anon->an_page->interlock);
- uvm_pageactivate(pg);
- if (dequeuepg != NULL) {
- uvm_pagedequeue(anon->an_page);
- }
+ mutex_exit(&newpg->interlock);
+ mutex_exit(&oldpg->interlock);
+ uvm_pageactivate(newpg);
- pg->flags &= ~(PG_BUSY|PG_FAKE);
- UVM_PAGE_OWN(pg, NULL);
+ newpg->flags &= ~(PG_BUSY|PG_FAKE);
+ UVM_PAGE_OWN(newpg, NULL);
if (uobj) {
mutex_exit(uobj->vmobjlock);
diff -r a9c1fa49eea5 -r 223fafc12fd7 sys/uvm/uvm_page.c
--- a/sys/uvm/uvm_page.c Wed Dec 18 19:40:34 2019 +0000
+++ b/sys/uvm/uvm_page.c Wed Dec 18 20:38:14 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_page.c,v 1.205 2019/12/16 22:47:55 ad Exp $ */
+/* $NetBSD: uvm_page.c,v 1.206 2019/12/18 20:38:14 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.205 2019/12/16 22:47:55 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.206 2019/12/18 20:38:14 ad Exp $");
#include "opt_ddb.h"
#include "opt_uvm.h"
@@ -148,13 +148,6 @@
struct vm_page *uvm_physseg_seg_alloc_from_slab(uvm_physseg_t, size_t);
/*
- * local prototypes
- */
-
-static int uvm_pageinsert(struct uvm_object *, struct vm_page *);
-static void uvm_pageremove(struct uvm_object *, struct vm_page *);
-
-/*
* inline functions
*/
@@ -205,22 +198,6 @@
return 0;
}
-static inline int
-uvm_pageinsert(struct uvm_object *uobj, struct vm_page *pg)
-{
- int error;
-
- KDASSERT(uobj != NULL);
- KDASSERT(uobj == pg->uobject);
- error = uvm_pageinsert_tree(uobj, pg);
- if (error != 0) {
- KASSERT(error == ENOMEM);
- return error;
- }
- uvm_pageinsert_object(uobj, pg);
- return error;
-}
-
/*
* uvm_page_remove: remove page from object.
*
@@ -265,16 +242,6 @@
KASSERT(pg == opg);
}
-static inline void
-uvm_pageremove(struct uvm_object *uobj, struct vm_page *pg)
-{
-
- KDASSERT(uobj != NULL);
- KASSERT(uobj == pg->uobject);
- uvm_pageremove_object(uobj, pg);
- uvm_pageremove_tree(uobj, pg);
-}
-
static void
uvm_page_init_buckets(struct pgfreelist *pgfl)
{
@@ -1043,9 +1010,10 @@
pg->flags |= PG_ANON;
cpu_count(CPU_COUNT_ANONPAGES, 1);
} else if (obj) {
- error = uvm_pageinsert(obj, pg);
+ uvm_pageinsert_object(obj, pg);
+ error = uvm_pageinsert_tree(obj, pg);
if (error != 0) {
- pg->uobject = NULL;
+ uvm_pageremove_object(obj, pg);
uvm_pagefree(pg);
return NULL;
}
@@ -1077,7 +1045,6 @@
* uvm_pagereplace: replace a page with another
*
* => object must be locked
- * => interlock must be held
*/
void
@@ -1091,13 +1058,23 @@
KASSERT(newpg->uobject == NULL);
KASSERT(mutex_owned(uobj->vmobjlock));
- newpg->uobject = uobj;
newpg->offset = oldpg->offset;
-
uvm_pageremove_tree(uobj, oldpg);
uvm_pageinsert_tree(uobj, newpg);
+
+ /* take page interlocks during rename */
+ if (oldpg < newpg) {
+ mutex_enter(&oldpg->interlock);
+ mutex_enter(&newpg->interlock);
+ } else {
+ mutex_enter(&newpg->interlock);
+ mutex_enter(&oldpg->interlock);
+ }
+ newpg->uobject = uobj;
uvm_pageinsert_object(uobj, newpg);
uvm_pageremove_object(uobj, oldpg);
+ mutex_exit(&oldpg->interlock);
+ mutex_exit(&newpg->interlock);
}
/*
@@ -1115,7 +1092,8 @@
*/
if (pg->uobject) {
- uvm_pageremove(pg->uobject, pg);
+ uvm_pageremove_tree(pg->uobject, pg);
+ uvm_pageremove_object(pg->uobject, pg);
}
/*
@@ -1197,6 +1175,14 @@
mutex_owned(pg->uanon->an_lock));
/*
+ * remove the page from the object's tree beore acquiring any page
+ * interlocks: this can acquire locks to free radixtree nodes.
+ */
+ if (pg->uobject != NULL) {
+ uvm_pageremove_tree(pg->uobject, pg);
+ }
+
+ /*
* if the page is loaned, resolve the loan instead of freeing.
*/
@@ -1217,7 +1203,7 @@
mutex_enter(&pg->interlock);
locked = true;
if (pg->uobject != NULL) {
- uvm_pageremove(pg->uobject, pg);
+ uvm_pageremove_object(pg->uobject, pg);
Home |
Main Index |
Thread Index |
Old Index