Author: alc
Date: Wed Sep 18 07:01:01 2019
New Revision: 352469
URL: https://svnweb.freebsd.org/changeset/base/352469

Log:
  MFC r350347
    Implement pmap_advise().  (Without a working pmap_advise() implementation
    madvise(MADV_DONTNEED) and madvise(MADV_FREE) are NOPs.)

Modified:
  stable/12/sys/arm64/arm64/pmap.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/arm64/arm64/pmap.c
==============================================================================
--- stable/12/sys/arm64/arm64/pmap.c    Wed Sep 18 06:50:29 2019        
(r352468)
+++ stable/12/sys/arm64/arm64/pmap.c    Wed Sep 18 07:01:01 2019        
(r352469)
@@ -2493,7 +2493,7 @@ pmap_remove_l2(pmap_t pmap, pt_entry_t *l2, vm_offset_
 /*
  * pmap_remove_l3: do the things to unmap a page in a process
  */
-static int __unused
+static int
 pmap_remove_l3(pmap_t pmap, pt_entry_t *l3, vm_offset_t va,
     pd_entry_t l2e, struct spglist *free, struct rwlock **lockp)
 {
@@ -4833,6 +4833,110 @@ out:
 void
 pmap_advise(pmap_t pmap, vm_offset_t sva, vm_offset_t eva, int advice)
 {
+       struct rwlock *lock;
+       vm_offset_t va, va_next;
+       vm_page_t m;
+       pd_entry_t *l0, *l1, *l2, oldl2;
+       pt_entry_t *l3, oldl3;
+
+       if (advice != MADV_DONTNEED && advice != MADV_FREE)
+               return;
+
+       PMAP_LOCK(pmap);
+       for (; sva < eva; sva = va_next) {
+               l0 = pmap_l0(pmap, sva);
+               if (pmap_load(l0) == 0) {
+                       va_next = (sva + L0_SIZE) & ~L0_OFFSET;
+                       if (va_next < sva)
+                               va_next = eva;
+                       continue;
+               }
+               l1 = pmap_l0_to_l1(l0, sva);
+               if (pmap_load(l1) == 0) {
+                       va_next = (sva + L1_SIZE) & ~L1_OFFSET;
+                       if (va_next < sva)
+                               va_next = eva;
+                       continue;
+               }
+               va_next = (sva + L2_SIZE) & ~L2_OFFSET;
+               if (va_next < sva)
+                       va_next = eva;
+               l2 = pmap_l1_to_l2(l1, sva);
+               oldl2 = pmap_load(l2);
+               if (oldl2 == 0)
+                       continue;
+               if ((oldl2 & ATTR_DESCR_MASK) == L2_BLOCK) {
+                       if ((oldl2 & ATTR_SW_MANAGED) == 0)
+                               continue;
+                       lock = NULL;
+                       if (!pmap_demote_l2_locked(pmap, l2, sva, &lock)) {
+                               if (lock != NULL)
+                                       rw_wunlock(lock);
+
+                               /*
+                                * The 2MB page mapping was destroyed.
+                                */
+                               continue;
+                       }
+
+                       /*
+                        * Unless the page mappings are wired, remove the
+                        * mapping to a single page so that a subsequent
+                        * access may repromote.  Since the underlying page
+                        * table page is fully populated, this removal never
+                        * frees a page table page.
+                        */
+                       if ((oldl2 & ATTR_SW_WIRED) == 0) {
+                               l3 = pmap_l2_to_l3(l2, sva);
+                               KASSERT(pmap_load(l3) != 0,
+                                   ("pmap_advise: invalid PTE"));
+                               pmap_remove_l3(pmap, l3, sva, pmap_load(l2),
+                                   NULL, &lock);
+                       }
+                       if (lock != NULL)
+                               rw_wunlock(lock);
+               }
+               KASSERT((pmap_load(l2) & ATTR_DESCR_MASK) == L2_TABLE,
+                   ("pmap_advise: invalid L2 entry after demotion"));
+               if (va_next > eva)
+                       va_next = eva;
+               va = va_next;
+               for (l3 = pmap_l2_to_l3(l2, sva); sva != va_next; l3++,
+                   sva += L3_SIZE) {
+                       oldl3 = pmap_load(l3);
+                       if ((oldl3 & (ATTR_SW_MANAGED | ATTR_DESCR_MASK)) !=
+                           (ATTR_SW_MANAGED | L3_PAGE))
+                               goto maybe_invlrng;
+                       else if (pmap_pte_dirty(oldl3)) {
+                               if (advice == MADV_DONTNEED) {
+                                       /*
+                                        * Future calls to pmap_is_modified()
+                                        * can be avoided by making the page
+                                        * dirty now.
+                                        */
+                                       m = PHYS_TO_VM_PAGE(oldl3 & ~ATTR_MASK);
+                                       vm_page_dirty(m);
+                               }
+                               while (!atomic_fcmpset_long(l3, &oldl3,
+                                   (oldl3 & ~ATTR_AF) | ATTR_AP(ATTR_AP_RO)))
+                                       cpu_spinwait();
+                       } else if ((oldl3 & ATTR_AF) != 0)
+                               pmap_clear_bits(l3, ATTR_AF);
+                       else
+                               goto maybe_invlrng;
+                       if (va == va_next)
+                               va = sva;
+                       continue;
+maybe_invlrng:
+                       if (va != va_next) {
+                               pmap_invalidate_range(pmap, va, sva);
+                               va = va_next;
+                       }
+               }
+               if (va != va_next)
+                       pmap_invalidate_range(pmap, va, sva);
+       }
+       PMAP_UNLOCK(pmap);
 }
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to