summaryrefslogtreecommitdiffstats
path: root/sys/mips
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2009-04-20 03:44:54 +0000
committeralc <alc@FreeBSD.org>2009-04-20 03:44:54 +0000
commit53ec7b000ff939e8a5285b9ba3b09c75c87493a7 (patch)
tree25e309a2089071841bbb53606fc2781c6a6cd806 /sys/mips
parentdce30a548c74852941ae18fa0911daf1fc988db1 (diff)
downloadFreeBSD-src-53ec7b000ff939e8a5285b9ba3b09c75c87493a7.zip
FreeBSD-src-53ec7b000ff939e8a5285b9ba3b09c75c87493a7.tar.gz
MFamd64/i386
Introduce pmap_try_insert_pv_entry(), a function that conditionally creates a pv entry if the number of entries is below the high water mark for pv entries. Introduce pmap_enter_quick_locked() and use it to reimplement pmap_enter_object(). The old implementation was broken. For example, it could block while holding a mutex lock. Change pmap_enter_quick_locked() to fail rather than wait if it is unable to allocate a page table page. This prevents a race between pmap_enter_object() and the page daemon. Specifically, an inactive page that is a successor to the page that was given to pmap_enter_quick_locked() might become a cache page while pmap_enter_quick_locked() waits and later pmap_enter_object() maps the cache page violating the invariant that cache pages are never mapped. Similarly, change pmap_enter_quick_locked() to call pmap_try_insert_pv_entry() rather than pmap_insert_entry(). Generally speaking, pmap_enter_quick_locked() is used to create speculative mappings. So, it should not try hard to allocate memory if free memory is scarce. Tested by: gonzo
Diffstat (limited to 'sys/mips')
-rw-r--r--sys/mips/mips/pmap.c109
1 files changed, 70 insertions, 39 deletions
diff --git a/sys/mips/mips/pmap.c b/sys/mips/mips/pmap.c
index de18279..353b181 100644
--- a/sys/mips/mips/pmap.c
+++ b/sys/mips/mips/pmap.c
@@ -171,6 +171,8 @@ static PMAP_INLINE void free_pv_entry(pv_entry_t pv);
static pv_entry_t get_pv_entry(pmap_t locked_pmap);
static __inline void pmap_changebit(vm_page_t m, int bit, boolean_t setem);
+static vm_page_t pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va,
+ vm_page_t m, vm_prot_t prot, vm_page_t mpte);
static int pmap_remove_pte(struct pmap *pmap, pt_entry_t *ptq, vm_offset_t va);
static void pmap_remove_page(struct pmap *pmap, vm_offset_t va);
static void pmap_remove_entry(struct pmap *pmap, vm_page_t m, vm_offset_t va);
@@ -178,6 +180,8 @@ static boolean_t pmap_testbit(vm_page_t m, int bit);
static void
pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t mpte,
vm_page_t m, boolean_t wired);
+static boolean_t pmap_try_insert_pv_entry(pmap_t pmap, vm_page_t mpte,
+ vm_offset_t va, vm_page_t m);
static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags);
@@ -1461,6 +1465,32 @@ pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t mpte, vm_page_t m,
}
/*
+ * Conditionally create a pv entry.
+ */
+static boolean_t
+pmap_try_insert_pv_entry(pmap_t pmap, vm_page_t mpte, vm_offset_t va,
+ vm_page_t m)
+{
+ pv_entry_t pv;
+
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ mtx_assert(&vm_page_queue_mtx, MA_OWNED);
+ if (pv_entry_count < pv_entry_high_water &&
+ (pv = uma_zalloc(pvzone, M_NOWAIT)) != NULL) {
+ pv_entry_count++;
+ pv->pv_va = va;
+ pv->pv_pmap = pmap;
+ pv->pv_ptem = mpte;
+ pv->pv_wired = FALSE;
+ TAILQ_INSERT_TAIL(&pmap->pm_pvlist, pv, pv_plist);
+ TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_list);
+ m->md.pv_list_count++;
+ return (TRUE);
+ } else
+ return (FALSE);
+}
+
+/*
* pmap_remove_pte: do the things to unmap a page in a process
*/
static int
@@ -1919,20 +1949,28 @@ validate:
* but is *MUCH* faster than pmap_enter...
*/
-
void
pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
{
+
+ PMAP_LOCK(pmap);
+ (void)pmap_enter_quick_locked(pmap, va, m, prot, NULL);
+ PMAP_UNLOCK(pmap);
+}
+
+static vm_page_t
+pmap_enter_quick_locked(pmap_t pmap, vm_offset_t va, vm_page_t m,
+ vm_prot_t prot, vm_page_t mpte)
+{
pt_entry_t *pte;
vm_offset_t pa;
- vm_page_t mpte = NULL;
KASSERT(va < kmi.clean_sva || va >= kmi.clean_eva ||
(m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) != 0,
- ("pmap_enter_quick: managed mapping within the clean submap"));
+ ("pmap_enter_quick_locked: managed mapping within the clean submap"));
mtx_assert(&vm_page_queue_mtx, MA_OWNED);
- VM_OBJECT_LOCK_ASSERT(m->object, MA_OWNED);
- PMAP_LOCK(pmap);
+ PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+
/*
* In the case that a page table page is not resident, we are
* creating it here.
@@ -1948,7 +1986,6 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
if (mpte && (mpte->pindex == ptepindex)) {
mpte->wire_count++;
} else {
- retry:
/*
* Get the page directory entry
*/
@@ -1968,19 +2005,10 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
}
mpte->wire_count++;
} else {
- mpte = _pmap_allocpte(pmap, ptepindex, M_NOWAIT);
- if (mpte == NULL) {
- PMAP_UNLOCK(pmap);
- vm_page_busy(m);
- vm_page_unlock_queues();
- VM_OBJECT_UNLOCK(m->object);
- VM_WAIT;
- VM_OBJECT_LOCK(m->object);
- vm_page_lock_queues();
- vm_page_wakeup(m);
- PMAP_LOCK(pmap);
- goto retry;
- }
+ mpte = _pmap_allocpte(pmap, ptepindex,
+ M_NOWAIT);
+ if (mpte == NULL)
+ return (mpte);
}
}
} else {
@@ -1989,18 +2017,24 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
pte = pmap_pte(pmap, va);
if (pmap_pte_v(pte)) {
- if (mpte)
- pmap_unwire_pte_hold(pmap, mpte);
- PMAP_UNLOCK(pmap);
- return;
+ if (mpte != NULL) {
+ mpte->wire_count--;
+ mpte = NULL;
+ }
+ return (mpte);
}
+
/*
- * Enter on the PV list if part of our managed memory. Note that we
- * raise IPL while manipulating pv_table since pmap_enter can be
- * called at interrupt time.
+ * Enter on the PV list if part of our managed memory.
*/
- if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) == 0)
- pmap_insert_entry(pmap, va, mpte, m, FALSE);
+ if ((m->flags & (PG_FICTITIOUS | PG_UNMANAGED)) == 0 &&
+ !pmap_try_insert_pv_entry(pmap, mpte, va, m)) {
+ if (mpte != NULL) {
+ pmap_unwire_pte_hold(pmap, mpte);
+ mpte = NULL;
+ }
+ return (mpte);
+ }
/*
* Increment counters
@@ -2033,9 +2067,7 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot)
mips_dcache_wbinv_range(va, NBPG);
}
}
-
- PMAP_UNLOCK(pmap);
- return;
+ return (mpte);
}
/*
@@ -2114,21 +2146,20 @@ void
pmap_enter_object(pmap_t pmap, vm_offset_t start, vm_offset_t end,
vm_page_t m_start, vm_prot_t prot)
{
- vm_page_t m;
+ vm_page_t m, mpte;
vm_pindex_t diff, psize;
+ VM_OBJECT_LOCK_ASSERT(m_start->object, MA_OWNED);
psize = atop(end - start);
+ mpte = NULL;
m = m_start;
+ PMAP_LOCK(pmap);
while (m != NULL && (diff = m->pindex - m_start->pindex) < psize) {
- /* FIX ME FIX ME - prot is passed in both the
- * the normal spot m, prot but also as the fault_type
- * which we don't use. If we ever use it in pmap_enter
- * we will have to fix this.
- */
- pmap_enter(pmap, start + ptoa(diff), prot, m, prot &
- (VM_PROT_READ | VM_PROT_EXECUTE), FALSE);
+ mpte = pmap_enter_quick_locked(pmap, start + ptoa(diff), m,
+ prot, mpte);
m = TAILQ_NEXT(m, listq);
}
+ PMAP_UNLOCK(pmap);
}
/*
OpenPOWER on IntegriCloud