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/arm | |
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/arm')
-rw-r--r-- | sys/arm/arm/pmap-v6.c | 95 | ||||
-rw-r--r-- | sys/arm/arm/pmap.c | 51 |
2 files changed, 94 insertions, 52 deletions
diff --git a/sys/arm/arm/pmap-v6.c b/sys/arm/arm/pmap-v6.c index 197a2eb..b2be785 100644 --- a/sys/arm/arm/pmap-v6.c +++ b/sys/arm/arm/pmap-v6.c @@ -3264,53 +3264,76 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) } /* - * 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. + * + * XXX Wired mappings of unmanaged pages cannot be counted by this pmap + * implementation. */ 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) { struct l2_bucket *l2b; struct md_page *pvh; - struct pv_entry *pve; - pd_entry_t *pl1pd, l1pd; + pd_entry_t l1pd; pt_entry_t *ptep, pte; + pv_entry_t pv; + vm_offset_t next_bucket; + vm_paddr_t pa; vm_page_t m; - + rw_wlock(&pvh_global_lock); PMAP_LOCK(pmap); - pl1pd = &pmap->pm_l1->l1_kva[L1_IDX(va)]; - l1pd = *pl1pd; - if ((l1pd & L1_TYPE_MASK) == L1_S_PROTO) { - m = PHYS_TO_VM_PAGE(l1pd & L1_S_FRAME); - KASSERT((m != NULL) && ((m->oflags & VPO_UNMANAGED) == 0), - ("pmap_change_wiring: unmanaged superpage should not " - "be changed")); - KASSERT(pmap != pmap_kernel(), - ("pmap_change_wiring: managed kernel superpage " - "should not exist")); - pvh = pa_to_pvh(l1pd & L1_S_FRAME); - pve = pmap_find_pv(pvh, pmap, trunc_1mpage(va)); - if (!wired != ((pve->pv_flags & PVF_WIRED) == 0)) { - if (!pmap_demote_section(pmap, va)) - panic("pmap_change_wiring: demotion failed"); - } else - goto out; + while (sva < eva) { + next_bucket = L2_NEXT_BUCKET(sva); + l1pd = pmap->pm_l1->l1_kva[L1_IDX(sva)]; + if ((l1pd & L1_TYPE_MASK) == L1_S_PROTO) { + pa = l1pd & L1_S_FRAME; + m = PHYS_TO_VM_PAGE(pa); + KASSERT(m != NULL && (m->oflags & VPO_UNMANAGED) == 0, + ("pmap_unwire: unmanaged 1mpage %p", m)); + pvh = pa_to_pvh(pa); + pv = pmap_find_pv(pvh, pmap, trunc_1mpage(sva)); + if ((pv->pv_flags & PVF_WIRED) == 0) + panic("pmap_unwire: pv %p isn't wired", pv); + + /* + * Are we unwiring the entire large page? If not, + * demote the mapping and fall through. + */ + if (sva + L1_S_SIZE == next_bucket && + eva >= next_bucket) { + pv->pv_flags &= ~PVF_WIRED; + pmap->pm_stats.wired_count -= L2_PTE_NUM_TOTAL; + sva = next_bucket; + continue; + } else if (!pmap_demote_section(pmap, sva)) + panic("pmap_unwire: demotion failed"); + } + if (next_bucket > eva) + next_bucket = eva; + l2b = pmap_get_l2_bucket(pmap, sva); + if (l2b == NULL) { + sva = next_bucket; + continue; + } + for (ptep = &l2b->l2b_kva[l2pte_index(sva)]; sva < next_bucket; + sva += PAGE_SIZE, ptep++) { + if ((pte = *ptep) == 0 || + (m = PHYS_TO_VM_PAGE(l2pte_pa(pte))) == NULL || + (m->oflags & VPO_UNMANAGED) != 0) + continue; + pv = pmap_find_pv(&m->md, pmap, sva); + if ((pv->pv_flags & PVF_WIRED) == 0) + panic("pmap_unwire: pv %p isn't wired", pv); + pv->pv_flags &= ~PVF_WIRED; + pmap->pm_stats.wired_count--; + } } - l2b = pmap_get_l2_bucket(pmap, va); - KASSERT(l2b, ("No l2b bucket in pmap_change_wiring")); - ptep = &l2b->l2b_kva[l2pte_index(va)]; - pte = *ptep; - m = PHYS_TO_VM_PAGE(l2pte_pa(pte)); - if (m != NULL) - pmap_modify_pv(m, pmap, va, PVF_WIRED, - wired == TRUE ? PVF_WIRED : 0); -out: rw_wunlock(&pvh_global_lock); - PMAP_UNLOCK(pmap); + PMAP_UNLOCK(pmap); } diff --git a/sys/arm/arm/pmap.c b/sys/arm/arm/pmap.c index 802d5ee..f4e44d7 100644 --- a/sys/arm/arm/pmap.c +++ b/sys/arm/arm/pmap.c @@ -3542,28 +3542,47 @@ pmap_enter_quick(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot) } /* - * 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. + * + * XXX Wired mappings of unmanaged pages cannot be counted by this pmap + * implementation. */ 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) { struct l2_bucket *l2b; pt_entry_t *ptep, pte; - vm_page_t pg; - + pv_entry_t pv; + vm_offset_t next_bucket; + vm_page_t m; + rw_wlock(&pvh_global_lock); - PMAP_LOCK(pmap); - l2b = pmap_get_l2_bucket(pmap, va); - KASSERT(l2b, ("No l2b bucket in pmap_change_wiring")); - ptep = &l2b->l2b_kva[l2pte_index(va)]; - pte = *ptep; - pg = PHYS_TO_VM_PAGE(l2pte_pa(pte)); - if (pg) - pmap_modify_pv(pg, pmap, va, PVF_WIRED, wired ? PVF_WIRED : 0); + PMAP_LOCK(pmap); + while (sva < eva) { + next_bucket = L2_NEXT_BUCKET(sva); + if (next_bucket > eva) + next_bucket = eva; + l2b = pmap_get_l2_bucket(pmap, sva); + if (l2b == NULL) { + sva = next_bucket; + continue; + } + for (ptep = &l2b->l2b_kva[l2pte_index(sva)]; sva < next_bucket; + sva += PAGE_SIZE, ptep++) { + if ((pte = *ptep) == 0 || + (m = PHYS_TO_VM_PAGE(l2pte_pa(pte))) == NULL || + (m->oflags & VPO_UNMANAGED) != 0) + continue; + pv = pmap_find_pv(m, pmap, sva); + if ((pv->pv_flags & PVF_WIRED) == 0) + panic("pmap_unwire: pv %p isn't wired", pv); + pv->pv_flags &= ~PVF_WIRED; + pmap->pm_stats.wired_count--; + } + } rw_wunlock(&pvh_global_lock); PMAP_UNLOCK(pmap); } |