summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2004-09-29 19:20:40 +0000
committeralc <alc@FreeBSD.org>2004-09-29 19:20:40 +0000
commit118a3c283b4d55527625f7c225bab927cc6e8a5b (patch)
tree84e879c73d19a080f90e5a8d82f490bd13e57a88 /sys
parent3793e610ff87a0e982b946fb512883efc2a352a3 (diff)
downloadFreeBSD-src-118a3c283b4d55527625f7c225bab927cc6e8a5b.zip
FreeBSD-src-118a3c283b4d55527625f7c225bab927cc6e8a5b.tar.gz
Prevent the unexpected deallocation of a page table page while performing
pmap_copy(). This entails additional locking in pmap_copy() and the addition of a "flags" parameter to the page table page allocator for specifying whether it may sleep when memory is unavailable. (Already, pmap_copy() checks the availability of memory, aborting if it is scarce. In theory, another CPU could, however, allocate memory between pmap_copy()'s check and the call to the page table page allocator, causing the current thread to release its locks and sleep. This change makes this scenario impossible.) Reviewed by: tegge@
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/amd64/pmap.c65
-rw-r--r--sys/amd64/include/pmap.h2
-rw-r--r--sys/i386/i386/pmap.c56
-rw-r--r--sys/i386/include/pmap.h2
4 files changed, 84 insertions, 41 deletions
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index 0af74de..6af3729 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -110,6 +110,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/msgbuf.h>
#include <sys/mutex.h>
@@ -212,9 +213,9 @@ static int pmap_remove_entry(struct pmap *pmap, vm_page_t m,
vm_offset_t va, pd_entry_t ptepde);
static void pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m);
-static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va);
+static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags);
-static vm_page_t _pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex);
+static vm_page_t _pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex, int flags);
static int _pmap_unwire_pte_hold(pmap_t pmap, vm_offset_t va, vm_page_t m);
static int pmap_unuse_pt(pmap_t, vm_offset_t, pd_entry_t);
static vm_offset_t pmap_kmem_choose(vm_offset_t addr);
@@ -1101,22 +1102,26 @@ pmap_pinit(pmap)
* race conditions.
*/
static vm_page_t
-_pmap_allocpte(pmap, ptepindex)
- pmap_t pmap;
- vm_pindex_t ptepindex;
+_pmap_allocpte(pmap_t pmap, vm_pindex_t ptepindex, int flags)
{
vm_page_t m, pdppg, pdpg;
+ KASSERT((flags & (M_NOWAIT | M_WAITOK)) == M_NOWAIT ||
+ (flags & (M_NOWAIT | M_WAITOK)) == M_WAITOK,
+ ("_pmap_allocpte: flags is neither M_NOWAIT nor M_WAITOK"));
+
/*
* Allocate a page table page.
*/
if ((m = vm_page_alloc(NULL, ptepindex, VM_ALLOC_NOOBJ |
VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL) {
- PMAP_UNLOCK(pmap);
- vm_page_unlock_queues();
- VM_WAIT;
- vm_page_lock_queues();
- PMAP_LOCK(pmap);
+ if (flags & M_WAITOK) {
+ PMAP_UNLOCK(pmap);
+ vm_page_unlock_queues();
+ VM_WAIT;
+ vm_page_lock_queues();
+ PMAP_LOCK(pmap);
+ }
/*
* Indicate the need to retry. While waiting, the page table
@@ -1156,7 +1161,8 @@ _pmap_allocpte(pmap, ptepindex)
pml4 = &pmap->pm_pml4[pml4index];
if ((*pml4 & PG_V) == 0) {
/* Have to allocate a new pdp, recurse */
- if (_pmap_allocpte(pmap, NUPDE + NUPDPE + pml4index) == NULL) {
+ if (_pmap_allocpte(pmap, NUPDE + NUPDPE + pml4index,
+ flags) == NULL) {
--m->wire_count;
vm_page_free(m);
return (NULL);
@@ -1187,7 +1193,8 @@ _pmap_allocpte(pmap, ptepindex)
pml4 = &pmap->pm_pml4[pml4index];
if ((*pml4 & PG_V) == 0) {
/* Have to allocate a new pd, recurse */
- if (_pmap_allocpte(pmap, NUPDE + pdpindex) == NULL) {
+ if (_pmap_allocpte(pmap, NUPDE + pdpindex,
+ flags) == NULL) {
--m->wire_count;
vm_page_free(m);
return (NULL);
@@ -1199,7 +1206,8 @@ _pmap_allocpte(pmap, ptepindex)
pdp = &pdp[pdpindex & ((1ul << NPDPEPGSHIFT) - 1)];
if ((*pdp & PG_V) == 0) {
/* Have to allocate a new pd, recurse */
- if (_pmap_allocpte(pmap, NUPDE + pdpindex) == NULL) {
+ if (_pmap_allocpte(pmap, NUPDE + pdpindex,
+ flags) == NULL) {
--m->wire_count;
vm_page_free(m);
return (NULL);
@@ -1221,12 +1229,16 @@ _pmap_allocpte(pmap, ptepindex)
}
static vm_page_t
-pmap_allocpte(pmap_t pmap, vm_offset_t va)
+pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags)
{
vm_pindex_t ptepindex;
pd_entry_t *pd;
vm_page_t m;
+ KASSERT((flags & (M_NOWAIT | M_WAITOK)) == M_NOWAIT ||
+ (flags & (M_NOWAIT | M_WAITOK)) == M_WAITOK,
+ ("pmap_allocpte: flags is neither M_NOWAIT nor M_WAITOK"));
+
/*
* Calculate pagetable page index
*/
@@ -1259,8 +1271,8 @@ retry:
* Here if the pte page isn't mapped, or if it has been
* deallocated.
*/
- m = _pmap_allocpte(pmap, ptepindex);
- if (m == NULL)
+ m = _pmap_allocpte(pmap, ptepindex, flags);
+ if (m == NULL && (flags & M_WAITOK))
goto retry;
}
return (m);
@@ -1848,7 +1860,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
* resident, we are creating it here.
*/
if (va < VM_MAXUSER_ADDRESS) {
- mpte = pmap_allocpte(pmap, va);
+ mpte = pmap_allocpte(pmap, va, M_WAITOK);
}
#if 0 && defined(PMAP_DIAGNOSTIC)
else {
@@ -2029,7 +2041,8 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_page_t mpte)
mpte = PHYS_TO_VM_PAGE(*ptepa & PG_FRAME);
mpte->wire_count++;
} else {
- mpte = _pmap_allocpte(pmap, ptepindex);
+ mpte = _pmap_allocpte(pmap, ptepindex,
+ M_WAITOK);
if (mpte == NULL)
goto retry;
}
@@ -2224,7 +2237,13 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
return;
vm_page_lock_queues();
- PMAP_LOCK(dst_pmap);
+ if (dst_pmap < src_pmap) {
+ PMAP_LOCK(dst_pmap);
+ PMAP_LOCK(src_pmap);
+ } else {
+ PMAP_LOCK(src_pmap);
+ PMAP_LOCK(dst_pmap);
+ }
for (addr = src_addr; addr < end_addr; addr = va_next) {
pt_entry_t *src_pte, *dst_pte;
vm_page_t dstmpte, srcmpte;
@@ -2303,9 +2322,12 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
* pte still being around... allocpte can
* block.
*/
- dstmpte = pmap_allocpte(dst_pmap, addr);
+ dstmpte = pmap_allocpte(dst_pmap, addr,
+ M_NOWAIT);
+ if (dstmpte == NULL)
+ break;
dst_pte = pmap_pte(dst_pmap, addr);
- if ((*dst_pte == 0) && (ptetemp = *src_pte)) {
+ if (*dst_pte == 0) {
/*
* Clear the modified and
* accessed (referenced) bits
@@ -2325,6 +2347,7 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
}
}
vm_page_unlock_queues();
+ PMAP_UNLOCK(src_pmap);
PMAP_UNLOCK(dst_pmap);
}
diff --git a/sys/amd64/include/pmap.h b/sys/amd64/include/pmap.h
index 3640e2f..90da462 100644
--- a/sys/amd64/include/pmap.h
+++ b/sys/amd64/include/pmap.h
@@ -240,7 +240,7 @@ extern struct pmap kernel_pmap_store;
mtx_assert(&(pmap)->pm_mtx, (type))
#define PMAP_LOCK_DESTROY(pmap) mtx_destroy(&(pmap)->pm_mtx)
#define PMAP_LOCK_INIT(pmap) mtx_init(&(pmap)->pm_mtx, "pmap", \
- NULL, MTX_DEF)
+ NULL, MTX_DEF | MTX_DUPOK)
#define PMAP_LOCKED(pmap) mtx_owned(&(pmap)->pm_mtx)
#define PMAP_MTX(pmap) (&(pmap)->pm_mtx)
#define PMAP_TRYLOCK(pmap) mtx_trylock(&(pmap)->pm_mtx)
diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c
index f6d52a1..02c2206 100644
--- a/sys/i386/i386/pmap.c
+++ b/sys/i386/i386/pmap.c
@@ -110,6 +110,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/msgbuf.h>
#include <sys/mutex.h>
@@ -256,9 +257,9 @@ static int pmap_remove_entry(struct pmap *pmap, vm_page_t m,
vm_offset_t va);
static void pmap_insert_entry(pmap_t pmap, vm_offset_t va, vm_page_t m);
-static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va);
+static vm_page_t pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags);
-static vm_page_t _pmap_allocpte(pmap_t pmap, unsigned ptepindex);
+static vm_page_t _pmap_allocpte(pmap_t pmap, unsigned ptepindex, int flags);
static int _pmap_unwire_pte_hold(pmap_t pmap, vm_page_t m);
static pt_entry_t *pmap_pte_quick(pmap_t pmap, vm_offset_t va);
static void pmap_pte_release(pt_entry_t *pte);
@@ -1161,23 +1162,27 @@ pmap_pinit(pmap)
* mapped correctly.
*/
static vm_page_t
-_pmap_allocpte(pmap, ptepindex)
- pmap_t pmap;
- unsigned ptepindex;
+_pmap_allocpte(pmap_t pmap, unsigned ptepindex, int flags)
{
vm_paddr_t ptepa;
vm_page_t m;
+ KASSERT((flags & (M_NOWAIT | M_WAITOK)) == M_NOWAIT ||
+ (flags & (M_NOWAIT | M_WAITOK)) == M_WAITOK,
+ ("_pmap_allocpte: flags is neither M_NOWAIT nor M_WAITOK"));
+
/*
* Allocate a page table page.
*/
if ((m = vm_page_alloc(NULL, ptepindex, VM_ALLOC_NOOBJ |
VM_ALLOC_WIRED | VM_ALLOC_ZERO)) == NULL) {
- PMAP_UNLOCK(pmap);
- vm_page_unlock_queues();
- VM_WAIT;
- vm_page_lock_queues();
- PMAP_LOCK(pmap);
+ if (flags & M_WAITOK) {
+ PMAP_UNLOCK(pmap);
+ vm_page_unlock_queues();
+ VM_WAIT;
+ vm_page_lock_queues();
+ PMAP_LOCK(pmap);
+ }
/*
* Indicate the need to retry. While waiting, the page table
@@ -1203,12 +1208,16 @@ _pmap_allocpte(pmap, ptepindex)
}
static vm_page_t
-pmap_allocpte(pmap_t pmap, vm_offset_t va)
+pmap_allocpte(pmap_t pmap, vm_offset_t va, int flags)
{
unsigned ptepindex;
pd_entry_t ptepa;
vm_page_t m;
+ KASSERT((flags & (M_NOWAIT | M_WAITOK)) == M_NOWAIT ||
+ (flags & (M_NOWAIT | M_WAITOK)) == M_WAITOK,
+ ("pmap_allocpte: flags is neither M_NOWAIT nor M_WAITOK"));
+
/*
* Calculate pagetable page index
*/
@@ -1241,8 +1250,8 @@ retry:
* Here if the pte page isn't mapped, or if it has
* been deallocated.
*/
- m = _pmap_allocpte(pmap, ptepindex);
- if (m == NULL)
+ m = _pmap_allocpte(pmap, ptepindex, flags);
+ if (m == NULL && (flags & M_WAITOK))
goto retry;
}
return (m);
@@ -1906,7 +1915,7 @@ pmap_enter(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
* resident, we are creating it here.
*/
if (va < VM_MAXUSER_ADDRESS) {
- mpte = pmap_allocpte(pmap, va);
+ mpte = pmap_allocpte(pmap, va, M_WAITOK);
}
#if 0 && defined(PMAP_DIAGNOSTIC)
else {
@@ -2095,7 +2104,8 @@ retry:
mpte = PHYS_TO_VM_PAGE(ptepa);
mpte->wire_count++;
} else {
- mpte = _pmap_allocpte(pmap, ptepindex);
+ mpte = _pmap_allocpte(pmap, ptepindex,
+ M_WAITOK);
if (mpte == NULL)
goto retry;
}
@@ -2297,7 +2307,13 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
return;
vm_page_lock_queues();
- PMAP_LOCK(dst_pmap);
+ if (dst_pmap < src_pmap) {
+ PMAP_LOCK(dst_pmap);
+ PMAP_LOCK(src_pmap);
+ } else {
+ PMAP_LOCK(src_pmap);
+ PMAP_LOCK(dst_pmap);
+ }
sched_pin();
for (addr = src_addr; addr < end_addr; addr = pdnxt) {
pt_entry_t *src_pte, *dst_pte;
@@ -2353,9 +2369,12 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
* pte still being around... allocpte can
* block.
*/
- dstmpte = pmap_allocpte(dst_pmap, addr);
+ dstmpte = pmap_allocpte(dst_pmap, addr,
+ M_NOWAIT);
+ if (dstmpte == NULL)
+ break;
dst_pte = pmap_pte_quick(dst_pmap, addr);
- if ((*dst_pte == 0) && (ptetemp = *src_pte)) {
+ if (*dst_pte == 0) {
/*
* Clear the modified and
* accessed (referenced) bits
@@ -2376,6 +2395,7 @@ pmap_copy(pmap_t dst_pmap, pmap_t src_pmap, vm_offset_t dst_addr, vm_size_t len,
}
sched_unpin();
vm_page_unlock_queues();
+ PMAP_UNLOCK(src_pmap);
PMAP_UNLOCK(dst_pmap);
}
diff --git a/sys/i386/include/pmap.h b/sys/i386/include/pmap.h
index 826c460..eb7b53b 100644
--- a/sys/i386/include/pmap.h
+++ b/sys/i386/include/pmap.h
@@ -302,7 +302,7 @@ extern struct pmap kernel_pmap_store;
mtx_assert(&(pmap)->pm_mtx, (type))
#define PMAP_LOCK_DESTROY(pmap) mtx_destroy(&(pmap)->pm_mtx)
#define PMAP_LOCK_INIT(pmap) mtx_init(&(pmap)->pm_mtx, "pmap", \
- NULL, MTX_DEF)
+ NULL, MTX_DEF | MTX_DUPOK)
#define PMAP_LOCKED(pmap) mtx_owned(&(pmap)->pm_mtx)
#define PMAP_MTX(pmap) (&(pmap)->pm_mtx)
#define PMAP_TRYLOCK(pmap) mtx_trylock(&(pmap)->pm_mtx)
OpenPOWER on IntegriCloud