Date: Wed, 1 Aug 2012 16:04:13 +0000 (UTC) From: Alan Cox <alc@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r238970 - head/sys/amd64/amd64 Message-ID: <201208011604.q71G4DYZ072074@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: alc Date: Wed Aug 1 16:04:13 2012 New Revision: 238970 URL: http://svn.freebsd.org/changeset/base/238970 Log: Revise pmap_enter()'s handling of mapping updates that change the PTE's PG_M and PG_RW bits but not the physical page frame. First, only perform vm_page_dirty() on a managed vm_page when the PG_M bit is being cleared. If the updated PTE continues to have PG_M set, then there is no requirement to perform vm_page_dirty(). Second, flush the mapping from the TLB when PG_M alone is cleared, not just when PG_M and PG_RW are cleared. Otherwise, a stale TLB entry may stop PG_M from being set again on the next store to the virtual page. However, since the vm_page's dirty field already shows the physical page as being dirty, no actual harm comes from the PG_M bit not being set. Nonetheless, it is potentially confusing to someone expecting to see the PTE change after a store to the virtual page. Modified: head/sys/amd64/amd64/pmap.c Modified: head/sys/amd64/amd64/pmap.c ============================================================================== --- head/sys/amd64/amd64/pmap.c Wed Aug 1 12:24:13 2012 (r238969) +++ head/sys/amd64/amd64/pmap.c Wed Aug 1 16:04:13 2012 (r238970) @@ -3439,7 +3439,6 @@ pmap_enter(pmap_t pmap, vm_offset_t va, pv_entry_t pv; vm_paddr_t opa, pa; vm_page_t mpte, om; - boolean_t invlva; va = trunc_page(va); KASSERT(va <= VM_MAX_KERNEL_ADDRESS, ("pmap_enter: toobig")); @@ -3537,7 +3536,6 @@ retry: newpte |= PG_MANAGED; if ((newpte & PG_RW) != 0) vm_page_aflag_set(m, PGA_WRITEABLE); - om = m; } if (((origpte ^ newpte) & ~(PG_M | PG_A)) == 0) goto unchanged; @@ -3576,30 +3574,40 @@ retry: */ if ((origpte & PG_V) != 0) { validate: - invlva = FALSE; origpte = pte_load_store(pte, newpte); opa = origpte & PG_FRAME; - if ((origpte & PG_A) != 0 && (opa != pa || - ((origpte & PG_NX) == 0 && (newpte & PG_NX) != 0))) - invlva = TRUE; - if ((origpte & (PG_M | PG_RW)) == (PG_M | PG_RW)) { + if (opa != pa) { + if ((origpte & PG_MANAGED) != 0) { + if ((origpte & (PG_M | PG_RW)) == (PG_M | + PG_RW)) + vm_page_dirty(om); + if ((origpte & PG_A) != 0) + vm_page_aflag_set(om, PGA_REFERENCED); + CHANGE_PV_LIST_LOCK_TO_PHYS(&lock, opa); + pmap_pvh_free(&om->md, pmap, va); + if ((om->aflags & PGA_WRITEABLE) != 0 && + TAILQ_EMPTY(&om->md.pv_list) && + ((om->flags & PG_FICTITIOUS) != 0 || + TAILQ_EMPTY(&pa_to_pvh(opa)->pv_list))) + vm_page_aflag_clear(om, PGA_WRITEABLE); + } + } else if ((newpte & PG_M) == 0 && (origpte & (PG_M | + PG_RW)) == (PG_M | PG_RW)) { if ((origpte & PG_MANAGED) != 0) - vm_page_dirty(om); - if ((newpte & PG_RW) == 0) - invlva = TRUE; - } - if (opa != pa && (origpte & PG_MANAGED) != 0) { - if ((origpte & PG_A) != 0) - vm_page_aflag_set(om, PGA_REFERENCED); - CHANGE_PV_LIST_LOCK_TO_PHYS(&lock, opa); - pmap_pvh_free(&om->md, pmap, va); - if ((om->aflags & PGA_WRITEABLE) != 0 && - TAILQ_EMPTY(&om->md.pv_list) && - ((om->flags & PG_FICTITIOUS) != 0 || - TAILQ_EMPTY(&pa_to_pvh(opa)->pv_list))) - vm_page_aflag_clear(om, PGA_WRITEABLE); + vm_page_dirty(m); + + /* + * Although the PTE may still have PG_RW set, TLB + * invalidation may nonetheless be required because + * the PTE no longer has PG_M set. + */ + } else if ((origpte & PG_NX) != 0 || (newpte & PG_NX) == 0) { + /* + * This PTE change does not require TLB invalidation. + */ + goto unchanged; } - if (invlva) + if ((origpte & PG_A) != 0) pmap_invalidate_page(pmap, va); } else pte_store(pte, newpte);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201208011604.q71G4DYZ072074>