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