Port-amd64 archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

reduce size of pv_pte



hi,

the following patch reduces the size of pv_pte, thus pv_entry and vm_page.
comments?

YAMAMOTO Takashi

Index: include/pmap_pv.h
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/include/pmap_pv.h,v
retrieving revision 1.2
diff -u -p -r1.2 pmap_pv.h
--- include/pmap_pv.h   28 Jan 2008 11:06:42 -0000      1.2
+++ include/pmap_pv.h   25 Apr 2011 22:35:38 -0000
@@ -44,9 +44,10 @@ struct vm_page;
  * pv_pte: describe a pte
  */
 
+typedef paddr_t pvkey_t;
+
 struct pv_pte {
-       struct vm_page *pte_ptp;        /* PTP; NULL for pmap_kernel() */
-       vaddr_t pte_va;                 /* VA */
+       pvkey_t pte_key;
 };
 
 /*
Index: x86/pmap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/pmap.c,v
retrieving revision 1.119
diff -u -p -r1.119 pmap.c
--- x86/pmap.c  14 Apr 2011 16:00:21 -0000      1.119
+++ x86/pmap.c  25 Apr 2011 22:35:38 -0000
@@ -438,11 +438,127 @@ struct pv_hash_head {
        SLIST_HEAD(, pv_entry) hh_list;
 } pv_hash_heads[PV_HASH_SIZE];
 
+/*
+ * to save memory, we convert a (ptp, va) tuple to an opaque type, pvkey_t.
+ * pvkey_t is logically a pointer to a pte.
+ */
+
+#define        PVKEY_KERNEL    1
+
+/*
+ * pvkey_decode: decode the (ptp, va) tuple for the given pvkey.
+ */
+
+static void
+pvkey_decode(const pvkey_t key, struct vm_page **ptpp, vaddr_t *vap)
+{
+       struct vm_page *ptp;
+       vaddr_t va;
+
+       if ((key & PVKEY_KERNEL) != 0) {
+               ptp = NULL;
+               va = key & ~PVKEY_KERNEL;
+       } else {
+               vaddr_t l2_frame;
+               vaddr_t l1_mask;
+
+               ptp = PHYS_TO_VM_PAGE(key);
+               l2_frame = ptp->offset / PAGE_SIZE * NBPD_L2;
+               l1_mask = (key & PAGE_MASK) / sizeof(pt_entry_t) * PAGE_SIZE;
+               KASSERT((l2_frame & ~L2_FRAME) == 0);
+               KASSERT((l1_mask & L2_FRAME) == 0);
+               KASSERT((l1_mask & PAGE_MASK) == 0);
+               va = l2_frame + l1_mask;
+       }
+       KASSERT((va & PAGE_MASK) == 0);
+       *vap = va;
+       *ptpp = ptp;
+}
+
+/*
+ * pvkey_encode: generate a pvkey for the given (ptp, va) tuple.
+ */
+
+static pvkey_t
+pvkey_encode(struct vm_page *ptp, vaddr_t va)
+{
+       pvkey_t key;
+
+       KASSERT((va & PAGE_MASK) == 0);
+       if (ptp == NULL) {
+               /*
+                * kernel pmap
+                *
+                * use (va | PVKEY_KERNEL) as a key.
+                */
+               KASSERT(va >= VM_MIN_KERNEL_ADDRESS);
+               CTASSERT(sizeof(va) <= sizeof(pvkey_t));
+               key = va | PVKEY_KERNEL;
+       } else {
+               /*
+                * user pmap
+                *
+                * use the physical address of the pte as a key.
+                */
+               const paddr_t ptppa = VM_PAGE_TO_PHYS(ptp);
+
+               KASSERT(va < VM_MIN_KERNEL_ADDRESS);
+               KASSERT(ptp->offset == ptp_va2o(va, 1));
+               CTASSERT(sizeof(paddr_t) <= sizeof(pvkey_t));
+               key = (pvkey_t)(ptppa + sizeof(pt_entry_t) * pl1_pi(va));
+               KASSERT(key < ptppa + PAGE_SIZE);
+               KASSERT((key & PVKEY_KERNEL) == 0);
+       }
+#if defined(DEBUG)
+       /*
+        * check if the pvkey is decodable to the original tuple.
+        */
+       {
+               struct vm_page *tptp;
+               vaddr_t tva;
+
+               pvkey_decode(key, &tptp, &tva);
+               KDASSERT(tptp == ptp);
+               KDASSERT(tva == va);
+       }
+#endif /* defined(DEBUG) */
+       return key;
+}
+
+/*
+ * pvkey_advance: calculate the pvkey for the next pte.
+ *
+ * basically the faster equivalent of
+ *     pvkey_decode(key, &ptp, &va);
+ *     pvkey_encode(ptp, va + PAGE_SIZE)
+ *
+ * note that pvkey_advance returns a garbage after crossing a ptp boundary.
+ * it's caller's responsibility not to use the garbage.
+ *
+ * XXX this could be micro-optimized to an uncoditional add if we adjust
+ * the pvkey encoding.  is it worth?
+ */
+
+static pvkey_t
+pvkey_advance(const pvkey_t key)
+{
+       pvkey_t nextkey;
+
+       if ((key & PVKEY_KERNEL) != 0) {
+               nextkey = key + PAGE_SIZE;
+       } else {
+               nextkey = key + sizeof(pt_entry_t);
+       }
+       return nextkey;
+}
+
 static u_int
-pvhash_hash(struct vm_page *ptp, vaddr_t va)
+pvhash_hash(const pvkey_t key)
 {
+       const u_int ptppn = key / NBPD_L2;
+       const u_int pfn = key / sizeof(pt_entry_t);
 
-       return (uintptr_t)ptp / sizeof(*ptp) + (va >> PAGE_SHIFT);
+       return ptppn + pfn;
 }
 
 static struct pv_hash_head *
@@ -460,15 +576,14 @@ pvhash_lock(u_int hash)
 }
 
 static struct pv_entry *
-pvhash_remove(struct pv_hash_head *hh, struct vm_page *ptp, vaddr_t va)
+pvhash_remove(struct pv_hash_head *hh, const pvkey_t key)
 {
        struct pv_entry *pve;
        struct pv_entry *prev;
 
        prev = NULL;
        SLIST_FOREACH(pve, &hh->hh_list, pve_hash) {
-               if (pve->pve_pte.pte_ptp == ptp &&
-                   pve->pve_pte.pte_va == va) {
+               if (pve->pve_pte.pte_key == key) {
                        if (prev != NULL) {
                                SLIST_REMOVE_AFTER(prev, pve_hash);
                        } else {
@@ -1779,7 +1894,7 @@ insert_pv(struct pmap_page *pp, struct p
 
        KASSERT(pp_locked(pp));
 
-       hash = pvhash_hash(pve->pve_pte.pte_ptp, pve->pve_pte.pte_va);
+       hash = pvhash_hash(pve->pve_pte.pte_key);
        lock = pvhash_lock(hash);
        hh = pvhash_head(hash);
        mutex_spin_enter(lock);
@@ -1800,20 +1915,23 @@ static struct pv_entry *
 pmap_enter_pv(struct pmap_page *pp,
              struct pv_entry *pve,     /* preallocated pve for us to use */
              struct pv_entry **sparepve,
-             struct vm_page *ptp,
-             vaddr_t va)
+             const pvkey_t key)
 {
+#if defined(DEBUG)
+       struct vm_page *ptp;
+       vaddr_t va;
 
-       KASSERT(ptp == NULL || ptp->wire_count >= 2);
-       KASSERT(ptp == NULL || ptp->uobject != NULL);
-       KASSERT(ptp == NULL || ptp_va2o(va, 1) == ptp->offset);
+       pvkey_decode(key, &ptp, &va);
+       KDASSERT(ptp == NULL || ptp->wire_count >= 2);
+       KDASSERT(ptp == NULL || ptp->uobject != NULL);
+       KDASSERT(ptp == NULL || ptp_va2o(va, 1) == ptp->offset);
+#endif /* defined(DEBUG) */
        KASSERT(pp_locked(pp));
 
        if ((pp->pp_flags & PP_EMBEDDED) == 0) {
                if (LIST_EMPTY(&pp->pp_head.pvh_list)) {
                        pp->pp_flags |= PP_EMBEDDED;
-                       pp->pp_pte.pte_ptp = ptp;
-                       pp->pp_pte.pte_va = va;
+                       pp->pp_pte.pte_key = key;
 
                        return pve;
                }
@@ -1829,8 +1947,7 @@ pmap_enter_pv(struct pmap_page *pp,
                insert_pv(pp, pve2);
        }
 
-       pve->pve_pte.pte_ptp = ptp;
-       pve->pve_pte.pte_va = va;
+       pve->pve_pte.pte_key = key;
        insert_pv(pp, pve);
 
        return NULL;
@@ -1845,20 +1962,24 @@ pmap_enter_pv(struct pmap_page *pp,
  */
 
 static struct pv_entry *
-pmap_remove_pv(struct pmap_page *pp, struct vm_page *ptp, vaddr_t va)
+pmap_remove_pv(struct pmap_page *pp, const pvkey_t key)
 {
        struct pv_hash_head *hh;
        struct pv_entry *pve;
        kmutex_t *lock;
        u_int hash;
+#if defined(DEBUG)
+       struct vm_page *ptp;
+       vaddr_t va;
 
-       KASSERT(ptp == NULL || ptp->uobject != NULL);
-       KASSERT(ptp == NULL || ptp_va2o(va, 1) == ptp->offset);
+       pvkey_decode(key, &ptp, &va);
+       KDASSERT(ptp == NULL || ptp->uobject != NULL);
+       KDASSERT(ptp == NULL || ptp_va2o(va, 1) == ptp->offset);
+#endif /* defined(DEBUG) */
        KASSERT(pp_locked(pp));
 
        if ((pp->pp_flags & PP_EMBEDDED) != 0) {
-               KASSERT(pp->pp_pte.pte_ptp == ptp);
-               KASSERT(pp->pp_pte.pte_va == va);
+               KASSERT(pp->pp_pte.pte_key == key);
 
                pp->pp_flags &= ~PP_EMBEDDED;
                LIST_INIT(&pp->pp_head.pvh_list);
@@ -1866,11 +1987,11 @@ pmap_remove_pv(struct pmap_page *pp, str
                return NULL;
        }
 
-       hash = pvhash_hash(ptp, va);
+       hash = pvhash_hash(key);
        lock = pvhash_lock(hash);
        hh = pvhash_head(hash);
        mutex_spin_enter(lock);
-       pve = pvhash_remove(hh, ptp, va);
+       pve = pvhash_remove(hh, key);
        mutex_spin_exit(lock);
 
        LIST_REMOVE(pve, pve_list);
@@ -3203,7 +3341,6 @@ pmap_unmap_pte(void)
 /*
  * pmap_remove_ptes: remove PTEs from a PTP
  *
- * => must have proper locking on pmap_master_lock
  * => caller must hold pmap's lock
  * => PTP must be mapped into KVA
  * => PTP should be null if pmap == pmap_kernel()
@@ -3218,9 +3355,13 @@ pmap_remove_ptes(struct pmap *pmap, stru
        struct pv_entry *pve;
        pt_entry_t *pte = (pt_entry_t *) ptpva;
        pt_entry_t opte, xpte = 0;
+       pvkey_t key;
 
        KASSERT(pmap == pmap_kernel() || mutex_owned(&pmap->pm_lock));
        KASSERT(kpreempt_disabled());
+       KASSERT((startva & PAGE_MASK) == 0);
+       KASSERT((endva & PAGE_MASK) == 0);
+       KASSERT((startva & L2_FRAME) == ((endva - 1) & L2_FRAME));
 
        /*
         * note that ptpva points to the PTE that maps startva.   this may
@@ -3231,11 +3372,13 @@ pmap_remove_ptes(struct pmap *pmap, stru
         * to keep track of the number of real PTEs in the PTP).
         */
 
-       for (/*null*/; startva < endva && (ptp == NULL || ptp->wire_count > 1)
-                            ; pte++, startva += PAGE_SIZE) {
+       for (key = pvkey_encode(ptp, startva);
+           startva < endva && (ptp == NULL || ptp->wire_count > 1);
+           pte++, startva += PAGE_SIZE, key = pvkey_advance(key)) {
                struct vm_page *pg;
                struct pmap_page *pp;
 
+               KASSERT(pvkey_encode(ptp, startva) == key);
                if (!pmap_valid_entry(*pte))
                        continue;                       /* VA not mapped */
 
@@ -3282,7 +3425,7 @@ pmap_remove_ptes(struct pmap *pmap, stru
                pp = VM_PAGE_TO_PP(pg);
                pp_lock(pp);
                pp->pp_attrs |= opte;
-               pve = pmap_remove_pv(pp, ptp, startva);
+               pve = pmap_remove_pv(pp, key);
                pp_unlock(pp);
 
                if (pve != NULL) {
@@ -3300,7 +3443,6 @@ pmap_remove_ptes(struct pmap *pmap, stru
 /*
  * pmap_remove_pte: remove a single PTE from a PTP
  *
- * => must have proper locking on pmap_master_lock
  * => caller must hold pmap's lock
  * => PTP must be mapped into KVA
  * => PTP should be null if pmap == pmap_kernel()
@@ -3316,6 +3458,7 @@ pmap_remove_pte(struct pmap *pmap, struc
        struct pv_entry *pve;
        struct vm_page *pg;
        struct pmap_page *pp;
+       paddr_t key;
 
        KASSERT(pmap == pmap_kernel() || mutex_owned(&pmap->pm_lock));
        KASSERT(pmap == pmap_kernel() || kpreempt_disabled());
@@ -3364,10 +3507,11 @@ pmap_remove_pte(struct pmap *pmap, struc
 #endif
 
        /* sync R/M bits */
+       key = pvkey_encode(ptp, va);
        pp = VM_PAGE_TO_PP(pg);
        pp_lock(pp);
        pp->pp_attrs |= opte;
-       pve = pmap_remove_pv(pp, ptp, va);
+       pve = pmap_remove_pv(pp, key);
        pp_unlock(pp);
 
        if (pve) { 
@@ -3487,6 +3631,8 @@ pmap_remove(struct pmap *pmap, vaddr_t s
                                panic("pmap_remove: unmanaged PTP "
                                      "detected");
 #endif
+                       KASSERT(ptp ==
+                           pmap_find_ptp(pmap, blkendva - PAGE_SIZE, -1, 1));
                }
                xpte |= pmap_remove_ptes(pmap, ptp,
                    (vaddr_t)&ptes[pl1_i(va)], va, blkendva, &pv_tofree);
@@ -3525,8 +3671,7 @@ pmap_sync_pv(struct pv_pte *pvpte, pt_en
        pt_entry_t npte;
        bool need_shootdown;
 
-       ptp = pvpte->pte_ptp;
-       va = pvpte->pte_va;
+       pvkey_decode(pvpte->pte_key, &ptp, &va);
        KASSERT(ptp == NULL || ptp->uobject != NULL);
        KASSERT(ptp == NULL || ptp_va2o(va, 1) == ptp->offset);
        pmap = ptp_to_pmap(ptp);
@@ -3615,7 +3760,6 @@ pmap_page_remove(struct vm_page *pg)
        struct pmap_page *pp;
        struct pv_pte *pvpte;
        struct pv_entry *killlist = NULL;
-       struct vm_page *ptp;
        pt_entry_t expect;
        lwp_t *l;
        int count;
@@ -3631,6 +3775,7 @@ startover:
                struct pmap *pmap;
                struct pv_entry *pve;
                pt_entry_t opte;
+               struct vm_page *ptp;
                vaddr_t va;
                int error;
 
@@ -3639,7 +3784,7 @@ startover:
                 * otherwise the pmap can disappear behind us.
                 */
 
-               ptp = pvpte->pte_ptp;
+               pvkey_decode(pvpte->pte_key, &ptp, &va);
                pmap = ptp_to_pmap(ptp);
                if (ptp != NULL) {
                        pmap_reference(pmap);
@@ -3659,8 +3804,7 @@ startover:
                }
 
                pp->pp_attrs |= opte;
-               va = pvpte->pte_va;
-               pve = pmap_remove_pv(pp, ptp, va);
+               pve = pmap_remove_pv(pp, pvpte->pte_key);
                pp_unlock(pp);
 
                /* update the PTP reference count.  free if last reference. */
@@ -3986,6 +4130,7 @@ pmap_enter_ma(struct pmap *pmap, vaddr_t
        int error;
        bool wired = (flags & PMAP_WIRED) != 0;
        struct pmap *pmap2;
+       pvkey_t key;
 
        KASSERT(pmap_initialized);
        KASSERT(curlwp->l_md.md_gc_pmap != pmap);
@@ -4124,6 +4272,8 @@ pmap_enter_ma(struct pmap *pmap, vaddr_t
                goto same_pa;
        }
 
+       key = pvkey_encode(ptp, va);
+
        /*
         * if old page is managed, remove pv_entry from its list.
         */
@@ -4140,7 +4290,7 @@ pmap_enter_ma(struct pmap *pmap, vaddr_t
                old_pp = VM_PAGE_TO_PP(pg);
 
                pp_lock(old_pp);
-               old_pve = pmap_remove_pv(old_pp, ptp, va);
+               old_pve = pmap_remove_pv(old_pp, key);
                old_pp->pp_attrs |= opte;
                pp_unlock(old_pp);
        }
@@ -4151,7 +4301,7 @@ pmap_enter_ma(struct pmap *pmap, vaddr_t
 
        if (new_pp) {
                pp_lock(new_pp);
-               new_pve = pmap_enter_pv(new_pp, new_pve, &new_pve2, ptp, va);
+               new_pve = pmap_enter_pv(new_pp, new_pve, &new_pve2, key);
                pp_unlock(new_pp);
        }
 
@@ -4693,6 +4843,7 @@ pmap_update(struct pmap *pmap)
                        ptp->flags |= PG_ZERO;
                        pp = VM_PAGE_TO_PP(ptp);
                        empty_ptps = pp->pp_link;
+                       KASSERT((pp->pp_flags & PP_EMBEDDED) == 0);
                        LIST_INIT(&pp->pp_head.pvh_list);
                        uvm_pagefree(ptp);
                }


Home | Main Index | Thread Index | Old Index