Port-amd64 archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: reduce size of pv_pte
In the very long run, we will be able to cache a reference to a
vm_physseg on the fault handler stack, then pass (paddr_t, vm_page) to
pmap. I agree that PHYS_TO_VM_PAGE is a bad idea in general.
Masao
On Thu, May 12, 2011 at 6:25 AM, Lars Heidieker <lars%heidieker.de@localhost>
wrote:
> -----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