Module Name: src
Committed By: yamt
Date: Mon Dec 26 16:03:12 UTC 2011
Modified Files:
src/sys/kern [yamt-pagecache]: kern_mutex_obj.c
src/sys/sys [yamt-pagecache]: mutex.h
src/sys/uvm [yamt-pagecache]: uvm.h uvm_amap.c uvm_amap.h uvm_anon.c
uvm_extern.h uvm_fault.c uvm_loan.c uvm_loan.h uvm_map.c
uvm_meter.c uvm_page.c uvm_pdaemon.c
Log Message:
- use O->A loan to serve read(2). based on a patch from Chuck Silvers
- associated O->A loan fixes.
To generate a diff of this commit:
cvs rdiff -u -r1.5.2.1 -r1.5.2.2 src/sys/kern/kern_mutex_obj.c
cvs rdiff -u -r1.20.10.1 -r1.20.10.2 src/sys/sys/mutex.h
cvs rdiff -u -r1.62.4.3 -r1.62.4.4 src/sys/uvm/uvm.h
cvs rdiff -u -r1.104 -r1.104.2.1 src/sys/uvm/uvm_amap.c
cvs rdiff -u -r1.37 -r1.37.2.1 src/sys/uvm/uvm_amap.h
cvs rdiff -u -r1.62.2.1 -r1.62.2.2 src/sys/uvm/uvm_anon.c
cvs rdiff -u -r1.176.2.5 -r1.176.2.6 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.190.2.2 -r1.190.2.3 src/sys/uvm/uvm_fault.c
cvs rdiff -u -r1.81.2.4 -r1.81.2.5 src/sys/uvm/uvm_loan.c
cvs rdiff -u -r1.17 -r1.17.4.1 src/sys/uvm/uvm_loan.h
cvs rdiff -u -r1.305 -r1.305.2.1 src/sys/uvm/uvm_map.c
cvs rdiff -u -r1.56.4.5 -r1.56.4.6 src/sys/uvm/uvm_meter.c
cvs rdiff -u -r1.178.2.8 -r1.178.2.9 src/sys/uvm/uvm_page.c
cvs rdiff -u -r1.103.2.2 -r1.103.2.3 src/sys/uvm/uvm_pdaemon.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/kern_mutex_obj.c
diff -u src/sys/kern/kern_mutex_obj.c:1.5.2.1 src/sys/kern/kern_mutex_obj.c:1.5.2.2
--- src/sys/kern/kern_mutex_obj.c:1.5.2.1 Fri Nov 18 00:57:33 2011
+++ src/sys/kern/kern_mutex_obj.c Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_mutex_obj.c,v 1.5.2.1 2011/11/18 00:57:33 yamt Exp $ */
+/* $NetBSD: kern_mutex_obj.c,v 1.5.2.2 2011/12/26 16:03:10 yamt Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_mutex_obj.c,v 1.5.2.1 2011/11/18 00:57:33 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_mutex_obj.c,v 1.5.2.2 2011/12/26 16:03:10 yamt Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
@@ -147,6 +147,38 @@ mutex_obj_free(kmutex_t *lock)
}
/*
+ * mutex_obj_free_if_last:
+ *
+ * Drop a reference from a lock object if it's the last reference.
+ * If the last reference is being dropped, free the object and return
+ * true. Otherwise, return false.
+ */
+bool
+mutex_obj_free_if_last(kmutex_t *lock)
+{
+ struct kmutexobj *mo = (struct kmutexobj *)lock;
+ bool ret;
+
+ KASSERTMSG(mo->mo_magic == MUTEX_OBJ_MAGIC,
+ "%s: lock %p: mo->mo_magic (%#x) != MUTEX_OBJ_MAGIC (%#x)",
+ __func__, mo, mo->mo_magic, MUTEX_OBJ_MAGIC);
+ KASSERTMSG(mo->mo_refcnt > 0,
+ "%s: lock %p: mo->mo_refcnt (%#x) == 0",
+ __func__, mo, mo->mo_refcnt);
+
+ /*
+ * if mo_refcnt is 1, no one except us have a reference to it and
+ * thus it's stable.
+ */
+ if (mo->mo_refcnt != 1) {
+ return false;
+ }
+ ret = mutex_obj_free(lock);
+ KASSERT(ret);
+ return true;
+}
+
+/*
* mutex_obj_pause:
*
* Pause until lock1 is available.
@@ -162,6 +194,10 @@ mutex_obj_pause(kmutex_t *lock1, kmutex_
KASSERT(mutex_owned(lock2));
mutex_obj_hold(lock1);
mutex_exit(lock2);
+ /*
+ * acquire and release lock1.
+ * this can involve priority lending.
+ */
mutex_enter(lock1);
mutex_exit(lock1);
mutex_obj_free(lock1);
Index: src/sys/sys/mutex.h
diff -u src/sys/sys/mutex.h:1.20.10.1 src/sys/sys/mutex.h:1.20.10.2
--- src/sys/sys/mutex.h:1.20.10.1 Fri Nov 18 00:57:33 2011
+++ src/sys/sys/mutex.h Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: mutex.h,v 1.20.10.1 2011/11/18 00:57:33 yamt Exp $ */
+/* $NetBSD: mutex.h,v 1.20.10.2 2011/12/26 16:03:10 yamt Exp $ */
/*-
* Copyright (c) 2002, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -210,6 +210,7 @@ void mutex_obj_init(void);
kmutex_t *mutex_obj_alloc(kmutex_type_t, int);
void mutex_obj_hold(kmutex_t *);
bool mutex_obj_free(kmutex_t *);
+bool mutex_obj_free_if_last(kmutex_t *);
void mutex_obj_pause(kmutex_t *, kmutex_t *);
kmutex_t *mutex_obj_alloc_kernel_obj_lock(kmutex_type_t, int);
Index: src/sys/uvm/uvm.h
diff -u src/sys/uvm/uvm.h:1.62.4.3 src/sys/uvm/uvm.h:1.62.4.4
--- src/sys/uvm/uvm.h:1.62.4.3 Sun Nov 20 10:52:33 2011
+++ src/sys/uvm/uvm.h Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm.h,v 1.62.4.3 2011/11/20 10:52:33 yamt Exp $ */
+/* $NetBSD: uvm.h,v 1.62.4.4 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -91,16 +91,35 @@ struct uvm_cpu {
int64_t loan_obj; /* O->K loan */
int64_t unloan_obj; /* O->K unloan */
- int64_t loanbreak_obj; /* O->K loan resolved on write */
- int64_t loanfree_obj; /* O->K loan resolved on free */
+ int64_t loanbreak_obj; /* O->K loan resolved on write to O */
+ int64_t loanfree_obj; /* O->K loan resolved on free of O */
int64_t loan_anon; /* A->K loan */
int64_t unloan_anon; /* A->K unloan */
- int64_t loanbreak_anon; /* A->K loan resolved on write */
- int64_t loanfree_anon; /* A->K loan resolved on free */
+ int64_t loanbreak_anon; /* A->K loan resolved on write to A */
+ int64_t loanfree_anon; /* A->K loan resolved on free of A */
+
+ int64_t loan_oa; /* O->A->K loan */
+ int64_t unloan_oa; /* O->A->K unloan */
int64_t loan_zero; /* O->K loan (zero) */
int64_t unloan_zero; /* O->K unloan (zero) */
+
+ int64_t loanbreak_orphaned; /* O->A->K loan turned into A->K loan due to
+ write to O */
+ int64_t loanfree_orphaned; /* O->A->K loan turned into A->K loan due to
+ free of O */
+ int64_t loanbreak_orphaned_anon; /* O->A->K loan turned into O->K loan
+ due to write to A */
+ int64_t loanfree_orphaned_anon; /* O->A->K loan turned into O->K loan
+ due to free of A */
+
+ int64_t loanbreak_oa_obj; /* O->A loan resolved on write to O */
+ int64_t loanfree_oa_obj; /* O->A loan resolved on free of O */
+ int64_t loanbreak_oa_anon; /* O->A loan resolved on write to A */
+ int64_t loanfree_oa_anon; /* O->A loan resolved on free of A */
+ int64_t loan_resolve_orphan; /* O->A loaned page taken over by anon */
+ int64_t loan_obj_read; /* O->A loan for read(2) */
};
/*
Index: src/sys/uvm/uvm_amap.c
diff -u src/sys/uvm/uvm_amap.c:1.104 src/sys/uvm/uvm_amap.c:1.104.2.1
--- src/sys/uvm/uvm_amap.c:1.104 Tue Oct 11 23:57:50 2011
+++ src/sys/uvm/uvm_amap.c Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_amap.c,v 1.104 2011/10/11 23:57:50 yamt Exp $ */
+/* $NetBSD: uvm_amap.c,v 1.104.2.1 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_amap.c,v 1.104 2011/10/11 23:57:50 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_amap.c,v 1.104.2.1 2011/12/26 16:03:10 yamt Exp $");
#include "opt_uvmhist.h"
@@ -178,6 +178,7 @@ amap_alloc1(int slots, int padslots, int
}
totalslots = amap_roundup_slots(slots + padslots);
amap->am_lock = NULL;
+ amap->am_obj_lock = NULL;
amap->am_ref = 1;
amap->am_flags = 0;
#ifdef UVM_AMAP_PPREF
@@ -288,6 +289,9 @@ amap_free(struct vm_amap *amap)
KASSERT(!mutex_owned(amap->am_lock));
mutex_obj_free(amap->am_lock);
}
+ if (amap->am_obj_lock != NULL) {
+ mutex_obj_free(amap->am_obj_lock);
+ }
slots = amap->am_maxslot;
kmem_free(amap->am_slots, slots * sizeof(*amap->am_slots));
kmem_free(amap->am_bckptr, slots * sizeof(*amap->am_bckptr));
@@ -767,6 +771,7 @@ amap_copy(struct vm_map *map, struct vm_
struct vm_anon *tofree;
u_int slots, lcv;
vsize_t len;
+ bool have_obj_page;
UVMHIST_FUNC("amap_copy"); UVMHIST_CALLED(maphist);
UVMHIST_LOG(maphist, " (map=%p, entry=%p, flags=%d)",
@@ -881,13 +886,22 @@ amap_copy(struct vm_map *map, struct vm_
*/
UVMHIST_LOG(maphist, " copying amap now",0, 0, 0, 0);
+ have_obj_page = false;
for (lcv = 0 ; lcv < slots; lcv++) {
- amap->am_anon[lcv] =
+ struct vm_anon * const anon =
srcamap->am_anon[entry->aref.ar_pageoff + lcv];
- if (amap->am_anon[lcv] == NULL)
+
+ amap->am_anon[lcv] = anon;
+ if (anon == NULL)
continue;
- KASSERT(amap->am_anon[lcv]->an_lock == srcamap->am_lock);
- KASSERT(amap->am_anon[lcv]->an_ref > 0);
+ if (anon->an_page != NULL && anon->an_page->uobject != NULL) {
+ KASSERT(anon->an_page->loan_count > 0);
+ KASSERT(srcamap->am_obj_lock ==
+ anon->an_page->uobject->vmobjlock);
+ have_obj_page = true;
+ }
+ KASSERT(anon->an_lock == srcamap->am_lock);
+ KASSERT(anon->an_ref > 0);
amap->am_anon[lcv]->an_ref++;
amap->am_bckptr[lcv] = amap->am_nused;
amap->am_slots[amap->am_nused] = lcv;
@@ -925,6 +939,10 @@ amap_copy(struct vm_map *map, struct vm_
if (amap->am_nused != 0) {
amap->am_lock = srcamap->am_lock;
mutex_obj_hold(amap->am_lock);
+ if (have_obj_page) {
+ amap->am_obj_lock = srcamap->am_obj_lock;
+ mutex_obj_hold(amap->am_obj_lock);
+ }
}
uvm_anon_freelst(srcamap, tofree);
@@ -1618,3 +1636,45 @@ amap_unref(struct vm_amap *amap, vaddr_t
UVMHIST_LOG(maphist,"<- done!", 0, 0, 0, 0);
}
+
+void
+amap_lock(struct vm_amap *amap)
+{
+
+ mutex_enter(amap->am_lock);
+ if (amap->am_obj_lock != NULL) {
+ if (mutex_obj_free_if_last(amap->am_obj_lock)) {
+ amap->am_obj_lock = NULL;
+ } else {
+ mutex_enter(amap->am_obj_lock);
+ }
+ }
+}
+
+int
+amap_lock_try(struct vm_amap *amap)
+{
+
+ if (!mutex_tryenter(amap->am_lock)) {
+ return 0;
+ }
+ if (amap->am_obj_lock != NULL) {
+ if (mutex_obj_free_if_last(amap->am_obj_lock)) {
+ amap->am_obj_lock = NULL;
+ } else if (!mutex_tryenter(amap->am_obj_lock)) {
+ mutex_exit(amap->am_lock);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+amap_unlock(struct vm_amap *amap)
+{
+
+ if (amap->am_obj_lock != NULL) {
+ mutex_exit(amap->am_obj_lock);
+ }
+ mutex_exit(amap->am_lock);
+}
Index: src/sys/uvm/uvm_amap.h
diff -u src/sys/uvm/uvm_amap.h:1.37 src/sys/uvm/uvm_amap.h:1.37.2.1
--- src/sys/uvm/uvm_amap.h:1.37 Sun Jun 12 03:36:02 2011
+++ src/sys/uvm/uvm_amap.h Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_amap.h,v 1.37 2011/06/12 03:36:02 rmind Exp $ */
+/* $NetBSD: uvm_amap.h,v 1.37.2.1 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -82,6 +82,8 @@ void amap_free /* free amap */
(struct vm_amap *);
void amap_lock /* lock amap */
(struct vm_amap *);
+int amap_lock_try /* trylock amap */
+ (struct vm_amap *);
struct vm_anon *amap_lookup /* lookup an anon @ offset in amap */
(struct vm_aref *, vaddr_t);
void amap_lookups /* lookup multiple anons */
@@ -152,6 +154,7 @@ bool amap_swap_off
struct vm_amap {
kmutex_t *am_lock; /* lock [locks all vm_amap fields] */
+ kmutex_t *am_obj_lock; /* uobj which might lend us pages */
int am_ref; /* reference count */
int am_flags; /* flags */
int am_maxslot; /* max # of slots allocated */
@@ -251,10 +254,7 @@ struct vm_amap {
*/
#define amap_flags(AMAP) ((AMAP)->am_flags)
-#define amap_lock(AMAP) mutex_enter((AMAP)->am_lock)
-#define amap_lock_try(AMAP) mutex_tryenter((AMAP)->am_lock)
#define amap_refs(AMAP) ((AMAP)->am_ref)
-#define amap_unlock(AMAP) mutex_exit((AMAP)->am_lock)
/*
* if we enable PPREF, then we have a couple of extra functions that
Index: src/sys/uvm/uvm_anon.c
diff -u src/sys/uvm/uvm_anon.c:1.62.2.1 src/sys/uvm/uvm_anon.c:1.62.2.2
--- src/sys/uvm/uvm_anon.c:1.62.2.1 Wed Nov 2 21:54:00 2011
+++ src/sys/uvm/uvm_anon.c Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_anon.c,v 1.62.2.1 2011/11/02 21:54:00 yamt Exp $ */
+/* $NetBSD: uvm_anon.c,v 1.62.2.2 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.62.2.1 2011/11/02 21:54:00 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.62.2.2 2011/12/26 16:03:10 yamt Exp $");
#include "opt_uvmhist.h"
@@ -134,12 +134,18 @@ uvm_anon_dispose(struct vm_anon *anon)
*/
if (pg->uobject) {
+ struct uvm_cpu *ucpu;
+
mutex_enter(&uvm_pageqlock);
KASSERT(pg->loan_count > 0);
pg->loan_count--;
pg->uanon = NULL;
+ anon->an_page = NULL;
mutex_exit(&uvm_pageqlock);
- mutex_exit(pg->uobject->vmobjlock);
+ ucpu = uvm_cpu_get();
+ ucpu->loanfree_orphaned_anon += pg->loan_count;
+ ucpu->loanfree_oa_anon++;
+ uvm_cpu_put(ucpu);
} else {
/*
@@ -261,69 +267,19 @@ struct vm_page *
uvm_anon_lockloanpg(struct vm_anon *anon)
{
struct vm_page *pg;
- bool locked = false;
KASSERT(mutex_owned(anon->an_lock));
-
- /*
- * loop while we have a resident page that has a non-zero loan count.
- * if we successfully get our lock, we will "break" the loop.
- * note that the test for pg->loan_count is not protected -- this
- * may produce false positive results. note that a false positive
- * result may cause us to do more work than we need to, but it will
- * not produce an incorrect result.
- */
-
- while (((pg = anon->an_page) != NULL) && pg->loan_count != 0) {
-
- /*
- * quickly check to see if the page has an object before
- * bothering to lock the page queues. this may also produce
- * a false positive result, but that's ok because we do a real
- * check after that.
- */
-
- if (pg->uobject) {
- mutex_enter(&uvm_pageqlock);
- if (pg->uobject) {
- locked =
- mutex_tryenter(pg->uobject->vmobjlock);
- } else {
- /* object disowned before we got PQ lock */
- locked = true;
- }
- mutex_exit(&uvm_pageqlock);
-
- /*
- * if we didn't get a lock (try lock failed), then we
- * toggle our anon lock and try again
- */
-
- if (!locked) {
- /*
- * someone locking the object has a chance to
- * lock us right now
- *
- * XXX Better than yielding but inadequate.
- */
- kpause("livelock", false, 1, anon->an_lock);
- continue;
- }
- }
-
+ pg = anon->an_page;
+ if (pg == NULL) {
+ return NULL;
+ }
+ if (pg->uobject) {
/*
- * If page is un-owned i.e. the object dropped its ownership,
- * then we have to take the ownership.
+ * locked via amap->am_obj_lock
*/
-
- if (pg->uobject == NULL && (pg->pqflags & PQ_ANON) == 0) {
- mutex_enter(&uvm_pageqlock);
- pg->pqflags |= PQ_ANON;
- pg->loan_count--;
- mutex_exit(&uvm_pageqlock);
- }
- break;
+ KASSERT(mutex_owned(pg->uobject->vmobjlock));
}
+ uvm_loan_resolve_orphan(pg, false);
return pg;
}
Index: src/sys/uvm/uvm_extern.h
diff -u src/sys/uvm/uvm_extern.h:1.176.2.5 src/sys/uvm/uvm_extern.h:1.176.2.6
--- src/sys/uvm/uvm_extern.h:1.176.2.5 Tue Dec 20 13:46:17 2011
+++ src/sys/uvm/uvm_extern.h Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_extern.h,v 1.176.2.5 2011/12/20 13:46:17 yamt Exp $ */
+/* $NetBSD: uvm_extern.h,v 1.176.2.6 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -468,16 +468,35 @@ struct uvmexp_sysctl {
int64_t loan_obj; /* O->K loan */
int64_t unloan_obj; /* O->K unloan */
- int64_t loanbreak_obj; /* O->K loan resolved on write */
- int64_t loanfree_obj; /* O->K loan resolved on free */
+ int64_t loanbreak_obj; /* O->K loan resolved on write to O */
+ int64_t loanfree_obj; /* O->K loan resolved on free of O */
int64_t loan_anon; /* A->K loan */
int64_t unloan_anon; /* A->K unloan */
- int64_t loanbreak_anon; /* A->K loan resolved on write */
- int64_t loanfree_anon; /* A->K loan resolved on free */
+ int64_t loanbreak_anon; /* A->K loan resolved on write to A */
+ int64_t loanfree_anon; /* A->K loan resolved on free of A */
+
+ int64_t loan_oa; /* O->A->K loan */
+ int64_t unloan_oa; /* O->A->K unloan */
int64_t loan_zero; /* O->K loan (zero) */
int64_t unloan_zero; /* O->K unloan (zero) */
+
+ int64_t loanbreak_orphaned; /* O->A->K loan turned into A->K loan due to
+ write to O */
+ int64_t loanfree_orphaned; /* O->A->K loan turned into A->K loan due to
+ free of O */
+ int64_t loanbreak_orphaned_anon; /* O->A->K loan turned into O->K loan
+ due to write to A */
+ int64_t loanfree_orphaned_anon; /* O->A->K loan turned into O->K loan
+ due to free of A */
+
+ int64_t loanbreak_oa_obj; /* O->A loan resolved on write to O */
+ int64_t loanfree_oa_obj; /* O->A loan resolved on free of O */
+ int64_t loanbreak_oa_anon; /* O->A loan resolved on write to A */
+ int64_t loanfree_oa_anon; /* O->A loan resolved on free of A */
+ int64_t loan_resolve_orphan; /* O->A loaned page taken over by anon */
+ int64_t loan_obj_read; /* O->A loan for read(2) */
};
#ifdef _KERNEL
Index: src/sys/uvm/uvm_fault.c
diff -u src/sys/uvm/uvm_fault.c:1.190.2.2 src/sys/uvm/uvm_fault.c:1.190.2.3
--- src/sys/uvm/uvm_fault.c:1.190.2.2 Mon Nov 14 14:23:16 2011
+++ src/sys/uvm/uvm_fault.c Mon Dec 26 16:03:10 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_fault.c,v 1.190.2.2 2011/11/14 14:23:16 yamt Exp $ */
+/* $NetBSD: uvm_fault.c,v 1.190.2.3 2011/12/26 16:03:10 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.190.2.2 2011/11/14 14:23:16 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.190.2.3 2011/12/26 16:03:10 yamt Exp $");
#include "opt_uvmhist.h"
@@ -332,9 +332,12 @@ uvmfault_anonget(struct uvm_faultinfo *u
* on the owner of page.
*/
+ uvmfault_unlockall(ufi, NULL, NULL);
if (pg->uobject) {
/* Owner of page is UVM object. */
- uvmfault_unlockall(ufi, amap, NULL);
+ KASSERT(amap->am_obj_lock ==
+ pg->uobject->vmobjlock);
+ mutex_exit(amap->am_lock); /* XXX */
UVMHIST_LOG(maphist, " unlock+wait on uobj",0,
0,0,0);
UVM_UNLOCK_AND_WAIT(pg,
@@ -342,7 +345,9 @@ uvmfault_anonget(struct uvm_faultinfo *u
false, "anonget1", 0);
} else {
/* Owner of page is anon. */
- uvmfault_unlockall(ufi, NULL, NULL);
+ if (amap->am_obj_lock != NULL) {
+ mutex_exit(amap->am_obj_lock); /* XXX */
+ }
UVMHIST_LOG(maphist, " unlock+wait on anon",0,
0,0,0);
UVM_UNLOCK_AND_WAIT(pg, anon->an_lock,
@@ -398,7 +403,9 @@ uvmfault_anonget(struct uvm_faultinfo *u
*/
locked = uvmfault_relock(ufi);
- if (locked || we_own) {
+ if (locked) {
+ amap_lock(amap);
+ } else if (we_own) {
mutex_enter(anon->an_lock);
}
@@ -448,6 +455,10 @@ uvmfault_anonget(struct uvm_faultinfo *u
if (locked) {
uvmfault_unlockall(ufi, NULL, NULL);
+ if (amap->am_obj_lock != NULL) {
+ /* XXX */
+ mutex_exit(amap->am_obj_lock);
+ }
}
mutex_exit(anon->an_lock);
UVMHIST_LOG(maphist, "<- ERROR", 0,0,0,0);
@@ -464,6 +475,10 @@ released:
if (locked) {
uvmfault_unlockall(ufi, NULL, NULL);
+ if (amap->am_obj_lock != NULL) {
+ /* XXX */
+ mutex_exit(amap->am_obj_lock);
+ }
}
uvm_anon_release(anon);
@@ -720,17 +735,17 @@ static inline void uvm_fault_upper_neigh
vaddr_t, struct vm_page *, bool);
static inline int uvm_fault_upper_loan(
struct uvm_faultinfo *, struct uvm_faultctx *,
- struct vm_anon *, struct uvm_object **);
+ struct vm_anon *);
static inline int uvm_fault_upper_promote(
struct uvm_faultinfo *, struct uvm_faultctx *,
- struct uvm_object *, struct vm_anon *);
+ struct vm_anon *);
static inline int uvm_fault_upper_direct(
struct uvm_faultinfo *, struct uvm_faultctx *,
- struct uvm_object *, struct vm_anon *);
+ struct vm_anon *);
static int uvm_fault_upper_enter(
struct uvm_faultinfo *, const struct uvm_faultctx *,
- struct uvm_object *, struct vm_anon *,
- struct vm_page *, struct vm_anon *);
+ struct vm_anon *, struct vm_page *,
+ struct vm_anon *);
static inline void uvm_fault_upper_done(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct vm_anon *, struct vm_page *);
@@ -1033,6 +1048,7 @@ uvm_fault_check(
if (amap) {
amap_lock(amap);
+ KASSERT(uobj == NULL || amap->am_obj_lock == NULL);
amap_lookups(&ufi->entry->aref, eoff, *ranons, flt->npages);
} else {
*ranons = NULL; /* to be safe */
@@ -1282,6 +1298,7 @@ uvm_fault_upper(
/* locked: maps(read), amap, anon, uobj(if one) */
KASSERT(mutex_owned(amap->am_lock));
KASSERT(anon->an_lock == amap->am_lock);
+ KASSERT(uobj == NULL || amap->am_obj_lock == uobj->vmobjlock);
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
/*
@@ -1289,7 +1306,7 @@ uvm_fault_upper(
*/
if (anon->an_page->loan_count) {
- error = uvm_fault_upper_loan(ufi, flt, anon, &uobj);
+ error = uvm_fault_upper_loan(ufi, flt, anon);
if (error != 0)
return error;
}
@@ -1309,9 +1326,9 @@ uvm_fault_upper(
if (flt->cow_now && anon->an_ref > 1) {
flt->promote = true;
- error = uvm_fault_upper_promote(ufi, flt, uobj, anon);
+ error = uvm_fault_upper_promote(ufi, flt, anon);
} else {
- error = uvm_fault_upper_direct(ufi, flt, uobj, anon);
+ error = uvm_fault_upper_direct(ufi, flt, anon);
}
return error;
}
@@ -1326,7 +1343,7 @@ uvm_fault_upper(
static int
uvm_fault_upper_loan(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
- struct vm_anon *anon, struct uvm_object **ruobj)
+ struct vm_anon *anon)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
int error = 0;
@@ -1357,15 +1374,19 @@ uvm_fault_upper_loan(
/* >1 case is already ok */
if (anon->an_ref == 1) {
- error = uvm_loanbreak_anon(anon, *ruobj);
+ struct uvm_object *uobj = anon->an_page->uobject;
+
+ KASSERT(uobj == NULL ||
+ uobj->vmobjlock == amap->am_obj_lock);
+ KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
+ error = uvm_loanbreak_anon(anon);
if (error != 0) {
- uvmfault_unlockall(ufi, amap, *ruobj);
+ uvmfault_unlockall(ufi, amap, NULL);
uvm_wait("flt_noram2");
return ERESTART;
}
/* if we were a loan reciever uobj is gone */
- if (*ruobj)
- *ruobj = NULL;
+ KASSERT(anon->an_page->uobject == NULL);
}
}
return error;
@@ -1383,7 +1404,7 @@ uvm_fault_upper_loan(
static int
uvm_fault_upper_promote(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
- struct uvm_object *uobj, struct vm_anon *anon)
+ struct vm_anon *anon)
{
struct vm_anon * const oanon = anon;
struct vm_page *pg;
@@ -1423,7 +1444,7 @@ uvm_fault_upper_promote(
* oanon != anon, we'll have to unlock anon, too.
*/
- return uvm_fault_upper_enter(ufi, flt, uobj, anon, pg, oanon);
+ return uvm_fault_upper_enter(ufi, flt, anon, pg, oanon);
}
/*
@@ -1433,7 +1454,7 @@ uvm_fault_upper_promote(
static int
uvm_fault_upper_direct(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
- struct uvm_object *uobj, struct vm_anon *anon)
+ struct vm_anon *anon)
{
struct vm_anon * const oanon = anon;
struct vm_page *pg;
@@ -1444,7 +1465,7 @@ uvm_fault_upper_direct(
if (anon->an_ref > 1) /* disallow writes to ref > 1 anons */
flt->enter_prot = flt->enter_prot & ~VM_PROT_WRITE;
- return uvm_fault_upper_enter(ufi, flt, uobj, anon, pg, oanon);
+ return uvm_fault_upper_enter(ufi, flt, anon, pg, oanon);
}
/*
@@ -1454,16 +1475,17 @@ uvm_fault_upper_direct(
static int
uvm_fault_upper_enter(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
- struct uvm_object *uobj, struct vm_anon *anon, struct vm_page *pg,
- struct vm_anon *oanon)
+ struct vm_anon *anon, struct vm_page *pg, struct vm_anon *oanon)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
+ struct uvm_object *uobj __unused = pg->uobject;
UVMHIST_FUNC("uvm_fault_upper_enter"); UVMHIST_CALLED(maphist);
/* locked: maps(read), amap, oanon, anon(if different from oanon) */
KASSERT(mutex_owned(amap->am_lock));
KASSERT(anon->an_lock == amap->am_lock);
KASSERT(oanon->an_lock == amap->am_lock);
+ KASSERT(uobj == NULL || amap->am_obj_lock == uobj->vmobjlock);
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
KASSERT(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN);
@@ -1487,7 +1509,7 @@ uvm_fault_upper_enter(
* as the map may change while we're asleep.
*/
- uvmfault_unlockall(ufi, amap, uobj);
+ uvmfault_unlockall(ufi, amap, NULL);
if (!uvm_reclaimable()) {
UVMHIST_LOG(maphist,
"<- failed. out of VM",0,0,0,0);
@@ -1506,7 +1528,7 @@ uvm_fault_upper_enter(
*/
pmap_update(ufi->orig_map->pmap);
- uvmfault_unlockall(ufi, amap, uobj);
+ uvmfault_unlockall(ufi, amap, NULL);
return 0;
}
Index: src/sys/uvm/uvm_loan.c
diff -u src/sys/uvm/uvm_loan.c:1.81.2.4 src/sys/uvm/uvm_loan.c:1.81.2.5
--- src/sys/uvm/uvm_loan.c:1.81.2.4 Sun Nov 20 10:52:33 2011
+++ src/sys/uvm/uvm_loan.c Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_loan.c,v 1.81.2.4 2011/11/20 10:52:33 yamt Exp $ */
+/* $NetBSD: uvm_loan.c,v 1.81.2.5 2011/12/26 16:03:11 yamt 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.81.2.4 2011/11/20 10:52:33 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.81.2.5 2011/12/26 16:03:11 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -41,6 +41,8 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v
#include <uvm/uvm.h>
+bool doloanobj = true;
+
/*
* "loaned" pages are pages which are (read-only, copy-on-write) loaned
* from the VM system to other parts of the kernel. this allows page
@@ -106,6 +108,8 @@ static int uvm_loanzero(struct uvm_fault
static void uvm_unloananon(struct vm_anon **, int);
static void uvm_unloanpage(struct vm_page **, int);
static int uvm_loanpage(struct vm_page **, int);
+static int uvm_loanobj_read(struct vm_map *, vaddr_t, size_t,
+ struct uvm_object *, off_t);
/*
@@ -328,7 +332,7 @@ fail:
/*
* uvm_loananon: loan a page from an anon out
*
- * => called with map, amap, uobj locked
+ * => called with map, amap, anon locked
* => return value:
* -1 = fatal error, everything is unlocked, abort.
* 0 = lookup in ufi went stale, everything unlocked, relookup and
@@ -427,11 +431,15 @@ uvm_loananon(struct uvm_faultinfo *ufi,
(*output)++;
/* unlock and return success */
- if (pg->uobject)
+ if (pg->uobject != NULL)
mutex_exit(pg->uobject->vmobjlock);
ucpu = uvm_cpu_get();
- ucpu->loan_anon++;
+ if (pg->uobject != NULL) {
+ ucpu->loan_oa++;
+ } else {
+ ucpu->loan_anon++;
+ }
uvm_cpu_put(ucpu);
UVMHIST_LOG(loanhist, "->K done", 0,0,0,0);
@@ -1007,38 +1015,36 @@ uvm_unloanpage(struct vm_page **ploans,
slock = NULL;
}
- /*
- * drop our loan. if page is owned by an anon but
- * PQ_ANON is not set, the page was loaned to the anon
- * from an object which dropped ownership, so resolve
- * this by turning the anon's loan into real ownership
- * (ie. decrement loan_count again and set PQ_ANON).
- * after all this, if there are no loans left, put the
- * page back a paging queue (if the page is owned by
- * an anon) or free it (if the page is now unowned).
- */
-
obj = pg->uobject;
anon = pg->uanon;
+ /*
+ * drop our loan. (->K)
+ */
KASSERT(pg->loan_count > 0);
pg->loan_count--;
- if (obj == NULL && anon != NULL &&
- (pg->pqflags & PQ_ANON) == 0) {
- KASSERT(pg->loan_count > 0);
- pg->loan_count--;
- pg->pqflags |= PQ_ANON;
- }
- if (pg->loan_count == 0 && obj == NULL && anon == NULL) {
- KASSERT((pg->flags & PG_BUSY) == 0);
- uvm_pagefree(pg);
+ /*
+ * if there are no loans left, put the page back a paging queue
+ * (if the page is owned by an anon) or free it (if the page
+ * is now unowned).
+ */
+ uvm_loan_resolve_orphan(pg, true);
+ if (pg->loan_count == 0) {
+ if (obj == NULL && anon == NULL) {
+ KASSERT((pg->flags & PG_BUSY) == 0);
+ uvm_pagefree(pg);
+ }
+ if (anon != NULL) {
+ uvm_pageactivate(pg);
+ }
}
if (slock != NULL) {
mutex_exit(slock);
}
ucpu = uvm_cpu_get();
if (obj != NULL) {
- KASSERT(anon == NULL); /* XXX no O->A loan */
- if (obj == &uvm_loanzero_object) {
+ if (anon != NULL) {
+ ucpu->unloan_oa++;
+ } else if (obj == &uvm_loanzero_object) {
ucpu->unloan_zero++;
} else {
ucpu->unloan_obj++;
@@ -1140,6 +1146,7 @@ uvm_loanbreak(struct vm_page *uobjpage)
#ifdef DIAGNOSTIC
struct uvm_object *uobj = uobjpage->uobject;
#endif
+ struct vm_anon * const anon = uobjpage->uanon;
const unsigned int count = uobjpage->loan_count;
KASSERT(uobj != NULL);
@@ -1170,6 +1177,15 @@ uvm_loanbreak(struct vm_page *uobjpage)
uobjpage->flags &= ~(PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(uobjpage, NULL);
+ mutex_enter(&uvm_pageqlock);
+
+ /*
+ * if the page is no longer referenced by an anon (i.e. we are breaking
+ * O->K loans), then remove it from any pageq's.
+ */
+ if (anon == NULL)
+ uvm_pagedequeue(uobjpage);
+
/*
* replace uobjpage with new page.
*
@@ -1178,20 +1194,8 @@ uvm_loanbreak(struct vm_page *uobjpage)
uvm_pagereplace(uobjpage, pg);
- mutex_enter(&uvm_pageqlock);
- KASSERT(uobjpage->uanon == NULL); /* XXX no O->A loan */
-
/*
- * 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.
- */
- if (uobjpage->uanon == NULL)
- uvm_pagedequeue(uobjpage);
-
- /*
- * at this point we have absolutely no
- * control over uobjpage
+ * at this point we have absolutely no control over uobjpage
*/
/* install new page */
@@ -1199,22 +1203,35 @@ uvm_loanbreak(struct vm_page *uobjpage)
mutex_exit(&uvm_pageqlock);
/*
- * done! loan is broken and "pg" is
- * PG_BUSY. it can now replace uobjpage.
+ * update statistics.
*/
-
ucpu = uvm_cpu_get();
- ucpu->loanbreak_obj += count;
+ if (anon != NULL) {
+ ucpu->loanbreak_oa_obj++;
+ ucpu->loanbreak_orphaned += count - 1;
+ } else {
+ ucpu->loanbreak_obj += count;
+ }
uvm_cpu_put(ucpu);
+
+ /*
+ * done! loan is broken and "pg" is PG_BUSY.
+ * it can now replace uobjpage.
+ */
return pg;
}
+/*
+ * uvm_loanbreak_anon:
+ */
+
int
-uvm_loanbreak_anon(struct vm_anon *anon, struct uvm_object *uobj)
+uvm_loanbreak_anon(struct vm_anon *anon)
{
struct uvm_cpu *ucpu;
struct vm_page *pg;
unsigned int oldstatus;
+ struct uvm_object * const uobj = anon->an_page->uobject;
const unsigned int count = anon->an_page->loan_count;
KASSERT(mutex_owned(anon->an_lock));
@@ -1237,25 +1254,24 @@ uvm_loanbreak_anon(struct vm_anon *anon,
mutex_enter(&uvm_pageqlock); /* KILL loan */
anon->an_page->uanon = NULL;
- /* in case we owned */
- anon->an_page->pqflags &= ~PQ_ANON;
-
- KASSERT(uobj == NULL); /* XXX O->A loan is currently broken */
- if (uobj) {
- /* if we were receiver of loan */
+ if (uobj != NULL) {
+ /*
+ * if we were receiver of loan (O->A)
+ */
+ KASSERT((anon->an_page->pqflags & PQ_ANON) == 0);
anon->an_page->loan_count--;
} else {
/*
* we were the lender (A->K); need to remove the page from
* pageq's.
+ *
+ * PQ_ANON is updated by the caller.
*/
+ KASSERT((anon->an_page->pqflags & PQ_ANON) != 0);
+ anon->an_page->pqflags &= ~PQ_ANON;
uvm_pagedequeue(anon->an_page);
}
- if (uobj) {
- mutex_exit(uobj->vmobjlock);
- }
-
/* install new page in anon */
anon->an_page = pg;
pg->uanon = anon;
@@ -1268,12 +1284,482 @@ uvm_loanbreak_anon(struct vm_anon *anon,
UVM_PAGE_OWN(pg, NULL);
/* done! */
- if (uobj == NULL) {
- ucpu = uvm_cpu_get();
+ ucpu = uvm_cpu_get();
+ if (uobj != NULL) {
+ ucpu->loanbreak_oa_anon++;
+ ucpu->loanbreak_orphaned_anon += count - 1;
+ atomic_inc_uint(&uvmexp.anonpages);
+ } else {
ucpu->loanbreak_anon += count;
ucpu->pagestate[1][oldstatus]--;
- ucpu->pagestate[1][UVM_PAGE_STATUS_DIRTY]++;
+ }
+ ucpu->pagestate[1][UVM_PAGE_STATUS_DIRTY]++;
+ uvm_cpu_put(ucpu);
+ return 0;
+}
+
+int
+uvm_loanobj(struct uvm_object *uobj, struct uio *uio)
+{
+ struct iovec *iov;
+ struct vm_map *map;
+ vaddr_t va;
+ size_t len;
+ int i, error = 0;
+
+ if (!doloanobj) {
+ return ENOSYS;
+ }
+
+ /*
+ * This interface is only for loaning to user space.
+ * Loans to the kernel should be done with the kernel-specific
+ * loaning interfaces.
+ */
+
+ if (VMSPACE_IS_KERNEL_P(uio->uio_vmspace)) {
+ return ENOSYS;
+ }
+
+ if (uio->uio_rw != UIO_READ) {
+ return ENOSYS;
+ }
+
+ /*
+ * Check that the uio is aligned properly for loaning.
+ */
+
+ if (uio->uio_offset & PAGE_MASK || uio->uio_resid & PAGE_MASK) {
+ return EINVAL;
+ }
+ for (i = 0; i < uio->uio_iovcnt; i++) {
+ if (((vaddr_t)uio->uio_iov[i].iov_base & PAGE_MASK) ||
+ (uio->uio_iov[i].iov_len & PAGE_MASK)) {
+ return EINVAL;
+ }
+ }
+
+ /*
+ * Process the uio.
+ */
+
+ map = &uio->uio_vmspace->vm_map;
+ while (uio->uio_resid) {
+ iov = uio->uio_iov;
+ while (iov->iov_len) {
+ va = (vaddr_t)iov->iov_base;
+ len = MIN(iov->iov_len, MAXPHYS);
+ error = uvm_loanobj_read(map, va, len, uobj,
+ uio->uio_offset);
+ if (error) {
+ goto out;
+ }
+ iov->iov_base = (char *)iov->iov_base + len;
+ iov->iov_len -= len;
+ uio->uio_offset += len;
+ uio->uio_resid -= len;
+ }
+ uio->uio_iov++;
+ uio->uio_iovcnt--;
+ }
+
+out:
+ pmap_update(map->pmap);
+ return error;
+}
+
+/*
+ * Loan object pages to a user process.
+ */
+
+/* XXX an arbitrary number larger than MAXPHYS/PAGE_SIZE */
+#define MAXPAGES 16
+
+static int
+uvm_loanobj_read(struct vm_map *map, vaddr_t va, size_t len,
+ struct uvm_object *uobj, off_t off)
+{
+ unsigned int npages = len >> PAGE_SHIFT;
+ struct vm_page *pgs[MAXPAGES];
+ struct vm_amap *amap;
+ struct vm_anon *anon, *oanons[MAXPAGES], *nanons[MAXPAGES];
+ struct vm_map_entry *entry;
+ struct vm_anon *anon_tofree;
+ unsigned int maptime;
+ unsigned int i, refs, aoff, pgoff;
+ unsigned int loaned; /* # of newly created O->A loan */
+ int error;
+ UVMHIST_FUNC("uvm_vnp_loanread"); UVMHIST_CALLED(ubchist);
+
+ UVMHIST_LOG(ubchist, "map %p va 0x%x npages %d", map, va, npages, 0);
+ UVMHIST_LOG(ubchist, "uobj %p off 0x%x", uobj, off, 0, 0);
+
+ if (npages > MAXPAGES) {
+ return EINVAL;
+ }
+retry:
+ vm_map_lock_read(map);
+ if (!uvm_map_lookup_entry(map, va, &entry)) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "no entry", 0,0,0,0);
+ return EINVAL;
+ }
+ if ((entry->protection & VM_PROT_WRITE) == 0) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "no write access", 0,0,0,0);
+ return EACCES;
+ }
+ if (VM_MAPENT_ISWIRED(entry)) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "entry is wired", 0,0,0,0);
+ return EBUSY;
+ }
+ if (!UVM_ET_ISCOPYONWRITE(entry)) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "entry is not COW", 0,0,0,0);
+ return EINVAL;
+ }
+ if (UVM_ET_ISOBJ(entry)) {
+ /*
+ * avoid locking order difficulty between
+ * am_obj_lock and backing object's lock.
+ */
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "entry is obj backed", 0,0,0,0);
+ return EINVAL;
+ }
+ if (entry->end < va + len) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "chunk longer than entry", 0,0,0,0);
+ return EINVAL;
+ }
+ amap = entry->aref.ar_amap;
+ if (amap != NULL && (amap->am_flags & AMAP_SHARED) != 0) {
+ vm_map_unlock_read(map);
+ UVMHIST_LOG(ubchist, "amap is shared", 0,0,0,0);
+ return EINVAL;
+ }
+
+ /*
+ * None of the trivial reasons why we might not be able to do the loan
+ * are true. If we need to COW the amap, try to do it now.
+ */
+
+ KASSERT(amap || UVM_ET_ISNEEDSCOPY(entry));
+ if (UVM_ET_ISNEEDSCOPY(entry)) {
+ maptime = map->timestamp;
+ vm_map_unlock_read(map);
+ vm_map_lock(map);
+ if (maptime + 1 != map->timestamp) {
+ vm_map_unlock(map);
+ goto retry;
+ }
+ amap_copy(map, entry, 0, va, va + len);
+ if (UVM_ET_ISNEEDSCOPY(entry)) {
+ vm_map_unlock(map);
+ UVMHIST_LOG(ubchist, "amap COW failed", 0,0,0,0);
+ return ENOMEM;
+ }
+ UVMHIST_LOG(ubchist, "amap has been COWed", 0,0,0,0);
+ aoff = va - entry->start;
+ maptime = map->timestamp;
+ vm_map_unlock(map);
+ } else {
+ aoff = va - entry->start;
+ maptime = map->timestamp;
+ vm_map_unlock_read(map);
+ }
+
+ /*
+ * The map is all ready for us, now fetch the obj pages.
+ * If the map changes out from under us, start over.
+ *
+ * XXX worth trying PGO_LOCKED?
+ */
+
+ memset(pgs, 0, sizeof(pgs));
+ mutex_enter(uobj->vmobjlock);
+ error = (*uobj->pgops->pgo_get)(uobj, off, pgs, &npages, 0,
+ VM_PROT_READ, 0, PGO_SYNCIO);
+ if (error) {
+ UVMHIST_LOG(ubchist, "getpages -> %d", error,0,0,0);
+ return error;
+ }
+ vm_map_lock_read(map);
+ if (map->timestamp != maptime) {
+ vm_map_unlock_read(map);
+ mutex_enter(uobj->vmobjlock);
+ mutex_enter(&uvm_pageqlock);
+ for (i = 0; i < npages; i++) {
+ uvm_pageactivate(pgs[i]);
+ }
+ uvm_page_unbusy(pgs, npages);
+ mutex_exit(&uvm_pageqlock);
+ mutex_exit(uobj->vmobjlock);
+ goto retry;
+ }
+ amap = entry->aref.ar_amap;
+ KASSERT(amap != NULL);
+
+ /*
+ * Prepare each object page for loaning. Allocate an anon for each page
+ * that doesn't already have one. If any of the pages are wired,
+ * undo everything and fail.
+ */
+
+ memset(nanons, 0, sizeof(nanons));
+ amap_lock(amap);
+ if (amap->am_obj_lock != NULL) {
+ if (amap->am_obj_lock != uobj->vmobjlock) {
+ /*
+ * the amap might already have pages loaned from
+ * another object. give up.
+ *
+ * XXX worth clipping amap?
+ */
+ error = EBUSY;
+ amap_unlock(amap);
+ amap = NULL;
+ mutex_enter(uobj->vmobjlock);
+ goto fail_amap_unlocked;
+ }
+ } else {
+ mutex_enter(uobj->vmobjlock);
+ }
+ KASSERT(mutex_owned(amap->am_lock));
+ KASSERT(mutex_owned(uobj->vmobjlock));
+ loaned = 0;
+ for (i = 0; i < npages; i++) {
+ struct vm_page * const pg = pgs[i];
+ KASSERT(uvm_page_locked_p(pg));
+ if (pg->wire_count) {
+ error = EBUSY;
+ goto fail;
+ }
+ pmap_page_protect(pg, VM_PROT_READ);
+ mutex_enter(&uvm_pageqlock);
+ uvm_pageactivate(pgs[i]);
+ mutex_exit(&uvm_pageqlock);
+ if (pg->uanon != NULL) {
+ KASSERTMSG(pg->loan_count > 0, "pg %p loan_count %u",
+ pg, (unsigned int)pg->loan_count);
+ anon = pg->uanon;
+ if (anon->an_lock != amap->am_lock) {
+ /*
+ * the page is already loaned to another amap
+ * whose lock is incompatible with ours.
+ * give up.
+ */
+ error = EBUSY;
+ goto fail;
+ }
+ anon->an_ref++;
+ } else {
+ anon = uvm_analloc();
+ if (anon == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+ mutex_enter(&uvm_pageqlock);
+ anon->an_page = pg;
+ pg->uanon = anon;
+ pg->loan_count++;
+ mutex_exit(&uvm_pageqlock);
+ loaned++;
+ }
+ nanons[i] = anon;
+ }
+
+ /*
+ * Look for any existing anons in the amap. These will be replaced
+ * by the new loan anons we just set up. If any of these anon pages
+ * are wired then we can't replace them.
+ */
+
+ memset(oanons, 0, sizeof(oanons));
+ for (i = 0; i < npages; i++) {
+ UVMHIST_LOG(ubchist, "pgs[%d] %p", i, pgs[i], 0,0);
+ anon = amap_lookup(&entry->aref, aoff + (i << PAGE_SHIFT));
+ oanons[i] = anon;
+ if (anon && anon->an_page && anon->an_page->wire_count) {
+ error = EBUSY;
+ goto fail;
+ }
+ }
+
+ /*
+ * Everything is good to go. Remove any existing anons and insert
+ * the loaned object anons.
+ */
+
+ anon_tofree = NULL;
+ for (i = 0; i < npages; i++) {
+ pgoff = i << PAGE_SHIFT;
+ anon = oanons[i];
+ if (anon != NULL) {
+ amap_unadd(&entry->aref, aoff + pgoff);
+ refs = --anon->an_ref;
+ if (refs == 0) {
+ anon->an_link = anon_tofree;
+ anon_tofree = anon;
+ }
+ }
+ anon = nanons[i];
+ if (anon->an_lock == NULL) {
+ anon->an_lock = amap->am_lock;
+ }
+ amap_add(&entry->aref, aoff + pgoff, anon, FALSE);
+ }
+
+ /*
+ * The map has all the new information now.
+ * Enter the pages into the pmap to save likely faults later.
+ */
+
+ for (i = 0; i < npages; i++) {
+ error = pmap_enter(map->pmap, va + (i << PAGE_SHIFT),
+ VM_PAGE_TO_PHYS(pgs[i]), VM_PROT_READ, PMAP_CANFAIL);
+ if (error != 0) {
+ /*
+ * while the failure of pmap_enter here is not critical,
+ * we should not leave the mapping of the oanon page.
+ */
+ pmap_remove(map->pmap, va + (i << PAGE_SHIFT),
+ va + (i << PAGE_SHIFT) + PAGE_SIZE);
+ }
+ }
+
+ /*
+ * At this point we're done with the pages, unlock them now.
+ */
+
+ mutex_enter(&uvm_pageqlock);
+ uvm_page_unbusy(pgs, npages);
+ mutex_exit(&uvm_pageqlock);
+ if (amap->am_obj_lock == NULL) {
+ mutex_obj_hold(uobj->vmobjlock);
+ amap->am_obj_lock = uobj->vmobjlock;
+ } else {
+ KASSERT(amap->am_obj_lock == uobj->vmobjlock);
+ }
+ uvm_anon_freelst(amap, anon_tofree);
+ vm_map_unlock_read(map);
+
+ /*
+ * update statistics
+ */
+ if (loaned) {
+ struct uvm_cpu *ucpu;
+
+ ucpu = uvm_cpu_get();
+ ucpu->loan_obj_read += loaned;
uvm_cpu_put(ucpu);
}
return 0;
+
+ /*
+ * We couldn't complete the loan for some reason.
+ * Undo any work we did so far.
+ */
+
+fail:
+ KASSERT(mutex_owned(amap->am_lock));
+fail_amap_unlocked:
+ KASSERT(mutex_owned(uobj->vmobjlock));
+ for (i = 0; i < npages; i++) {
+ anon = nanons[i];
+ if (anon != NULL) {
+ KASSERT(amap != NULL);
+ KASSERT(uvm_page_locked_p(anon->an_page));
+ if (anon->an_lock == NULL) {
+ struct vm_page * const pg = anon->an_page;
+
+ KASSERT(anon->an_ref == 1);
+ KASSERT(pg != NULL);
+ KASSERT(pg->loan_count > 0);
+ KASSERT(pg->uanon == anon);
+ mutex_enter(&uvm_pageqlock);
+ pg->loan_count--;
+ pg->uanon = NULL;
+ anon->an_page = NULL;
+ mutex_exit(&uvm_pageqlock);
+ anon->an_ref--;
+ uvm_anon_free(anon);
+ } else {
+ KASSERT(anon->an_lock == amap->am_lock);
+ KASSERT(anon->an_page->loan_count > 0);
+ KASSERT(anon->an_ref > 1);
+ anon->an_ref--;
+ }
+ } else {
+ mutex_enter(&uvm_pageqlock);
+ uvm_pageenqueue(pgs[i]);
+ mutex_exit(&uvm_pageqlock);
+ }
+ }
+ mutex_enter(&uvm_pageqlock);
+ uvm_page_unbusy(pgs, npages);
+ mutex_exit(&uvm_pageqlock);
+ if (amap != NULL) {
+ if (amap->am_obj_lock == NULL) {
+ mutex_exit(uobj->vmobjlock);
+ }
+ amap_unlock(amap);
+ } else {
+ mutex_exit(uobj->vmobjlock);
+ }
+ vm_map_unlock_read(map);
+ return error;
+}
+
+/*
+ * uvm_loan_resolve_orphan: update the state of the page after a possible
+ * ownership change
+ *
+ * if page is owned by an anon but PQ_ANON is not set, the page was loaned
+ * to the anon from an object which dropped ownership, so resolve this by
+ * turning the anon's loan into real ownership (ie. decrement loan_count
+ * again and set PQ_ANON).
+ */
+
+void
+uvm_loan_resolve_orphan(struct vm_page *pg, bool pageqlocked)
+{
+ struct uvm_object * const uobj = pg->uobject;
+ struct vm_anon * const anon = pg->uanon;
+ struct uvm_cpu *ucpu;
+
+ KASSERT(!pageqlocked || mutex_owned(&uvm_pageqlock));
+ KASSERT(uvm_page_locked_p(pg));
+ if (uobj != NULL) {
+ return;
+ }
+ if (anon == NULL) {
+ return;
+ }
+ if ((pg->pqflags & PQ_ANON) != 0) {
+ return;
+ }
+ KASSERT(pg->loan_count > 0);
+ if (!pageqlocked) {
+ mutex_enter(&uvm_pageqlock);
+ }
+ pg->loan_count--;
+ pg->pqflags |= PQ_ANON;
+ if (!pageqlocked) {
+ mutex_exit(&uvm_pageqlock);
+ }
+
+ /*
+ * adjust statistics after the owner change.
+ *
+ * the pagestate should have been decremented when uobj dropped the
+ * ownership.
+ */
+ uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
+ ucpu = uvm_cpu_get();
+ ucpu->loan_resolve_orphan++;
+ ucpu->pagestate[1][UVM_PAGE_STATUS_DIRTY]++;
+ uvm_cpu_put(ucpu);
+ atomic_inc_uint(&uvmexp.anonpages);
}
Index: src/sys/uvm/uvm_loan.h
diff -u src/sys/uvm/uvm_loan.h:1.17 src/sys/uvm/uvm_loan.h:1.17.4.1
--- src/sys/uvm/uvm_loan.h:1.17 Wed Feb 2 15:13:34 2011
+++ src/sys/uvm/uvm_loan.h Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_loan.h,v 1.17 2011/02/02 15:13:34 chuck Exp $ */
+/* $NetBSD: uvm_loan.h,v 1.17.4.1 2011/12/26 16:03:11 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -49,7 +49,9 @@ void uvm_unloan(void *, int, int);
int uvm_loanuobjpages(struct uvm_object *, voff_t, int,
struct vm_page **);
struct vm_page *uvm_loanbreak(struct vm_page *);
-int uvm_loanbreak_anon(struct vm_anon *, struct uvm_object *);
+int uvm_loanbreak_anon(struct vm_anon *);
+int uvm_loanobj(struct uvm_object *, struct uio *);
+void uvm_loan_resolve_orphan(struct vm_page *, bool);
#endif /* _KERNEL */
Index: src/sys/uvm/uvm_map.c
diff -u src/sys/uvm/uvm_map.c:1.305 src/sys/uvm/uvm_map.c:1.305.2.1
--- src/sys/uvm/uvm_map.c:1.305 Tue Sep 27 01:02:39 2011
+++ src/sys/uvm/uvm_map.c Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_map.c,v 1.305 2011/09/27 01:02:39 jym Exp $ */
+/* $NetBSD: uvm_map.c,v 1.305.2.1 2011/12/26 16:03:11 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.305 2011/09/27 01:02:39 jym Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.305.2.1 2011/12/26 16:03:11 yamt Exp $");
#include "opt_ddb.h"
#include "opt_uvmhist.h"
@@ -4948,11 +4948,13 @@ uvm_mapent_trymerge(struct vm_map *map,
struct uvm_object *uobj;
struct vm_map_entry *next;
struct vm_map_entry *prev;
+ struct vm_amap *amap; /* neighbour's amap */
vsize_t size;
int merged = 0;
bool copying;
int newetype;
+ KASSERT(vm_map_locked_p(map));
if (VM_MAP_USE_KMAPENT(map)) {
return 0;
}
@@ -4969,11 +4971,12 @@ uvm_mapent_trymerge(struct vm_map *map,
newetype = copying ? (entry->etype & ~UVM_ET_NEEDSCOPY) : entry->etype;
next = entry->next;
+ amap = next->aref.ar_amap;
if (next != &map->header &&
next->start == entry->end &&
- ((copying && next->aref.ar_amap != NULL &&
- amap_refs(next->aref.ar_amap) == 1) ||
- (!copying && next->aref.ar_amap == NULL)) &&
+ ((copying && amap != NULL && amap_refs(amap) == 1 &&
+ amap->am_obj_lock == NULL) ||
+ (!copying && amap == NULL)) &&
UVM_ET_ISCOMPATIBLE(next, newetype,
uobj, entry->flags, entry->protection,
entry->max_protection, entry->inheritance, entry->advice,
@@ -5008,10 +5011,11 @@ uvm_mapent_trymerge(struct vm_map *map,
}
prev = entry->prev;
+ amap = prev->aref.ar_amap;
if (prev != &map->header &&
prev->end == entry->start &&
- ((copying && !merged && prev->aref.ar_amap != NULL &&
- amap_refs(prev->aref.ar_amap) == 1) ||
+ ((copying && !merged && amap != NULL && amap_refs(amap) == 1 &&
+ amap->am_obj_lock == NULL) ||
(!copying && prev->aref.ar_amap == NULL)) &&
UVM_ET_ISCOMPATIBLE(prev, newetype,
uobj, entry->flags, entry->protection,
@@ -5186,6 +5190,8 @@ uvm_map_lock_entry(struct vm_map_entry *
{
if (entry->aref.ar_amap != NULL) {
+ KASSERT(entry->aref.ar_amap->am_obj_lock == NULL ||
+ !UVM_ET_ISOBJ(entry));
amap_lock(entry->aref.ar_amap);
}
if (UVM_ET_ISOBJ(entry)) {
Index: src/sys/uvm/uvm_meter.c
diff -u src/sys/uvm/uvm_meter.c:1.56.4.5 src/sys/uvm/uvm_meter.c:1.56.4.6
--- src/sys/uvm/uvm_meter.c:1.56.4.5 Sun Nov 20 10:52:34 2011
+++ src/sys/uvm/uvm_meter.c Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_meter.c,v 1.56.4.5 2011/11/20 10:52:34 yamt Exp $ */
+/* $NetBSD: uvm_meter.c,v 1.56.4.6 2011/12/26 16:03:11 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_meter.c,v 1.56.4.5 2011/11/20 10:52:34 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_meter.c,v 1.56.4.6 2011/12/26 16:03:11 yamt Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -197,6 +197,9 @@ sysctl_vm_uvmexp2(SYSCTLFN_ARGS)
u.loanbreak_obj += ucpu->loanbreak_obj;
u.loanfree_obj += ucpu->loanfree_obj;
+ u.loan_oa += ucpu->loan_oa;
+ u.unloan_oa += ucpu->unloan_oa;
+
u.loan_anon += ucpu->loan_anon;
u.unloan_anon += ucpu->unloan_anon;
u.loanbreak_anon += ucpu->loanbreak_anon;
@@ -204,6 +207,18 @@ sysctl_vm_uvmexp2(SYSCTLFN_ARGS)
u.loan_zero += ucpu->loan_zero;
u.unloan_zero += ucpu->unloan_zero;
+
+ u.loanbreak_orphaned += ucpu->loanbreak_orphaned;
+ u.loanfree_orphaned += ucpu->loanfree_orphaned;
+ u.loanbreak_orphaned_anon += ucpu->loanbreak_orphaned_anon;
+ u.loanfree_orphaned_anon += ucpu->loanfree_orphaned_anon;
+
+ u.loanbreak_oa_obj += ucpu->loanbreak_oa_obj;
+ u.loanfree_oa_obj += ucpu->loanfree_oa_obj;
+ u.loanbreak_oa_anon += ucpu->loanbreak_oa_anon;
+ u.loanfree_oa_anon += ucpu->loanfree_oa_anon;
+ u.loan_resolve_orphan += ucpu->loan_resolve_orphan;
+ u.loan_obj_read += ucpu->loan_obj_read;
}
node = *rnode;
node.sysctl_data = &u;
Index: src/sys/uvm/uvm_page.c
diff -u src/sys/uvm/uvm_page.c:1.178.2.8 src/sys/uvm/uvm_page.c:1.178.2.9
--- src/sys/uvm/uvm_page.c:1.178.2.8 Wed Nov 30 14:33:46 2011
+++ src/sys/uvm/uvm_page.c Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_page.c,v 1.178.2.8 2011/11/30 14:33:46 yamt Exp $ */
+/* $NetBSD: uvm_page.c,v 1.178.2.9 2011/12/26 16:03:11 yamt 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.178.2.8 2011/11/30 14:33:46 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.178.2.9 2011/12/26 16:03:11 yamt Exp $");
#include "opt_ddb.h"
#include "opt_uvmhist.h"
@@ -1537,8 +1537,9 @@ uvm_pagefree(struct vm_page *pg)
*/
if (obj != NULL) {
- uvm_pageremove(obj, pg);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
+ uvm_pageremove(obj, pg);
+ pg->pqflags &= ~(PQ_FILE|PQ_AOBJ);
} else if (pg->uanon != NULL) {
if ((pg->pqflags & PQ_ANON) == 0) {
pg->loan_count--;
@@ -1561,6 +1562,7 @@ uvm_pagefree(struct vm_page *pg)
#ifdef UVM_PAGE_TRKOWN
pg->owner_tag = NULL;
#endif
+ KASSERT((pg->pqflags & PQ_STAT) == 0);
if (pg->loan_count) {
KASSERT(pg->uobject == NULL);
if (pg->uanon == NULL) {
@@ -1568,7 +1570,13 @@ uvm_pagefree(struct vm_page *pg)
}
ucpu = uvm_cpu_get();
if (obj != NULL) {
- ucpu->loanfree_obj += pg->loan_count;
+ if (pg->uanon != NULL) {
+ ucpu->loanfree_oa_obj++;
+ ucpu->loanfree_orphaned +=
+ pg->loan_count - 1;
+ } else {
+ ucpu->loanfree_obj += pg->loan_count;
+ }
} else {
ucpu->loanfree_anon += pg->loan_count;
}
Index: src/sys/uvm/uvm_pdaemon.c
diff -u src/sys/uvm/uvm_pdaemon.c:1.103.2.2 src/sys/uvm/uvm_pdaemon.c:1.103.2.3
--- src/sys/uvm/uvm_pdaemon.c:1.103.2.2 Fri Nov 18 00:57:34 2011
+++ src/sys/uvm/uvm_pdaemon.c Mon Dec 26 16:03:11 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_pdaemon.c,v 1.103.2.2 2011/11/18 00:57:34 yamt Exp $ */
+/* $NetBSD: uvm_pdaemon.c,v 1.103.2.3 2011/12/26 16:03:11 yamt Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.103.2.2 2011/11/18 00:57:34 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.103.2.3 2011/12/26 16:03:11 yamt Exp $");
#include "opt_uvmhist.h"
#include "opt_readahead.h"
@@ -415,7 +415,6 @@ uvm_pageout_done(int npages)
kmutex_t *
uvmpd_trylockowner(struct vm_page *pg)
{
- struct uvm_object *uobj = pg->uobject;
kmutex_t *lock;
KASSERT(mutex_owned(&uvm_pageqlock));
@@ -424,20 +423,7 @@ uvmpd_trylockowner(struct vm_page *pg)
if (!mutex_tryenter(lock)) {
return NULL;
}
- if (uobj == NULL) {
-
- /*
- * set PQ_ANON if it isn't set already.
- */
-
- if ((pg->pqflags & PQ_ANON) == 0) {
- KASSERT(pg->loan_count > 0);
- pg->loan_count--;
- pg->pqflags |= PQ_ANON;
- /* anon now owns it */
- }
- }
-
+ uvm_loan_resolve_orphan(pg, true);
return lock;
}