diff options
author | Owain Ainsworth <oga@cvs.openbsd.org> | 2011-05-10 21:48:18 +0000 |
---|---|---|
committer | Owain Ainsworth <oga@cvs.openbsd.org> | 2011-05-10 21:48:18 +0000 |
commit | 563ca8765260186ce5628efb1fc5501b5e34c8f7 (patch) | |
tree | 46fa4124973f87704bc3d2cd99e3599812ebb963 /sys | |
parent | 2c12eefb2876e0c05a397f3bba92fc865bb0624d (diff) |
Don't leak swapslots when doing a uvm_km_pgremove and a page is in swap only.
Before we were only calling uao_dropswap() if there was a page, maning
that if the buffer was swapped out then we would leak the slot.
Quite rare because only pipebuffers should swap from the kernel object,
but i've seen panics that implied this had happened (alpha.p for example).
ok thib@ after a lot of discussion and checking the semantics.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/uvm/uvm_aobj.c | 5 | ||||
-rw-r--r-- | sys/uvm/uvm_aobj.h | 4 | ||||
-rw-r--r-- | sys/uvm/uvm_km.c | 27 |
3 files changed, 17 insertions, 19 deletions
diff --git a/sys/uvm/uvm_aobj.c b/sys/uvm/uvm_aobj.c index fdea6df5605..527d1a27328 100644 --- a/sys/uvm/uvm_aobj.c +++ b/sys/uvm/uvm_aobj.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_aobj.c,v 1.52 2011/05/07 15:31:25 oga Exp $ */ +/* $OpenBSD: uvm_aobj.c,v 1.53 2011/05/10 21:48:17 oga Exp $ */ /* $NetBSD: uvm_aobj.c,v 1.39 2001/02/18 21:19:08 chs Exp $ */ /* @@ -1139,7 +1139,7 @@ uao_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps, * => aobj must be locked or have a reference count of 0. */ -void +int uao_dropswap(struct uvm_object *uobj, int pageidx) { int slot; @@ -1148,6 +1148,7 @@ uao_dropswap(struct uvm_object *uobj, int pageidx) if (slot) { uvm_swap_free(slot, 1); } + return (slot); } diff --git a/sys/uvm/uvm_aobj.h b/sys/uvm/uvm_aobj.h index 27645df5b9b..218e9e3df65 100644 --- a/sys/uvm/uvm_aobj.h +++ b/sys/uvm/uvm_aobj.h @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_aobj.h,v 1.12 2009/07/22 21:05:37 oga Exp $ */ +/* $OpenBSD: uvm_aobj.h,v 1.13 2011/05/10 21:48:17 oga Exp $ */ /* $NetBSD: uvm_aobj.h,v 1.10 2000/01/11 06:57:49 chs Exp $ */ /* @@ -65,7 +65,7 @@ void uao_init(void); int uao_set_swslot(struct uvm_object *, int, int); -void uao_dropswap(struct uvm_object *, int); +int uao_dropswap(struct uvm_object *, int); int uao_swap_off(int, int); /* diff --git a/sys/uvm/uvm_km.c b/sys/uvm/uvm_km.c index 2a3a2ffe7c6..368aaa92f2d 100644 --- a/sys/uvm/uvm_km.c +++ b/sys/uvm/uvm_km.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_km.c,v 1.100 2011/04/23 17:48:48 kettenis Exp $ */ +/* $OpenBSD: uvm_km.c,v 1.101 2011/05/10 21:48:17 oga Exp $ */ /* $NetBSD: uvm_km.c,v 1.42 2001/01/14 02:10:01 thorpej Exp $ */ /* @@ -267,36 +267,33 @@ uvm_km_pgremove(struct uvm_object *uobj, vaddr_t start, vaddr_t end) { struct vm_page *pp; voff_t curoff; + int slot; UVMHIST_FUNC("uvm_km_pgremove"); UVMHIST_CALLED(maphist); KASSERT(uobj->pgops == &aobj_pager); for (curoff = start ; curoff < end ; curoff += PAGE_SIZE) { pp = uvm_pagelookup(uobj, curoff); - if (pp == NULL) - continue; - - UVMHIST_LOG(maphist," page %p, busy=%ld", pp, - pp->pg_flags & PG_BUSY, 0, 0); - - if (pp->pg_flags & PG_BUSY) { + if (pp && pp->pg_flags & PG_BUSY) { atomic_setbits_int(&pp->pg_flags, PG_WANTED); UVM_UNLOCK_AND_WAIT(pp, &uobj->vmobjlock, 0, "km_pgrm", 0); simple_lock(&uobj->vmobjlock); curoff -= PAGE_SIZE; /* loop back to us */ continue; - } else { - /* free the swap slot... */ - uao_dropswap(uobj, curoff >> PAGE_SHIFT); + } + + /* free the swap slot, then the page */ + slot = uao_dropswap(uobj, curoff >> PAGE_SHIFT); - /* - * ...and free the page; note it may be on the - * active or inactive queues. - */ + if (pp != NULL) { uvm_lock_pageq(); uvm_pagefree(pp); uvm_unlock_pageq(); + } else if (slot != 0) { + simple_lock(&uvm.swap_data_lock); + uvmexp.swpgonly--; + simple_unlock(&uvm.swap_data_lock); } } } |