summaryrefslogtreecommitdiffstats
path: root/sys/arm
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-09-01 07:58:15 +0000
committerkib <kib@FreeBSD.org>2014-09-01 07:58:15 +0000
commit798eea16149d6a39c6fb5f721410f61b5bb1134a (patch)
tree8a38ed27916582f1759cfcc70e76cd5fabbebce1 /sys/arm
parent14d8fe45061d6304d3e6438cfe4267aa7e17c705 (diff)
downloadFreeBSD-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.c95
-rw-r--r--sys/arm/arm/pmap.c51
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);
}
OpenPOWER on IntegriCloud