diff options
author | kib <kib@FreeBSD.org> | 2014-09-01 07:58:15 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2014-09-01 07:58:15 +0000 |
commit | 798eea16149d6a39c6fb5f721410f61b5bb1134a (patch) | |
tree | 8a38ed27916582f1759cfcc70e76cd5fabbebce1 /sys/i386 | |
parent | 14d8fe45061d6304d3e6438cfe4267aa7e17c705 (diff) | |
download | FreeBSD-src-798eea16149d6a39c6fb5f721410f61b5bb1134a.zip FreeBSD-src-798eea16149d6a39c6fb5f721410f61b5bb1134a.tar.gz |
Fix a leak of the wired pages when unwiring of the PROT_NONE-mapped
wired region. Rework the handling of unwire to do the it in batch,
both at pmap and object level.
All commits below are by alc.
MFC r268327:
Introduce pmap_unwire().
MFC r268591:
Implement pmap_unwire() for powerpc.
MFC r268776:
Implement pmap_unwire() for arm.
MFC r268806:
pmap_unwire(9) man page.
MFC r269134:
When unwiring a region of an address space, do not assume that the
underlying physical pages are mapped by the pmap. This fixes a leak
of the wired pages on the unwiring of the region mapped with no access
allowed.
MFC r269339:
In the implementation of the new function pmap_unwire(), the call to
MOEA64_PVO_TO_PTE() must be performed before any changes are made to the
PVO. Otherwise, MOEA64_PVO_TO_PTE() will panic.
MFC r269365:
Correct a long-standing problem in moea{,64}_pvo_enter() that was revealed
by the combination of r268591 and r269134: When we attempt to add the
wired attribute to an existing mapping, moea{,64}_pvo_enter() do nothing.
(They only set the wired attribute on newly created mappings.)
MFC r269433:
Handle wiring failures in vm_map_wire() with the new functions
pmap_unwire() and vm_object_unwire().
Retire vm_fault_{un,}wire(), since they are no longer used.
MFC r269438:
Rewrite a loop in vm_map_wire() so that gcc doesn't think that the variable
"rv" is uninitialized.
MFC r269485:
Retire pmap_change_wiring().
Reviewed by: alc
Diffstat (limited to 'sys/i386')
-rw-r--r-- | sys/i386/i386/pmap.c | 117 | ||||
-rw-r--r-- | sys/i386/xen/pmap.c | 62 |
2 files changed, 119 insertions, 60 deletions
diff --git a/sys/i386/i386/pmap.c b/sys/i386/i386/pmap.c index 0336295..d5c2cbe 100644 --- a/sys/i386/i386/pmap.c +++ b/sys/i386/i386/pmap.c @@ -3968,59 +3968,100 @@ pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, } /* - * Routine: pmap_change_wiring - * Function: Change the wiring attribute for a map/virtual-address - * pair. - * In/out conditions: - * The mapping must already exist in the pmap. + * Clear the wired attribute from the mappings for the specified range of + * addresses in the given pmap. Every valid mapping within that range + * must have the wired attribute set. In contrast, invalid mappings + * cannot have the wired attribute set, so they are ignored. + * + * The wired attribute of the page table entry is not a hardware feature, + * so there is no need to invalidate any TLB entries. */ void -pmap_change_wiring(pmap_t pmap, vm_offset_t va, boolean_t wired) +pmap_unwire(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { + vm_offset_t pdnxt; pd_entry_t *pde; pt_entry_t *pte; - boolean_t are_queues_locked; + boolean_t pv_lists_locked; - are_queues_locked = FALSE; -retry: + if (pmap_is_current(pmap)) + pv_lists_locked = FALSE; + else { + pv_lists_locked = TRUE; +resume: + rw_wlock(&pvh_global_lock); + sched_pin(); + } PMAP_LOCK(pmap); - pde = pmap_pde(pmap, va); - if ((*pde & PG_PS) != 0) { - if (!wired != ((*pde & PG_W) == 0)) { - if (!are_queues_locked) { - are_queues_locked = TRUE; - if (!rw_try_wlock(&pvh_global_lock)) { - PMAP_UNLOCK(pmap); - rw_wlock(&pvh_global_lock); - goto retry; + for (; sva < eva; sva = pdnxt) { + pdnxt = (sva + NBPDR) & ~PDRMASK; + if (pdnxt < sva) + pdnxt = eva; + pde = pmap_pde(pmap, sva); + if ((*pde & PG_V) == 0) + continue; + if ((*pde & PG_PS) != 0) { + if ((*pde & PG_W) == 0) + panic("pmap_unwire: pde %#jx is missing PG_W", + (uintmax_t)*pde); + + /* + * Are we unwiring the entire large page? If not, + * demote the mapping and fall through. + */ + if (sva + NBPDR == pdnxt && eva >= pdnxt) { + /* + * Regardless of whether a pde (or pte) is 32 + * or 64 bits in size, PG_W is among the least + * significant 32 bits. + */ + atomic_clear_int((u_int *)pde, PG_W); + pmap->pm_stats.wired_count -= NBPDR / + PAGE_SIZE; + continue; + } else { + if (!pv_lists_locked) { + pv_lists_locked = TRUE; + if (!rw_try_wlock(&pvh_global_lock)) { + PMAP_UNLOCK(pmap); + /* Repeat sva. */ + goto resume; + } + sched_pin(); } + if (!pmap_demote_pde(pmap, pde, sva)) + panic("pmap_unwire: demotion failed"); } - if (!pmap_demote_pde(pmap, pde, va)) - panic("pmap_change_wiring: demotion failed"); - } else - goto out; - } - pte = pmap_pte(pmap, va); - - if (wired && !pmap_pte_w(pte)) - pmap->pm_stats.wired_count++; - else if (!wired && pmap_pte_w(pte)) - pmap->pm_stats.wired_count--; + } + if (pdnxt > eva) + pdnxt = eva; + for (pte = pmap_pte_quick(pmap, sva); sva != pdnxt; pte++, + sva += PAGE_SIZE) { + if ((*pte & PG_V) == 0) + continue; + if ((*pte & PG_W) == 0) + panic("pmap_unwire: pte %#jx is missing PG_W", + (uintmax_t)*pte); - /* - * Wiring is not a hardware characteristic so there is no need to - * invalidate TLB. - */ - pmap_pte_set_w(pte, wired); - pmap_pte_release(pte); -out: - if (are_queues_locked) + /* + * PG_W must be cleared atomically. Although the pmap + * lock synchronizes access to PG_W, another processor + * could be setting PG_M and/or PG_A concurrently. + * + * PG_W is among the least significant 32 bits. + */ + atomic_clear_int((u_int *)pte, PG_W); + pmap->pm_stats.wired_count--; + } + } + if (pv_lists_locked) { + sched_unpin(); rw_wunlock(&pvh_global_lock); + } PMAP_UNLOCK(pmap); } - /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len diff --git a/sys/i386/xen/pmap.c b/sys/i386/xen/pmap.c index 1ed2c03..48139a1 100644 --- a/sys/i386/xen/pmap.c +++ b/sys/i386/xen/pmap.c @@ -3169,40 +3169,58 @@ pmap_object_init_pt(pmap_t pmap, vm_offset_t addr, vm_object_t object, } /* - * Routine: pmap_change_wiring - * Function: Change the wiring attribute for a map/virtual-address - * pair. - * In/out conditions: - * The mapping must already exist in the pmap. + * Clear the wired attribute from the mappings for the specified range of + * addresses in the given pmap. Every valid mapping within that range + * must have the wired attribute set. In contrast, invalid mappings + * cannot have the wired attribute set, so they are ignored. + * + * The wired attribute of the page table entry is not a hardware feature, + * so there is no need to invalidate any TLB entries. */ void -pmap_change_wiring(pmap_t pmap, vm_offset_t va, boolean_t wired) +pmap_unwire(pmap_t pmap, vm_offset_t sva, vm_offset_t eva) { + vm_offset_t pdnxt; + pd_entry_t *pde; pt_entry_t *pte; + CTR3(KTR_PMAP, "pmap_unwire: pmap=%p sva=0x%x eva=0x%x", pmap, sva, + eva); rw_wlock(&pvh_global_lock); + sched_pin(); PMAP_LOCK(pmap); - pte = pmap_pte(pmap, va); - - if (wired && !pmap_pte_w(pte)) { - PT_SET_VA_MA((pte), *(pte) | PG_W, TRUE); - pmap->pm_stats.wired_count++; - } else if (!wired && pmap_pte_w(pte)) { - PT_SET_VA_MA((pte), *(pte) & ~PG_W, TRUE); - pmap->pm_stats.wired_count--; + for (; sva < eva; sva = pdnxt) { + pdnxt = (sva + NBPDR) & ~PDRMASK; + if (pdnxt < sva) + pdnxt = eva; + pde = pmap_pde(pmap, sva); + if ((*pde & PG_V) == 0) + continue; + if ((*pde & PG_PS) != 0) + panic("pmap_unwire: unexpected PG_PS in pde %#jx", + (uintmax_t)*pde); + if (pdnxt > eva) + pdnxt = eva; + for (pte = pmap_pte_quick(pmap, sva); sva != pdnxt; pte++, + sva += PAGE_SIZE) { + if ((*pte & PG_V) == 0) + continue; + if ((*pte & PG_W) == 0) + panic("pmap_unwire: pte %#jx is missing PG_W", + (uintmax_t)*pte); + PT_SET_VA_MA(pte, *pte & ~PG_W, FALSE); + pmap->pm_stats.wired_count--; + } } - - /* - * Wiring is not a hardware characteristic so there is no need to - * invalidate TLB. - */ - pmap_pte_release(pte); - PMAP_UNLOCK(pmap); + if (*PMAP1) + PT_CLEAR_VA(PMAP1, FALSE); + PT_UPDATES_FLUSH(); + sched_unpin(); rw_wunlock(&pvh_global_lock); + PMAP_UNLOCK(pmap); } - /* * Copy the range specified by src_addr/len * from the source map to the range dst_addr/len |