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
define_bool CONFIG_X86_PAE y
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 'MTRR (Memory Type Range Register) support' CONFIG_MTRR
......
......@@ -370,4 +370,6 @@ extern void paging_init(void);
/* We have our own get_unmapped_area to cope with ADDR_LIMIT_32BIT. */
#define HAVE_ARCH_UNMAPPED_AREA
typedef pte_t *pte_addr_t;
#endif /* _ALPHA_PGTABLE_H */
......@@ -161,6 +161,8 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
#define io_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 /* _ASMARM_PGTABLE_H */
......@@ -515,4 +515,6 @@ static inline void update_mmu_cache(struct vm_area_struct * vma,
*/
#define pgtable_cache_init() do { } while (0)
typedef pte_t *pte_addr_t;
#endif /* _CRIS_PGTABLE_H */
......@@ -13,6 +13,16 @@
* - page->index has the high bits of the address
* - the lower bits of the address are calculated from the
* 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>
......@@ -39,16 +49,42 @@ static inline void pgtable_remove_rmap(struct page * page)
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;
}
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;
low_bits = ((unsigned long)ptep & ~PAGE_MASK) * PTRS_PER_PTE;
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 */
......@@ -103,6 +103,7 @@ extern void __set_fixmap (enum fixed_addresses idx,
#define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE)
#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);
......@@ -128,4 +129,10 @@ static inline unsigned long fix_to_virt(const unsigned int 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
......@@ -122,6 +122,19 @@ static inline void kunmap_atomic(void *kvaddr, enum km_type type)
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 /* _ASM_HIGHMEM_H */
......@@ -19,9 +19,10 @@ D(5) KM_BIO_SRC_IRQ,
D(6) KM_BIO_DST_IRQ,
D(7) KM_PTE0,
D(8) KM_PTE1,
D(9) KM_IRQ0,
D(10) KM_IRQ1,
D(11) KM_TYPE_NR
D(9) KM_PTE2,
D(10) KM_IRQ0,
D(11) KM_IRQ1,
D(12) KM_TYPE_NR
};
#undef D
......
......@@ -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_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
* tables contain all the necessary information.
......
......@@ -4,4 +4,18 @@
/* nothing to see, move along */
#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
......@@ -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 */
#define HAVE_ARCH_UNMAPPED_AREA
typedef pte_t *pte_addr_t;
# endif /* !__ASSEMBLY__ */
/*
......
......@@ -171,6 +171,9 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma,
#ifndef __ASSEMBLY__
#include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
/*
......
......@@ -771,6 +771,8 @@ extern inline void set_context(unsigned long val)
#include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !defined (_LANGUAGE_ASSEMBLY) */
#define io_remap_page_range remap_page_range
......
......@@ -811,6 +811,8 @@ extern inline void set_context(unsigned long val)
#include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !defined (_LANGUAGE_ASSEMBLY) */
/*
......
......@@ -326,6 +326,8 @@ extern inline void update_mmu_cache(struct vm_area_struct * vma,
#include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#define io_remap_page_range remap_page_range
......
......@@ -543,6 +543,8 @@ extern void kernel_set_cachemode (unsigned long address, unsigned long size,
*/
#define pgtable_cache_init() do { } while (0)
#endif /* __ASSEMBLY__ */
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#endif /* _PPC_PGTABLE_H */
#endif /* __KERNEL__ */
......@@ -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_iSeries(void);
typedef pte_t *pte_addr_t;
#endif /* __ASSEMBLY__ */
#endif /* _PPC64_PGTABLE_H */
......@@ -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 __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1)
......
......@@ -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 __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1)
......
......@@ -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))
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#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
#include <asm-generic/pgtable.h>
typedef pte_t *pte_addr_t;
#endif /* !(__ASSEMBLY__) */
/* 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
extern void check_pgt_cache(void);
typedef pte_t *pte_addr_t;
#endif /* !(__ASSEMBLY__) */
#endif /* !(_SPARC64_PGTABLE_H) */
......@@ -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 __swp_entry_to_pte(x) ((pte_t) { (x).val })
typedef pte_t *pte_addr_t;
#endif /* !__ASSEMBLY__ */
#define kern_addr_valid(addr) (1)
......
......@@ -26,6 +26,7 @@ static inline void *kmap(struct page *page) { return page_address(page); }
#define kmap_atomic(page, idx) page_address(page)
#define kunmap_atomic(addr, idx) do { } while (0)
#define kmap_atomic_to_page(ptr) virt_to_page(ptr)
#endif /* CONFIG_HIGHMEM */
......
......@@ -159,9 +159,9 @@ struct page {
struct list_head lru; /* Pageout list, eg. active_list;
protected by zone->lru_lock !! */
union {
struct pte_chain * chain; /* Reverse pte mapping pointer.
struct pte_chain *chain;/* Reverse pte mapping pointer.
* protected by PG_chainlock */
pte_t * direct;
pte_addr_t direct;
} pte;
unsigned long private; /* mapping-private opaque data */
......@@ -321,6 +321,16 @@ static inline void set_page_zone(struct page *page, unsigned long zone_num)
#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
*/
......
......@@ -93,7 +93,7 @@ void __free_pages_ok (struct page *page, unsigned int order)
BUG_ON(PageLocked(page));
BUG_ON(PageActive(page));
BUG_ON(PageWriteback(page));
BUG_ON(page->pte.chain != NULL);
BUG_ON(page->pte.direct != 0);
if (PageDirty(page))
ClearPageDirty(page);
BUG_ON(page_count(page) != 0);
......@@ -184,7 +184,7 @@ static inline void prep_new_page(struct page *page)
BUG_ON(PageActive(page));
BUG_ON(PageDirty(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 |
1 << PG_referenced | 1 << PG_arch_1 |
1 << PG_checked);
......
......@@ -43,11 +43,11 @@
* We use an array of pte pointers in this structure to minimise cache misses
* 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 *next;
pte_t *ptes[NRPTE];
pte_addr_t ptes[NRPTE];
};
static kmem_cache_t *pte_chain_cache;
......@@ -126,8 +126,10 @@ int page_referenced(struct page * page)
referenced++;
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++;
rmap_ptep_unmap(pte);
} else {
int nr_chains = 0;
......@@ -136,11 +138,15 @@ int page_referenced(struct page * page)
int i;
for (i = NRPTE-1; i >= 0; i--) {
pte_t *p = pc->ptes[i];
if (!p)
pte_addr_t pte_paddr = pc->ptes[i];
pte_t *p;
if (!pte_paddr)
break;
p = rmap_ptep_map(pte_paddr);
if (ptep_test_and_clear_young(p))
referenced++;
rmap_ptep_unmap(p);
nr_chains++;
}
}
......@@ -150,7 +156,6 @@ int page_referenced(struct page * page)
SetPageDirect(page);
pc->ptes[NRPTE-1] = 0;
pte_chain_free(pc);
dec_page_state(nr_reverse_maps);
}
}
return referenced;
......@@ -166,8 +171,8 @@ int page_referenced(struct page * page)
*/
void page_add_rmap(struct page * page, pte_t * ptep)
{
struct pte_chain * pte_chain;
unsigned long pfn = pte_pfn(*ptep);
pte_addr_t pte_paddr = ptep_to_paddr(ptep);
struct pte_chain *pte_chain;
int i;
#ifdef DEBUG_RMAP
......@@ -179,23 +184,26 @@ void page_add_rmap(struct page * page, pte_t * ptep)
BUG();
#endif
if (!pfn_valid(pfn) || PageReserved(page))
if (!pfn_valid(page_to_pfn(page)) || PageReserved(page))
return;
pte_chain_lock(page);
#ifdef DEBUG_RMAP
/*
* This stuff needs help to get up to highmem speed.
*/
{
struct pte_chain * pc;
if (PageDirect(page)) {
if (page->pte.direct == ptep)
if (page->pte.direct == pte_paddr)
BUG();
} else {
for (pc = page->pte.chain; pc; pc = pc->next) {
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();
}
}
......@@ -203,19 +211,19 @@ void page_add_rmap(struct page * page, pte_t * ptep)
}
#endif
if (page->pte.chain == NULL) {
page->pte.direct = ptep;
if (page->pte.direct == 0) {
page->pte.direct = pte_paddr;
SetPageDirect(page);
goto out;
}
if (PageDirect(page)) {
/* Convert a direct pointer into a pte_chain */
ClearPageDirect(page);
pte_chain = pte_chain_alloc();
pte_chain->ptes[NRPTE-1] = page->pte.direct;
pte_chain->ptes[NRPTE-2] = ptep;
mod_page_state(nr_reverse_maps, 2);
pte_chain->ptes[NRPTE-2] = pte_paddr;
page->pte.direct = 0;
page->pte.chain = pte_chain;
goto out;
}
......@@ -227,23 +235,22 @@ void page_add_rmap(struct page * page, pte_t * ptep)
new = pte_chain_alloc();
new->next = pte_chain;
page->pte.chain = new;
new->ptes[NRPTE-1] = ptep;
inc_page_state(nr_reverse_maps);
new->ptes[NRPTE-1] = pte_paddr;
goto out;
}
BUG_ON(pte_chain->ptes[NRPTE-1] == NULL);
BUG_ON(!pte_chain->ptes[NRPTE-1]);
for (i = NRPTE-2; i >= 0; i--) {
if (pte_chain->ptes[i] == NULL) {
pte_chain->ptes[i] = ptep;
inc_page_state(nr_reverse_maps);
if (!pte_chain->ptes[i]) {
pte_chain->ptes[i] = pte_paddr;
goto out;
}
}
BUG();
out:
pte_chain_unlock(page);
inc_page_state(nr_reverse_maps);
return;
}
......@@ -259,19 +266,22 @@ void page_add_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;
unsigned long pfn = page_to_pfn(page);
if (!page || !ptep)
BUG();
if (!pfn_valid(pfn) || PageReserved(page))
if (!pfn_valid(page_to_pfn(page)) || PageReserved(page))
return;
pte_chain_lock(page);
BUG_ON(page->pte.direct == 0);
if (PageDirect(page)) {
if (page->pte.direct == ptep) {
page->pte.direct = NULL;
if (page->pte.direct == pte_paddr) {
page->pte.direct = 0;
dec_page_state(nr_reverse_maps);
ClearPageDirect(page);
goto out;
}
......@@ -285,17 +295,17 @@ void page_remove_rmap(struct page * page, pte_t * ptep)
if (pc->next)
prefetch(pc->next);
for (i = 0; i < NRPTE; i++) {
pte_t *p = pc->ptes[i];
pte_addr_t pa = pc->ptes[i];
if (!p)
if (!pa)
continue;
if (victim_i == -1)
victim_i = i;
if (p != ptep)
if (pa != pte_paddr)
continue;
pc->ptes[i] = start->ptes[victim_i];
start->ptes[victim_i] = NULL;
dec_page_state(nr_reverse_maps);
start->ptes[victim_i] = 0;
if (victim_i == NRPTE-1) {
/* Emptied a pte_chain */
page->pte.chain = start->next;
......@@ -343,9 +353,10 @@ void page_remove_rmap(struct page * page, pte_t * ptep)
* pte_chain_lock page_launder()
* mm->page_table_lock try_to_unmap_one(), trylock
*/
static int FASTCALL(try_to_unmap_one(struct page *, pte_t *));
static int try_to_unmap_one(struct page * page, pte_t * ptep)
static int FASTCALL(try_to_unmap_one(struct page *, pte_addr_t));
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);
struct mm_struct * mm = ptep_to_mm(ptep);
struct vm_area_struct * vma;
......@@ -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,
* munmap, fork, etc...
*/
if (!spin_trylock(&mm->page_table_lock))
if (!spin_trylock(&mm->page_table_lock)) {
rmap_ptep_unmap(ptep);
return SWAP_AGAIN;
}
/* During mremap, it's possible pages are not in a VMA. */
vma = find_vma(mm, address);
......@@ -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() ... */
if (PageSwapCache(page)) {
swp_entry_t entry;
entry.val = page->index;
swp_entry_t entry = { .val = page->index };
swap_duplicate(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)
ret = SWAP_SUCCESS;
out_unlock:
rmap_ptep_unmap(ptep);
spin_unlock(&mm->page_table_lock);
return ret;
}
......@@ -432,7 +446,7 @@ int try_to_unmap(struct page * page)
if (PageDirect(page)) {
ret = try_to_unmap_one(page, page->pte.direct);
if (ret == SWAP_SUCCESS) {
page->pte.direct = NULL;
page->pte.direct = 0;
ClearPageDirect(page);
}
goto out;
......@@ -446,14 +460,14 @@ int try_to_unmap(struct page * page)
if (next_pc)
prefetch(next_pc);
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;
if (victim_i == -1)
victim_i = i;
switch (try_to_unmap_one(page, p)) {
switch (try_to_unmap_one(page, pte_paddr)) {
case SWAP_SUCCESS:
/*
* Release a slot. If we're releasing the
......@@ -462,7 +476,7 @@ int try_to_unmap(struct page * page)
* refer to the same thing. It works out.
*/
pc->ptes[i] = start->ptes[victim_i];
start->ptes[victim_i] = NULL;
start->ptes[victim_i] = 0;
dec_page_state(nr_reverse_maps);
victim_i++;
if (victim_i == NRPTE) {
......
......@@ -75,7 +75,7 @@ static inline int page_mapping_inuse(struct page * page)
struct address_space *mapping = page->mapping;
/* Page is in somebody's page tables. */
if (page->pte.chain)
if (page_mapped(page))
return 1;
/* XXX: does this happen ? */
......@@ -140,7 +140,7 @@ shrink_list(struct list_head *page_list, int nr_pages,
*
* XXX: implement swap clustering ?
*/
if (page->pte.chain && !mapping && !PagePrivate(page)) {
if (page_mapped(page) && !mapping && !PagePrivate(page)) {
pte_chain_unlock(page);
if (!add_to_swap(page))
goto activate_locked;
......@@ -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
* processes. Try to unmap it here.
*/
if (page->pte.chain && mapping) {
if (page_mapped(page) && mapping) {
switch (try_to_unmap(page)) {
case SWAP_ERROR:
case SWAP_FAIL:
......@@ -408,9 +408,9 @@ refill_inactive_zone(struct zone *zone, const int nr_pages_in)
while (!list_empty(&l_hold)) {
page = list_entry(l_hold.prev, struct page, lru);
list_del(&page->lru);
if (page->pte.chain) {
if (page_mapped(page)) {
pte_chain_lock(page);
if (page->pte.chain && page_referenced(page)) {
if (page_mapped(page) && page_referenced(page)) {
pte_chain_unlock(page);
list_add(&page->lru, &l_active);
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