Port-amd64 archive

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

Re: reduce size of pv_pte



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 04/26/11 00:40, YAMAMOTO Takashi wrote:
> hi,
> 
> the following patch reduces the size of pv_pte, thus pv_entry and vm_page.
> comments?
> 
> YAMAMOTO Takashi
> 

Hi, that's interesting. It is cutting a pv_entry from 40 bytes down to
32 bytes for 64 bit.
I've a concern about the runtime requirements because of the
PHYS_TO_VM_PAGE lookup which might be significant.
Do all amd64 machines have only a few physical memory segments?

Lars

> 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);
>               }
> 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.17 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk3K/tgACgkQcxuYqjT7GRYETACfWbH1nWYgnIJjBc/ucbsbtArN
SzgAn12V24jkJA/qsjxuIxeZgneVx4Ox
=D0Di
-----END PGP SIGNATURE-----


Home | Main Index | Thread Index | Old Index