Commit 81e0a1a6 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] resurrect CONFIG_HIGHPTE

Bill Irwin's patch to fix up pte's in highmem.

With CONFIG_HIGHPTE, the direct pte pointer in struct page becomes the
64-bit physical address of the single pte which is mapping this page.

If the page is not PageDirect then page->pte.chain points at a list of
pte_chains, which each now contain an array of 64-bit physical
addresses of the pte's which are mapping the page.

The functions rmap_ptep_map() and rmap_ptep_unmap() are used for
mapping and unmapping the page which backs the target pte.

The patch touches all architectures (adding do-nothing compatibility
macros and inlines).  It generally mangles lots of header files and may
break non-ia32 compiles.  I've had it in testing since 2.5.31.
parent 9dc8af80
...@@ -210,6 +210,10 @@ if [ "$CONFIG_HIGHMEM64G" = "y" ]; then ...@@ -210,6 +210,10 @@ if [ "$CONFIG_HIGHMEM64G" = "y" ]; then
define_bool CONFIG_X86_PAE y define_bool CONFIG_X86_PAE y
fi fi
if [ "$CONFIG_HIGHMEM4G" = "y" -o "$CONFIG_HIGHMEM64G" = "y" ]; then
bool 'Allocate 3rd-level pagetables from highmem' CONFIG_HIGHPTE
fi
bool 'Math emulation' CONFIG_MATH_EMULATION bool 'Math emulation' CONFIG_MATH_EMULATION
bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
......
...@@ -370,4 +370,6 @@ extern void paging_init(void); ...@@ -370,4 +370,6 @@ extern void paging_init(void);
/* We have our own get_unmapped_area to cope with ADDR_LIMIT_32BIT. */ /* We have our own get_unmapped_area to cope with ADDR_LIMIT_32BIT. */
#define HAVE_ARCH_UNMAPPED_AREA #define HAVE_ARCH_UNMAPPED_AREA
typedef pte_t *pte_addr_t;
#endif /* _ALPHA_PGTABLE_H */ #endif /* _ALPHA_PGTABLE_H */
...@@ -161,6 +161,8 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; ...@@ -161,6 +161,8 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
#define io_remap_page_range(vma,from,phys,size,prot) \ #define io_remap_page_range(vma,from,phys,size,prot) \
remap_page_range(vma,from,phys,size,prot) remap_page_range(vma,from,phys,size,prot)
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#endif /* _ASMARM_PGTABLE_H */ #endif /* _ASMARM_PGTABLE_H */
...@@ -515,4 +515,6 @@ static inline void update_mmu_cache(struct vm_area_struct * vma, ...@@ -515,4 +515,6 @@ static inline void update_mmu_cache(struct vm_area_struct * vma,
*/ */
#define pgtable_cache_init() do { } while (0) #define pgtable_cache_init() do { } while (0)
typedef pte_t *pte_addr_t;
#endif /* _CRIS_PGTABLE_H */ #endif /* _CRIS_PGTABLE_H */
...@@ -13,6 +13,16 @@ ...@@ -13,6 +13,16 @@
* - page->index has the high bits of the address * - page->index has the high bits of the address
* - the lower bits of the address are calculated from the * - the lower bits of the address are calculated from the
* offset of the page table entry within the page table page * offset of the page table entry within the page table page
*
* For CONFIG_HIGHPTE, we need to represent the address of a pte in a
* scalar pte_addr_t. The pfn of the pte's page is shifted left by PAGE_SIZE
* bits and is then ORed with the byte offset of the pte within its page.
*
* For CONFIG_HIGHMEM4G, the pte_addr_t is 32 bits. 20 for the pfn, 12 for
* the offset.
*
* For CONFIG_HIGHMEM64G, the pte_addr_t is 64 bits. 52 for the pfn, 12 for
* the offset.
*/ */
#include <linux/mm.h> #include <linux/mm.h>
...@@ -39,16 +49,42 @@ static inline void pgtable_remove_rmap(struct page * page) ...@@ -39,16 +49,42 @@ static inline void pgtable_remove_rmap(struct page * page)
static inline struct mm_struct * ptep_to_mm(pte_t * ptep) static inline struct mm_struct * ptep_to_mm(pte_t * ptep)
{ {
struct page * page = virt_to_page(ptep); struct page * page = kmap_atomic_to_page(ptep);
return (struct mm_struct *) page->mapping; return (struct mm_struct *) page->mapping;
} }
static inline unsigned long ptep_to_address(pte_t * ptep) static inline unsigned long ptep_to_address(pte_t * ptep)
{ {
struct page * page = virt_to_page(ptep); struct page * page = kmap_atomic_to_page(ptep);
unsigned long low_bits; unsigned long low_bits;
low_bits = ((unsigned long)ptep & ~PAGE_MASK) * PTRS_PER_PTE; low_bits = ((unsigned long)ptep & ~PAGE_MASK) * PTRS_PER_PTE;
return page->index + low_bits; return page->index + low_bits;
} }
#if CONFIG_HIGHPTE
static inline pte_addr_t ptep_to_paddr(pte_t *ptep)
{
pte_addr_t paddr;
paddr = ((pte_addr_t)page_to_pfn(kmap_atomic_to_page(ptep))) << PAGE_SHIFT;
return paddr + (pte_addr_t)((unsigned long)ptep & ~PAGE_MASK);
}
#else
static inline pte_addr_t ptep_to_paddr(pte_t *ptep)
{
return (pte_addr_t)ptep;
}
#endif
#ifndef CONFIG_HIGHPTE
static inline pte_t *rmap_ptep_map(pte_addr_t pte_paddr)
{
return (pte_t *)pte_paddr;
}
static inline void rmap_ptep_unmap(pte_t *pte)
{
return;
}
#endif
#endif /* _GENERIC_RMAP_H */ #endif /* _GENERIC_RMAP_H */
...@@ -103,6 +103,7 @@ extern void __set_fixmap (enum fixed_addresses idx, ...@@ -103,6 +103,7 @@ extern void __set_fixmap (enum fixed_addresses idx,
#define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE) #define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE)
#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))
#define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)
extern void __this_fixmap_does_not_exist(void); extern void __this_fixmap_does_not_exist(void);
...@@ -128,4 +129,10 @@ static inline unsigned long fix_to_virt(const unsigned int idx) ...@@ -128,4 +129,10 @@ static inline unsigned long fix_to_virt(const unsigned int idx)
return __fix_to_virt(idx); return __fix_to_virt(idx);
} }
static inline unsigned long virt_to_fix(const unsigned long vaddr)
{
BUG_ON(vaddr >= FIXADDR_TOP || vaddr < FIXADDR_START);
return __virt_to_fix(vaddr);
}
#endif #endif
...@@ -122,6 +122,19 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type) ...@@ -122,6 +122,19 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
dec_preempt_count(); dec_preempt_count();
} }
static inline struct page *kmap_atomic_to_page(void *ptr)
{
unsigned long idx, vaddr = (unsigned long)ptr;
pte_t *pte;
if (vaddr < FIXADDR_START)
return virt_to_page(ptr);
idx = virt_to_fix(vaddr);
pte = kmap_pte - (idx - FIX_KMAP_BEGIN);
return pte_page(*pte);
}
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _ASM_HIGHMEM_H */ #endif /* _ASM_HIGHMEM_H */
...@@ -19,9 +19,10 @@ D(5) KM_BIO_SRC_IRQ, ...@@ -19,9 +19,10 @@ D(5) KM_BIO_SRC_IRQ,
D(6) KM_BIO_DST_IRQ, D(6) KM_BIO_DST_IRQ,
D(7) KM_PTE0, D(7) KM_PTE0,
D(8) KM_PTE1, D(8) KM_PTE1,
D(9) KM_IRQ0, D(9) KM_PTE2,
D(10) KM_IRQ1, D(10) KM_IRQ0,
D(11) KM_TYPE_NR D(11) KM_IRQ1,
D(12) KM_TYPE_NR
}; };
#undef D #undef D
......
...@@ -266,6 +266,18 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) ...@@ -266,6 +266,18 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0) #define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0)
#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1) #define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1)
#if defined(CONFIG_HIGHPTE) && defined(CONFIG_HIGHMEM4G)
typedef u32 pte_addr_t;
#endif
#if defined(CONFIG_HIGHPTE) && defined(CONFIG_HIGHMEM64G)
typedef u64 pte_addr_t;
#endif
#if !defined(CONFIG_HIGHPTE)
typedef pte_t *pte_addr_t;
#endif
/* /*
* The i386 doesn't have any external MMU info: the kernel page * The i386 doesn't have any external MMU info: the kernel page
* tables contain all the necessary information. * tables contain all the necessary information.
......
...@@ -4,4 +4,18 @@ ...@@ -4,4 +4,18 @@
/* nothing to see, move along */ /* nothing to see, move along */
#include <asm-generic/rmap.h> #include <asm-generic/rmap.h>
#ifdef CONFIG_HIGHPTE
static inline pte_t *rmap_ptep_map(pte_addr_t pte_paddr)
{
unsigned long pfn = (unsigned long)(pte_paddr >> PAGE_SHIFT);
unsigned long off = ((unsigned long)pte_paddr) & ~PAGE_MASK;
return (pte_t *)((char *)kmap_atomic(pfn_to_page(pfn), KM_PTE2) + off);
}
static inline void rmap_ptep_unmap(pte_t *pte)
{
kunmap_atomic(pte, KM_PTE2);
}
#endif
#endif #endif
...@@ -420,6 +420,8 @@ extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)]; ...@@ -420,6 +420,8 @@ extern unsigned long empty_zero_page[PAGE_SIZE/sizeof(unsigned long)];
/* We provide our own get_unmapped_area to cope with VA holes for userland */ /* We provide our own get_unmapped_area to cope with VA holes for userland */
#define HAVE_ARCH_UNMAPPED_AREA #define HAVE_ARCH_UNMAPPED_AREA
typedef pte_t *pte_addr_t;
# endif /* !__ASSEMBLY__ */ # endif /* !__ASSEMBLY__ */
/* /*
......
...@@ -171,6 +171,9 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, ...@@ -171,6 +171,9 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma,
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <asm-generic/pgtable.h> #include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
/* /*
......
...@@ -771,6 +771,8 @@ extern inline void set_context(unsigned long val) ...@@ -771,6 +771,8 @@ extern inline void set_context(unsigned long val)
#include <asm-generic/pgtable.h> #include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !defined (_LANGUAGE_ASSEMBLY) */ #endif /* !defined (_LANGUAGE_ASSEMBLY) */
#define io_remap_page_range remap_page_range #define io_remap_page_range remap_page_range
......
...@@ -811,6 +811,8 @@ extern inline void set_context(unsigned long val) ...@@ -811,6 +811,8 @@ extern inline void set_context(unsigned long val)
#include <asm-generic/pgtable.h> #include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !defined (_LANGUAGE_ASSEMBLY) */ #endif /* !defined (_LANGUAGE_ASSEMBLY) */
/* /*
......
...@@ -326,6 +326,8 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma, ...@@ -326,6 +326,8 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma,
#include <asm-generic/pgtable.h> #include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define io_remap_page_range remap_page_range #define io_remap_page_range remap_page_range
......
...@@ -543,6 +543,8 @@ extern void kernel_set_cachemode (unsigned long address, unsigned long size, ...@@ -543,6 +543,8 @@ extern void kernel_set_cachemode (unsigned long address, unsigned long size,
*/ */
#define pgtable_cache_init() do { } while (0) #define pgtable_cache_init() do { } while (0)
#endif /* __ASSEMBLY__ */ typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#endif /* _PPC_PGTABLE_H */ #endif /* _PPC_PGTABLE_H */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
...@@ -377,5 +377,7 @@ extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); ...@@ -377,5 +377,7 @@ extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t);
extern void hpte_init_pSeries(void); extern void hpte_init_pSeries(void);
extern void hpte_init_iSeries(void); extern void hpte_init_iSeries(void);
typedef pte_t *pte_addr_t;
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* _PPC64_PGTABLE_H */ #endif /* _PPC64_PGTABLE_H */
...@@ -505,6 +505,8 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) ...@@ -505,6 +505,8 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1) #define kern_addr_valid(addr) (1)
......
...@@ -531,6 +531,8 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset) ...@@ -531,6 +531,8 @@ extern inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1) #define kern_addr_valid(addr) (1)
......
...@@ -310,6 +310,8 @@ extern void update_mmu_cache(struct vm_area_struct * vma, ...@@ -310,6 +310,8 @@ extern void update_mmu_cache(struct vm_area_struct * vma,
#define pte_same(A,B) (pte_val(A) == pte_val(B)) #define pte_same(A,B) (pte_val(A) == pte_val(B))
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1) #define kern_addr_valid(addr) (1)
......
...@@ -442,6 +442,8 @@ extern int io_remap_page_range(struct vm_area_struct *vma, unsigned long from, u ...@@ -442,6 +442,8 @@ extern int io_remap_page_range(struct vm_area_struct *vma, unsigned long from, u
#include <asm-generic/pgtable.h> #include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !(__ASSEMBLY__) */ #endif /* !(__ASSEMBLY__) */
/* We provide our own get_unmapped_area to cope with VA holes for userland */ /* We provide our own get_unmapped_area to cope with VA holes for userland */
......
...@@ -369,6 +369,8 @@ extern unsigned long get_fb_unmapped_area(struct file *filp, unsigned long, unsi ...@@ -369,6 +369,8 @@ extern unsigned long get_fb_unmapped_area(struct file *filp, unsigned long, unsi
extern void check_pgt_cache(void); extern void check_pgt_cache(void);
typedef pte_t *pte_addr_t;
#endif /* !(__ASSEMBLY__) */ #endif /* !(__ASSEMBLY__) */
#endif /* !(_SPARC64_PGTABLE_H) */ #endif /* !(_SPARC64_PGTABLE_H) */
...@@ -336,6 +336,8 @@ extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot) ...@@ -336,6 +336,8 @@ extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1) #define kern_addr_valid(addr) (1)
......
...@@ -26,6 +26,7 @@ static inline void *kmap(struct page *page) { return page_address(page); } ...@@ -26,6 +26,7 @@ static inline void *kmap(struct page *page) { return page_address(page); }
#define kmap_atomic(page, idx) page_address(page) #define kmap_atomic(page, idx) page_address(page)
#define kunmap_atomic(addr, idx) do { } while (0) #define kunmap_atomic(addr, idx) do { } while (0)
#define kmap_atomic_to_page(ptr) virt_to_page(ptr)
#endif /* CONFIG_HIGHMEM */ #endif /* CONFIG_HIGHMEM */
......
...@@ -159,9 +159,9 @@ struct page { ...@@ -159,9 +159,9 @@ struct page {
struct list_head lru; /* Pageout list, eg. active_list; struct list_head lru; /* Pageout list, eg. active_list;
protected by zone->lru_lock !! */ protected by zone->lru_lock !! */
union { union {
struct pte_chain * chain; /* Reverse pte mapping pointer. struct pte_chain *chain;/* Reverse pte mapping pointer.
* protected by PG_chainlock */ * protected by PG_chainlock */
pte_t * direct; pte_addr_t direct;
} pte; } pte;
unsigned long private; /* mapping-private opaque data */ unsigned long private; /* mapping-private opaque data */
...@@ -321,6 +321,16 @@ static inline void set_page_zone(struct page *page, unsigned long zone_num) ...@@ -321,6 +321,16 @@ static inline void set_page_zone(struct page *page, unsigned long zone_num)
#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */ #endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
/*
* Return true if this page is mapped into pagetables. Subtle: test pte.direct
* rather than pte.chain. Because sometimes pte.direct is 64-bit, and .chain
* is only 32-bit.
*/
static inline int page_mapped(struct page *page)
{
return page->pte.direct != 0;
}
/* /*
* Error return values for the *_nopage functions * Error return values for the *_nopage functions
*/ */
......
...@@ -93,7 +93,7 @@ void __free_pages_ok (struct page *page, unsigned int order) ...@@ -93,7 +93,7 @@ void __free_pages_ok (struct page *page, unsigned int order)
BUG_ON(PageLocked(page)); BUG_ON(PageLocked(page));
BUG_ON(PageActive(page)); BUG_ON(PageActive(page));
BUG_ON(PageWriteback(page)); BUG_ON(PageWriteback(page));
BUG_ON(page->pte.chain != NULL); BUG_ON(page->pte.direct != 0);
if (PageDirty(page)) if (PageDirty(page))
ClearPageDirty(page); ClearPageDirty(page);
BUG_ON(page_count(page) != 0); BUG_ON(page_count(page) != 0);
...@@ -184,7 +184,7 @@ static inline void prep_new_page(struct page *page) ...@@ -184,7 +184,7 @@ static inline void prep_new_page(struct page *page)
BUG_ON(PageActive(page)); BUG_ON(PageActive(page));
BUG_ON(PageDirty(page)); BUG_ON(PageDirty(page));
BUG_ON(PageWriteback(page)); BUG_ON(PageWriteback(page));
BUG_ON(page->pte.chain != NULL); BUG_ON(page->pte.direct != 0);
page->flags &= ~(1 << PG_uptodate | 1 << PG_error | page->flags &= ~(1 << PG_uptodate | 1 << PG_error |
1 << PG_referenced | 1 << PG_arch_1 | 1 << PG_referenced | 1 << PG_arch_1 |
1 << PG_checked); 1 << PG_checked);
......
...@@ -43,11 +43,11 @@ ...@@ -43,11 +43,11 @@
* We use an array of pte pointers in this structure to minimise cache misses * We use an array of pte pointers in this structure to minimise cache misses
* while traversing reverse maps. * while traversing reverse maps.
*/ */
#define NRPTE (L1_CACHE_BYTES/sizeof(void *) - 1) #define NRPTE ((L1_CACHE_BYTES - sizeof(void *))/sizeof(pte_addr_t))
struct pte_chain { struct pte_chain {
struct pte_chain *next; struct pte_chain *next;
pte_t *ptes[NRPTE]; pte_addr_t ptes[NRPTE];
}; };
static kmem_cache_t *pte_chain_cache; static kmem_cache_t *pte_chain_cache;
...@@ -126,8 +126,10 @@ int page_referenced(struct page * page) ...@@ -126,8 +126,10 @@ int page_referenced(struct page * page)
referenced++; referenced++;
if (PageDirect(page)) { if (PageDirect(page)) {
if (ptep_test_and_clear_young(page->pte.direct)) pte_t *pte = rmap_ptep_map(page->pte.direct);
if (ptep_test_and_clear_young(pte))
referenced++; referenced++;
rmap_ptep_unmap(pte);
} else { } else {
int nr_chains = 0; int nr_chains = 0;
...@@ -136,11 +138,15 @@ int page_referenced(struct page * page) ...@@ -136,11 +138,15 @@ int page_referenced(struct page * page)
int i; int i;
for (i = NRPTE-1; i >= 0; i--) { for (i = NRPTE-1; i >= 0; i--) {
pte_t *p = pc->ptes[i]; pte_addr_t pte_paddr = pc->ptes[i];
if (!p) pte_t *p;
if (!pte_paddr)
break; break;
p = rmap_ptep_map(pte_paddr);
if (ptep_test_and_clear_young(p)) if (ptep_test_and_clear_young(p))
referenced++; referenced++;
rmap_ptep_unmap(p);
nr_chains++; nr_chains++;
} }
} }
...@@ -150,7 +156,6 @@ int page_referenced(struct page * page) ...@@ -150,7 +156,6 @@ int page_referenced(struct page * page)
SetPageDirect(page); SetPageDirect(page);
pc->ptes[NRPTE-1] = 0; pc->ptes[NRPTE-1] = 0;
pte_chain_free(pc); pte_chain_free(pc);
dec_page_state(nr_reverse_maps);
} }
} }
return referenced; return referenced;
...@@ -166,8 +171,8 @@ int page_referenced(struct page * page) ...@@ -166,8 +171,8 @@ int page_referenced(struct page * page)
*/ */
void page_add_rmap(struct page * page, pte_t * ptep) void page_add_rmap(struct page * page, pte_t * ptep)
{ {
struct pte_chain * pte_chain; pte_addr_t pte_paddr = ptep_to_paddr(ptep);
unsigned long pfn = pte_pfn(*ptep); struct pte_chain *pte_chain;
int i; int i;
#ifdef DEBUG_RMAP #ifdef DEBUG_RMAP
...@@ -179,23 +184,26 @@ void page_add_rmap(struct page * page, pte_t * ptep) ...@@ -179,23 +184,26 @@ void page_add_rmap(struct page * page, pte_t * ptep)
BUG(); BUG();
#endif #endif
if (!pfn_valid(pfn) || PageReserved(page)) if (!pfn_valid(page_to_pfn(page)) || PageReserved(page))
return; return;
pte_chain_lock(page); pte_chain_lock(page);
#ifdef DEBUG_RMAP #ifdef DEBUG_RMAP
/*
* This stuff needs help to get up to highmem speed.
*/
{ {
struct pte_chain * pc; struct pte_chain * pc;
if (PageDirect(page)) { if (PageDirect(page)) {
if (page->pte.direct == ptep) if (page->pte.direct == pte_paddr)
BUG(); BUG();
} else { } else {
for (pc = page->pte.chain; pc; pc = pc->next) { for (pc = page->pte.chain; pc; pc = pc->next) {
for (i = 0; i < NRPTE; i++) { for (i = 0; i < NRPTE; i++) {
pte_t *p = pc->ptes[i]; pte_addr_t p = pc->ptes[i];
if (p && p == ptep) if (p && p == pte_paddr)
BUG(); BUG();
} }
} }
...@@ -203,19 +211,19 @@ void page_add_rmap(struct page * page, pte_t * ptep) ...@@ -203,19 +211,19 @@ void page_add_rmap(struct page * page, pte_t * ptep)
} }
#endif #endif
if (page->pte.chain == NULL) { if (page->pte.direct == 0) {
page->pte.direct = ptep; page->pte.direct = pte_paddr;
SetPageDirect(page); SetPageDirect(page);
goto out; goto out;
} }
if (PageDirect(page)) { if (PageDirect(page)) {
/* Convert a direct pointer into a pte_chain */ /* Convert a direct pointer into a pte_chain */
ClearPageDirect(page); ClearPageDirect(page);
pte_chain = pte_chain_alloc(); pte_chain = pte_chain_alloc();
pte_chain->ptes[NRPTE-1] = page->pte.direct; pte_chain->ptes[NRPTE-1] = page->pte.direct;
pte_chain->ptes[NRPTE-2] = ptep; pte_chain->ptes[NRPTE-2] = pte_paddr;
mod_page_state(nr_reverse_maps, 2); page->pte.direct = 0;
page->pte.chain = pte_chain; page->pte.chain = pte_chain;
goto out; goto out;
} }
...@@ -227,23 +235,22 @@ void page_add_rmap(struct page * page, pte_t * ptep) ...@@ -227,23 +235,22 @@ void page_add_rmap(struct page * page, pte_t * ptep)
new = pte_chain_alloc(); new = pte_chain_alloc();
new->next = pte_chain; new->next = pte_chain;
page->pte.chain = new; page->pte.chain = new;
new->ptes[NRPTE-1] = ptep; new->ptes[NRPTE-1] = pte_paddr;
inc_page_state(nr_reverse_maps);
goto out; goto out;
} }
BUG_ON(pte_chain->ptes[NRPTE-1] == NULL); BUG_ON(!pte_chain->ptes[NRPTE-1]);
for (i = NRPTE-2; i >= 0; i--) { for (i = NRPTE-2; i >= 0; i--) {
if (pte_chain->ptes[i] == NULL) { if (!pte_chain->ptes[i]) {
pte_chain->ptes[i] = ptep; pte_chain->ptes[i] = pte_paddr;
inc_page_state(nr_reverse_maps);
goto out; goto out;
} }
} }
BUG(); BUG();
out: out:
pte_chain_unlock(page); pte_chain_unlock(page);
inc_page_state(nr_reverse_maps);
return; return;
} }
...@@ -259,19 +266,22 @@ void page_add_rmap(struct page * page, pte_t * ptep) ...@@ -259,19 +266,22 @@ void page_add_rmap(struct page * page, pte_t * ptep)
*/ */
void page_remove_rmap(struct page * page, pte_t * ptep) void page_remove_rmap(struct page * page, pte_t * ptep)
{ {
pte_addr_t pte_paddr = ptep_to_paddr(ptep);
struct pte_chain *pc; struct pte_chain *pc;
unsigned long pfn = page_to_pfn(page);
if (!page || !ptep) if (!page || !ptep)
BUG(); BUG();
if (!pfn_valid(pfn) || PageReserved(page)) if (!pfn_valid(page_to_pfn(page)) || PageReserved(page))
return; return;
pte_chain_lock(page); pte_chain_lock(page);
BUG_ON(page->pte.direct == 0);
if (PageDirect(page)) { if (PageDirect(page)) {
if (page->pte.direct == ptep) { if (page->pte.direct == pte_paddr) {
page->pte.direct = NULL; page->pte.direct = 0;
dec_page_state(nr_reverse_maps);
ClearPageDirect(page); ClearPageDirect(page);
goto out; goto out;
} }
...@@ -285,17 +295,17 @@ void page_remove_rmap(struct page * page, pte_t * ptep) ...@@ -285,17 +295,17 @@ void page_remove_rmap(struct page * page, pte_t * ptep)
if (pc->next) if (pc->next)
prefetch(pc->next); prefetch(pc->next);
for (i = 0; i < NRPTE; i++) { for (i = 0; i < NRPTE; i++) {
pte_t *p = pc->ptes[i]; pte_addr_t pa = pc->ptes[i];
if (!p) if (!pa)
continue; continue;
if (victim_i == -1) if (victim_i == -1)
victim_i = i; victim_i = i;
if (p != ptep) if (pa != pte_paddr)
continue; continue;
pc->ptes[i] = start->ptes[victim_i]; pc->ptes[i] = start->ptes[victim_i];
start->ptes[victim_i] = NULL;
dec_page_state(nr_reverse_maps); dec_page_state(nr_reverse_maps);
start->ptes[victim_i] = 0;
if (victim_i == NRPTE-1) { if (victim_i == NRPTE-1) {
/* Emptied a pte_chain */ /* Emptied a pte_chain */
page->pte.chain = start->next; page->pte.chain = start->next;
...@@ -343,9 +353,10 @@ void page_remove_rmap(struct page * page, pte_t * ptep) ...@@ -343,9 +353,10 @@ void page_remove_rmap(struct page * page, pte_t * ptep)
* pte_chain_lock page_launder() * pte_chain_lock page_launder()
* mm->page_table_lock try_to_unmap_one(), trylock * mm->page_table_lock try_to_unmap_one(), trylock
*/ */
static int FASTCALL(try_to_unmap_one(struct page *, pte_t *)); static int FASTCALL(try_to_unmap_one(struct page *, pte_addr_t));
static int try_to_unmap_one(struct page * page, pte_t * ptep) static int try_to_unmap_one(struct page * page, pte_addr_t paddr)
{ {
pte_t *ptep = rmap_ptep_map(paddr);
unsigned long address = ptep_to_address(ptep); unsigned long address = ptep_to_address(ptep);
struct mm_struct * mm = ptep_to_mm(ptep); struct mm_struct * mm = ptep_to_mm(ptep);
struct vm_area_struct * vma; struct vm_area_struct * vma;
...@@ -359,8 +370,11 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep) ...@@ -359,8 +370,11 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep)
* We need the page_table_lock to protect us from page faults, * We need the page_table_lock to protect us from page faults,
* munmap, fork, etc... * munmap, fork, etc...
*/ */
if (!spin_trylock(&mm->page_table_lock)) if (!spin_trylock(&mm->page_table_lock)) {
rmap_ptep_unmap(ptep);
return SWAP_AGAIN; return SWAP_AGAIN;
}
/* During mremap, it's possible pages are not in a VMA. */ /* During mremap, it's possible pages are not in a VMA. */
vma = find_vma(mm, address); vma = find_vma(mm, address);
...@@ -382,8 +396,7 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep) ...@@ -382,8 +396,7 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep)
/* Store the swap location in the pte. See handle_pte_fault() ... */ /* Store the swap location in the pte. See handle_pte_fault() ... */
if (PageSwapCache(page)) { if (PageSwapCache(page)) {
swp_entry_t entry; swp_entry_t entry = { .val = page->index };
entry.val = page->index;
swap_duplicate(entry); swap_duplicate(entry);
set_pte(ptep, swp_entry_to_pte(entry)); set_pte(ptep, swp_entry_to_pte(entry));
} }
...@@ -397,6 +410,7 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep) ...@@ -397,6 +410,7 @@ static int try_to_unmap_one(struct page * page, pte_t * ptep)
ret = SWAP_SUCCESS; ret = SWAP_SUCCESS;
out_unlock: out_unlock:
rmap_ptep_unmap(ptep);
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
return ret; return ret;
} }
...@@ -432,7 +446,7 @@ int try_to_unmap(struct page * page) ...@@ -432,7 +446,7 @@ int try_to_unmap(struct page * page)
if (PageDirect(page)) { if (PageDirect(page)) {
ret = try_to_unmap_one(page, page->pte.direct); ret = try_to_unmap_one(page, page->pte.direct);
if (ret == SWAP_SUCCESS) { if (ret == SWAP_SUCCESS) {
page->pte.direct = NULL; page->pte.direct = 0;
ClearPageDirect(page); ClearPageDirect(page);
} }
goto out; goto out;
...@@ -446,14 +460,14 @@ int try_to_unmap(struct page * page) ...@@ -446,14 +460,14 @@ int try_to_unmap(struct page * page)
if (next_pc) if (next_pc)
prefetch(next_pc); prefetch(next_pc);
for (i = 0; i < NRPTE; i++) { for (i = 0; i < NRPTE; i++) {
pte_t *p = pc->ptes[i]; pte_addr_t pte_paddr = pc->ptes[i];
if (!p) if (!pte_paddr)
continue; continue;
if (victim_i == -1) if (victim_i == -1)
victim_i = i; victim_i = i;
switch (try_to_unmap_one(page, p)) { switch (try_to_unmap_one(page, pte_paddr)) {
case SWAP_SUCCESS: case SWAP_SUCCESS:
/* /*
* Release a slot. If we're releasing the * Release a slot. If we're releasing the
...@@ -462,7 +476,7 @@ int try_to_unmap(struct page * page) ...@@ -462,7 +476,7 @@ int try_to_unmap(struct page * page)
* refer to the same thing. It works out. * refer to the same thing. It works out.
*/ */
pc->ptes[i] = start->ptes[victim_i]; pc->ptes[i] = start->ptes[victim_i];
start->ptes[victim_i] = NULL; start->ptes[victim_i] = 0;
dec_page_state(nr_reverse_maps); dec_page_state(nr_reverse_maps);
victim_i++; victim_i++;
if (victim_i == NRPTE) { if (victim_i == NRPTE) {
......
...@@ -75,7 +75,7 @@ static inline int page_mapping_inuse(struct page * page) ...@@ -75,7 +75,7 @@ static inline int page_mapping_inuse(struct page * page)
struct address_space *mapping = page->mapping; struct address_space *mapping = page->mapping;
/* Page is in somebody's page tables. */ /* Page is in somebody's page tables. */
if (page->pte.chain) if (page_mapped(page))
return 1; return 1;
/* XXX: does this happen ? */ /* XXX: does this happen ? */
...@@ -140,7 +140,7 @@ shrink_list(struct list_head *page_list, int nr_pages, ...@@ -140,7 +140,7 @@ shrink_list(struct list_head *page_list, int nr_pages,
* *
* XXX: implement swap clustering ? * XXX: implement swap clustering ?
*/ */
if (page->pte.chain && !mapping && !PagePrivate(page)) { if (page_mapped(page) && !mapping && !PagePrivate(page)) {
pte_chain_unlock(page); pte_chain_unlock(page);
if (!add_to_swap(page)) if (!add_to_swap(page))
goto activate_locked; goto activate_locked;
...@@ -151,7 +151,7 @@ shrink_list(struct list_head *page_list, int nr_pages, ...@@ -151,7 +151,7 @@ shrink_list(struct list_head *page_list, int nr_pages,
* The page is mapped into the page tables of one or more * The page is mapped into the page tables of one or more
* processes. Try to unmap it here. * processes. Try to unmap it here.
*/ */
if (page->pte.chain && mapping) { if (page_mapped(page) && mapping) {
switch (try_to_unmap(page)) { switch (try_to_unmap(page)) {
case SWAP_ERROR: case SWAP_ERROR:
case SWAP_FAIL: case SWAP_FAIL:
...@@ -408,9 +408,9 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in) ...@@ -408,9 +408,9 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in)
while (!list_empty(&l_hold)) { while (!list_empty(&l_hold)) {
page = list_entry(l_hold.prev, struct page, lru); page = list_entry(l_hold.prev, struct page, lru);
list_del(&page->lru); list_del(&page->lru);
if (page->pte.chain) { if (page_mapped(page)) {
pte_chain_lock(page); pte_chain_lock(page);
if (page->pte.chain && page_referenced(page)) { if (page_mapped(page) && page_referenced(page)) {
pte_chain_unlock(page); pte_chain_unlock(page);
list_add(&page->lru, &l_active); list_add(&page->lru, &l_active);
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment