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@
This commit is contained in:
parent
30b490009b
commit
0a752e9843
@ -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 @@ pmap_allocpte(pmap_t pmap, vm_offset_t va)
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 @@ pmap_allocpte(pmap_t pmap, vm_offset_t va)
|
||||
* 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 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_page_t mpte)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user