diff options
author | alc <alc@FreeBSD.org> | 2004-09-29 19:20:40 +0000 |
---|---|---|
committer | alc <alc@FreeBSD.org> | 2004-09-29 19:20:40 +0000 |
commit | 118a3c283b4d55527625f7c225bab927cc6e8a5b (patch) | |
tree | 84e879c73d19a080f90e5a8d82f490bd13e57a88 /sys | |
parent | 3793e610ff87a0e982b946fb512883efc2a352a3 (diff) | |
download | FreeBSD-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.c | 65 | ||||
-rw-r--r-- | sys/amd64/include/pmap.h | 2 | ||||
-rw-r--r-- | sys/i386/i386/pmap.c | 56 | ||||
-rw-r--r-- | sys/i386/include/pmap.h | 2 |
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) |