summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorOwain Ainsworth <oga@cvs.openbsd.org>2011-05-10 21:48:18 +0000
committerOwain Ainsworth <oga@cvs.openbsd.org>2011-05-10 21:48:18 +0000
commit563ca8765260186ce5628efb1fc5501b5e34c8f7 (patch)
tree46fa4124973f87704bc3d2cd99e3599812ebb963 /sys
parent2c12eefb2876e0c05a397f3bba92fc865bb0624d (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.c5
-rw-r--r--sys/uvm/uvm_aobj.h4
-rw-r--r--sys/uvm/uvm_km.c27
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);
}
}
}