2005-04-17 00:20:36 +02:00
|
|
|
#ifndef _I386_PGTABLE_H
|
|
|
|
#define _I386_PGTABLE_H
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Linux memory management assumes a three-level page table setup. On
|
|
|
|
* the i386, we use that, but "fold" the mid level into the top-level page
|
|
|
|
* table, so that we physically have the same two-level page table as the
|
|
|
|
* i386 mmu expects.
|
|
|
|
*
|
|
|
|
* This file contains the functions and defines necessary to modify and use
|
|
|
|
* the i386 page table tree.
|
|
|
|
*/
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/fixmap.h>
|
|
|
|
#include <linux/threads.h>
|
|
|
|
|
|
|
|
#ifndef _I386_BITOPS_H
|
|
|
|
#include <asm/bitops.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
|
2005-11-07 09:59:43 +01:00
|
|
|
struct mm_struct;
|
|
|
|
struct vm_area_struct;
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* ZERO_PAGE is a global shared page that is always zero: used
|
|
|
|
* for zero-mapped memory areas etc..
|
|
|
|
*/
|
|
|
|
#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
|
|
|
|
extern unsigned long empty_zero_page[1024];
|
|
|
|
extern pgd_t swapper_pg_dir[1024];
|
|
|
|
extern kmem_cache_t *pgd_cache;
|
|
|
|
extern kmem_cache_t *pmd_cache;
|
|
|
|
extern spinlock_t pgd_lock;
|
|
|
|
extern struct page *pgd_list;
|
|
|
|
|
|
|
|
void pmd_ctor(void *, kmem_cache_t *, unsigned long);
|
|
|
|
void pgd_ctor(void *, kmem_cache_t *, unsigned long);
|
|
|
|
void pgd_dtor(void *, kmem_cache_t *, unsigned long);
|
|
|
|
void pgtable_cache_init(void);
|
|
|
|
void paging_init(void);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Linux x86 paging architecture is 'compile-time dual-mode', it
|
|
|
|
* implements both the traditional 2-level x86 page tables and the
|
|
|
|
* newer 3-level PAE-mode page tables.
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_X86_PAE
|
|
|
|
# include <asm/pgtable-3level-defs.h>
|
|
|
|
# define PMD_SIZE (1UL << PMD_SHIFT)
|
|
|
|
# define PMD_MASK (~(PMD_SIZE-1))
|
|
|
|
#else
|
|
|
|
# include <asm/pgtable-2level-defs.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
|
|
|
|
#define PGDIR_MASK (~(PGDIR_SIZE-1))
|
|
|
|
|
|
|
|
#define USER_PTRS_PER_PGD (TASK_SIZE/PGDIR_SIZE)
|
2005-04-19 22:29:23 +02:00
|
|
|
#define FIRST_USER_ADDRESS 0
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT)
|
|
|
|
#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
|
|
|
|
|
|
|
|
#define TWOLEVEL_PGDIR_SHIFT 22
|
|
|
|
#define BOOT_USER_PGD_PTRS (__PAGE_OFFSET >> TWOLEVEL_PGDIR_SHIFT)
|
|
|
|
#define BOOT_KERNEL_PGD_PTRS (1024-BOOT_USER_PGD_PTRS)
|
|
|
|
|
|
|
|
/* Just any arbitrary offset to the start of the vmalloc VM area: the
|
|
|
|
* current 8MB value just means that there will be a 8MB "hole" after the
|
|
|
|
* physical memory until the kernel virtual memory starts. That means that
|
|
|
|
* any out-of-bounds memory accesses will hopefully be caught.
|
|
|
|
* The vmalloc() routines leaves a hole of 4kB between each vmalloced
|
|
|
|
* area for the same reason. ;)
|
|
|
|
*/
|
|
|
|
#define VMALLOC_OFFSET (8*1024*1024)
|
|
|
|
#define VMALLOC_START (((unsigned long) high_memory + vmalloc_earlyreserve + \
|
|
|
|
2*VMALLOC_OFFSET-1) & ~(VMALLOC_OFFSET-1))
|
|
|
|
#ifdef CONFIG_HIGHMEM
|
|
|
|
# define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE)
|
|
|
|
#else
|
|
|
|
# define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2005-09-04 00:54:57 +02:00
|
|
|
* _PAGE_PSE set in the page directory entry just means that
|
2005-04-17 00:20:36 +02:00
|
|
|
* the page directory entry points directly to a 4MB-aligned block of
|
|
|
|
* memory.
|
|
|
|
*/
|
|
|
|
#define _PAGE_BIT_PRESENT 0
|
|
|
|
#define _PAGE_BIT_RW 1
|
|
|
|
#define _PAGE_BIT_USER 2
|
|
|
|
#define _PAGE_BIT_PWT 3
|
|
|
|
#define _PAGE_BIT_PCD 4
|
|
|
|
#define _PAGE_BIT_ACCESSED 5
|
|
|
|
#define _PAGE_BIT_DIRTY 6
|
|
|
|
#define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page, Pentium+, if present.. */
|
|
|
|
#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
|
|
|
|
#define _PAGE_BIT_UNUSED1 9 /* available for programmer */
|
|
|
|
#define _PAGE_BIT_UNUSED2 10
|
|
|
|
#define _PAGE_BIT_UNUSED3 11
|
|
|
|
#define _PAGE_BIT_NX 63
|
|
|
|
|
|
|
|
#define _PAGE_PRESENT 0x001
|
|
|
|
#define _PAGE_RW 0x002
|
|
|
|
#define _PAGE_USER 0x004
|
|
|
|
#define _PAGE_PWT 0x008
|
|
|
|
#define _PAGE_PCD 0x010
|
|
|
|
#define _PAGE_ACCESSED 0x020
|
|
|
|
#define _PAGE_DIRTY 0x040
|
|
|
|
#define _PAGE_PSE 0x080 /* 4 MB (or 2MB) page, Pentium+, if present.. */
|
|
|
|
#define _PAGE_GLOBAL 0x100 /* Global TLB entry PPro+ */
|
|
|
|
#define _PAGE_UNUSED1 0x200 /* available for programmer */
|
|
|
|
#define _PAGE_UNUSED2 0x400
|
|
|
|
#define _PAGE_UNUSED3 0x800
|
|
|
|
|
2005-09-04 00:54:57 +02:00
|
|
|
/* If _PAGE_PRESENT is clear, we use these: */
|
|
|
|
#define _PAGE_FILE 0x040 /* nonlinear file mapping, saved PTE; unset:swap */
|
|
|
|
#define _PAGE_PROTNONE 0x080 /* if the user mapped it with PROT_NONE;
|
|
|
|
pte_present gives true */
|
2005-04-17 00:20:36 +02:00
|
|
|
#ifdef CONFIG_X86_PAE
|
|
|
|
#define _PAGE_NX (1ULL<<_PAGE_BIT_NX)
|
|
|
|
#else
|
|
|
|
#define _PAGE_NX 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
|
|
|
|
#define _KERNPG_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
|
|
|
|
#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
|
|
|
|
|
|
|
|
#define PAGE_NONE \
|
|
|
|
__pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
|
|
|
|
#define PAGE_SHARED \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
|
|
|
|
|
|
|
|
#define PAGE_SHARED_EXEC \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED)
|
|
|
|
#define PAGE_COPY_NOEXEC \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_NX)
|
|
|
|
#define PAGE_COPY_EXEC \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
|
|
|
|
#define PAGE_COPY \
|
|
|
|
PAGE_COPY_NOEXEC
|
|
|
|
#define PAGE_READONLY \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_NX)
|
|
|
|
#define PAGE_READONLY_EXEC \
|
|
|
|
__pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED)
|
|
|
|
|
|
|
|
#define _PAGE_KERNEL \
|
|
|
|
(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_NX)
|
|
|
|
#define _PAGE_KERNEL_EXEC \
|
|
|
|
(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED)
|
|
|
|
|
|
|
|
extern unsigned long long __PAGE_KERNEL, __PAGE_KERNEL_EXEC;
|
|
|
|
#define __PAGE_KERNEL_RO (__PAGE_KERNEL & ~_PAGE_RW)
|
|
|
|
#define __PAGE_KERNEL_NOCACHE (__PAGE_KERNEL | _PAGE_PCD)
|
|
|
|
#define __PAGE_KERNEL_LARGE (__PAGE_KERNEL | _PAGE_PSE)
|
|
|
|
#define __PAGE_KERNEL_LARGE_EXEC (__PAGE_KERNEL_EXEC | _PAGE_PSE)
|
|
|
|
|
|
|
|
#define PAGE_KERNEL __pgprot(__PAGE_KERNEL)
|
|
|
|
#define PAGE_KERNEL_RO __pgprot(__PAGE_KERNEL_RO)
|
|
|
|
#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC)
|
|
|
|
#define PAGE_KERNEL_NOCACHE __pgprot(__PAGE_KERNEL_NOCACHE)
|
|
|
|
#define PAGE_KERNEL_LARGE __pgprot(__PAGE_KERNEL_LARGE)
|
|
|
|
#define PAGE_KERNEL_LARGE_EXEC __pgprot(__PAGE_KERNEL_LARGE_EXEC)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The i386 can't do page protection for execute, and considers that
|
|
|
|
* the same are read. Also, write permissions imply read permissions.
|
|
|
|
* This is the closest we can get..
|
|
|
|
*/
|
|
|
|
#define __P000 PAGE_NONE
|
|
|
|
#define __P001 PAGE_READONLY
|
|
|
|
#define __P010 PAGE_COPY
|
|
|
|
#define __P011 PAGE_COPY
|
|
|
|
#define __P100 PAGE_READONLY_EXEC
|
|
|
|
#define __P101 PAGE_READONLY_EXEC
|
|
|
|
#define __P110 PAGE_COPY_EXEC
|
|
|
|
#define __P111 PAGE_COPY_EXEC
|
|
|
|
|
|
|
|
#define __S000 PAGE_NONE
|
|
|
|
#define __S001 PAGE_READONLY
|
|
|
|
#define __S010 PAGE_SHARED
|
|
|
|
#define __S011 PAGE_SHARED
|
|
|
|
#define __S100 PAGE_READONLY_EXEC
|
|
|
|
#define __S101 PAGE_READONLY_EXEC
|
|
|
|
#define __S110 PAGE_SHARED_EXEC
|
|
|
|
#define __S111 PAGE_SHARED_EXEC
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Define this if things work differently on an i386 and an i486:
|
|
|
|
* it will (on an i486) warn about kernel memory accesses that are
|
2005-05-01 17:59:08 +02:00
|
|
|
* done without a 'access_ok(VERIFY_WRITE,..)'
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
2005-05-01 17:59:08 +02:00
|
|
|
#undef TEST_ACCESS_OK
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* The boot page tables (all created as a single array) */
|
|
|
|
extern unsigned long pg0[];
|
|
|
|
|
|
|
|
#define pte_present(x) ((x).pte_low & (_PAGE_PRESENT | _PAGE_PROTNONE))
|
|
|
|
|
2005-10-30 02:16:27 +01:00
|
|
|
/* To avoid harmful races, pmd_none(x) should check only the lower when PAE */
|
|
|
|
#define pmd_none(x) (!(unsigned long)pmd_val(x))
|
2005-04-17 00:20:36 +02:00
|
|
|
#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
|
|
|
|
#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
|
|
|
|
|
|
|
|
|
|
|
|
#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following only work if pte_present() is true.
|
|
|
|
* Undefined behaviour if not..
|
|
|
|
*/
|
|
|
|
static inline int pte_user(pte_t pte) { return (pte).pte_low & _PAGE_USER; }
|
|
|
|
static inline int pte_read(pte_t pte) { return (pte).pte_low & _PAGE_USER; }
|
|
|
|
static inline int pte_dirty(pte_t pte) { return (pte).pte_low & _PAGE_DIRTY; }
|
|
|
|
static inline int pte_young(pte_t pte) { return (pte).pte_low & _PAGE_ACCESSED; }
|
|
|
|
static inline int pte_write(pte_t pte) { return (pte).pte_low & _PAGE_RW; }
|
2006-03-22 09:08:50 +01:00
|
|
|
static inline int pte_huge(pte_t pte) { return (pte).pte_low & _PAGE_PSE; }
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The following only works if pte_present() is not true.
|
|
|
|
*/
|
|
|
|
static inline int pte_file(pte_t pte) { return (pte).pte_low & _PAGE_FILE; }
|
|
|
|
|
|
|
|
static inline pte_t pte_rdprotect(pte_t pte) { (pte).pte_low &= ~_PAGE_USER; return pte; }
|
|
|
|
static inline pte_t pte_exprotect(pte_t pte) { (pte).pte_low &= ~_PAGE_USER; return pte; }
|
|
|
|
static inline pte_t pte_mkclean(pte_t pte) { (pte).pte_low &= ~_PAGE_DIRTY; return pte; }
|
|
|
|
static inline pte_t pte_mkold(pte_t pte) { (pte).pte_low &= ~_PAGE_ACCESSED; return pte; }
|
|
|
|
static inline pte_t pte_wrprotect(pte_t pte) { (pte).pte_low &= ~_PAGE_RW; return pte; }
|
|
|
|
static inline pte_t pte_mkread(pte_t pte) { (pte).pte_low |= _PAGE_USER; return pte; }
|
|
|
|
static inline pte_t pte_mkexec(pte_t pte) { (pte).pte_low |= _PAGE_USER; return pte; }
|
|
|
|
static inline pte_t pte_mkdirty(pte_t pte) { (pte).pte_low |= _PAGE_DIRTY; return pte; }
|
|
|
|
static inline pte_t pte_mkyoung(pte_t pte) { (pte).pte_low |= _PAGE_ACCESSED; return pte; }
|
|
|
|
static inline pte_t pte_mkwrite(pte_t pte) { (pte).pte_low |= _PAGE_RW; return pte; }
|
2006-03-22 09:08:50 +01:00
|
|
|
static inline pte_t pte_mkhuge(pte_t pte) { (pte).pte_low |= _PAGE_PSE; return pte; }
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_X86_PAE
|
|
|
|
# include <asm/pgtable-3level.h>
|
|
|
|
#else
|
|
|
|
# include <asm/pgtable-2level.h>
|
|
|
|
#endif
|
|
|
|
|
2006-09-26 08:32:31 +02:00
|
|
|
/*
|
|
|
|
* We only update the dirty/accessed state if we set
|
|
|
|
* the dirty bit by hand in the kernel, since the hardware
|
|
|
|
* will do the accessed bit for us, and we don't want to
|
|
|
|
* race with other CPU's that might be updating the dirty
|
|
|
|
* bit at the same time.
|
|
|
|
*/
|
|
|
|
#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
|
|
|
|
#define ptep_set_access_flags(vma, address, ptep, entry, dirty) \
|
|
|
|
do { \
|
|
|
|
if (dirty) { \
|
|
|
|
(ptep)->pte_low = (entry).pte_low; \
|
|
|
|
flush_tlb_page(vma, address); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2006-09-26 08:32:30 +02:00
|
|
|
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY
|
2005-04-17 00:20:36 +02:00
|
|
|
static inline int ptep_test_and_clear_dirty(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
|
|
|
|
{
|
|
|
|
if (!pte_dirty(*ptep))
|
|
|
|
return 0;
|
|
|
|
return test_and_clear_bit(_PAGE_BIT_DIRTY, &ptep->pte_low);
|
|
|
|
}
|
|
|
|
|
2006-09-26 08:32:30 +02:00
|
|
|
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
|
2005-04-17 00:20:36 +02:00
|
|
|
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
|
|
|
|
{
|
|
|
|
if (!pte_young(*ptep))
|
|
|
|
return 0;
|
|
|
|
return test_and_clear_bit(_PAGE_BIT_ACCESSED, &ptep->pte_low);
|
|
|
|
}
|
|
|
|
|
2006-09-26 08:32:30 +02:00
|
|
|
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR_FULL
|
[PATCH] x86: ptep_clear optimization
Add a new accessor for PTEs, which passes the full hint from the mmu_gather
struct; this allows architectures with hardware pagetables to optimize away
atomic PTE operations when destroying an address space. Removing the
locked operation should allow better pipelining of memory access in this
loop. I measured an average savings of 30-35 cycles per zap_pte_range on
the first 500 destructions on Pentium-M, but I believe the optimization
would win more on older processors which still assert the bus lock on xchg
for an exclusive cacheline.
Update: I made some new measurements, and this saves exactly 26 cycles over
ptep_get_and_clear on Pentium M. On P4, with a PAE kernel, this saves 180
cycles per ptep_get_and_clear, for a whopping 92160 cycles savings for a
full address space destruction.
pte_clear_full is not yet used, but is provided for future optimizations
(in particular, when running inside of a hypervisor that queues page table
updates, the full hint allows us to avoid queueing unnecessary page table
update for an address space in the process of being destroyed.
This is not a huge win, but it does help a bit, and sets the stage for
further hypervisor optimization of the mm layer on all architectures.
Signed-off-by: Zachary Amsden <zach@vmware.com>
Cc: Christoph Lameter <christoph@lameter.com>
Cc: <linux-mm@kvack.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-04 00:55:04 +02:00
|
|
|
static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, unsigned long addr, pte_t *ptep, int full)
|
|
|
|
{
|
|
|
|
pte_t pte;
|
|
|
|
if (full) {
|
|
|
|
pte = *ptep;
|
[PATCH] x86/PAE: Fix pte_clear for the >4GB RAM case
Proposed fix for ptep_get_and_clear_full PAE bug. Pte_clear had the same bug,
so use the same fix for both. Turns out pmd_clear had it as well, but pgds
are not affected.
The problem is rather intricate. Page table entries in PAE mode are 64-bits
wide, but the only atomic 8-byte write operation available in 32-bit mode is
cmpxchg8b, which is expensive (at least on P4), and thus avoided. But it can
happen that the processor may prefetch entries into the TLB in the middle of an
operation which clears a page table entry. So one must always clear the P-bit
in the low word of the page table entry first when clearing it.
Since the sequence *ptep = __pte(0) leaves the order of the write dependent on
the compiler, it must be coded explicitly as a clear of the low word followed
by a clear of the high word. Further, there must be a write memory barrier
here to enforce proper ordering by the compiler (and, in the future, by the
processor as well).
On > 4GB memory machines, the implementation of pte_clear for PAE was clearly
deficient, as it could leave virtual mappings of physical memory above 4GB
aliased to memory below 4GB in the TLB. The implementation of
ptep_get_and_clear_full has a similar bug, although not nearly as likely to
occur, since the mappings being cleared are in the process of being destroyed,
and should never be dereferenced again.
But, as luck would have it, it is possible to trigger bugs even without ever
dereferencing these bogus TLB mappings, even if the clear is followed fairly
soon after with a TLB flush or invalidation. The problem is that memory above
4GB may now be aliased into the first 4GB of memory, and in fact, may hit a
region of memory with non-memory semantics. These regions include AGP and PCI
space. As such, these memory regions are not cached by the processor. This
introduces the bug.
The processor can speculate memory operations, including memory writes, as long
as they are committed with the proper ordering. Speculating a memory write to
a linear address that has a bogus TLB mapping is possible. Normally, the
speculation is harmless. But for cached memory, it does leave the falsely
speculated cacheline unmodified, but in a dirty state. This cache line will be
eventually written back. If this cacheline happens to intersect a region of
memory that is not protected by the cache coherency protocol, it can corrupt
data in I/O memory, which is generally a very bad thing to do, and can cause
total system failure or just plain undefined behavior.
These bugs are extremely unlikely, but the severity is of such magnitude, and
the fix so simple that I think fixing them immediately is justified. Also,
they are nearly impossible to debug.
Signed-off-by: Zachary Amsden <zach@vmware.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-04-27 20:32:29 +02:00
|
|
|
pte_clear(mm, addr, ptep);
|
[PATCH] x86: ptep_clear optimization
Add a new accessor for PTEs, which passes the full hint from the mmu_gather
struct; this allows architectures with hardware pagetables to optimize away
atomic PTE operations when destroying an address space. Removing the
locked operation should allow better pipelining of memory access in this
loop. I measured an average savings of 30-35 cycles per zap_pte_range on
the first 500 destructions on Pentium-M, but I believe the optimization
would win more on older processors which still assert the bus lock on xchg
for an exclusive cacheline.
Update: I made some new measurements, and this saves exactly 26 cycles over
ptep_get_and_clear on Pentium M. On P4, with a PAE kernel, this saves 180
cycles per ptep_get_and_clear, for a whopping 92160 cycles savings for a
full address space destruction.
pte_clear_full is not yet used, but is provided for future optimizations
(in particular, when running inside of a hypervisor that queues page table
updates, the full hint allows us to avoid queueing unnecessary page table
update for an address space in the process of being destroyed.
This is not a huge win, but it does help a bit, and sets the stage for
further hypervisor optimization of the mm layer on all architectures.
Signed-off-by: Zachary Amsden <zach@vmware.com>
Cc: Christoph Lameter <christoph@lameter.com>
Cc: <linux-mm@kvack.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-04 00:55:04 +02:00
|
|
|
} else {
|
|
|
|
pte = ptep_get_and_clear(mm, addr, ptep);
|
|
|
|
}
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
2006-09-26 08:32:30 +02:00
|
|
|
#define __HAVE_ARCH_PTEP_SET_WRPROTECT
|
2005-04-17 00:20:36 +02:00
|
|
|
static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
|
|
|
|
{
|
|
|
|
clear_bit(_PAGE_BIT_RW, &ptep->pte_low);
|
|
|
|
}
|
|
|
|
|
2005-09-04 00:56:50 +02:00
|
|
|
/*
|
|
|
|
* clone_pgd_range(pgd_t *dst, pgd_t *src, int count);
|
|
|
|
*
|
|
|
|
* dst - pointer to pgd range anwhere on a pgd page
|
|
|
|
* src - ""
|
|
|
|
* count - the number of pgds to copy.
|
|
|
|
*
|
|
|
|
* dst and src can be on the same page, but the range must not overlap,
|
|
|
|
* and must not cross a page boundary.
|
|
|
|
*/
|
|
|
|
static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count)
|
|
|
|
{
|
|
|
|
memcpy(dst, src, count * sizeof(pgd_t));
|
|
|
|
}
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* Macro to mark a page protection value as "uncacheable". On processors which do not support
|
|
|
|
* it, this is a no-op.
|
|
|
|
*/
|
|
|
|
#define pgprot_noncached(prot) ((boot_cpu_data.x86 > 3) \
|
|
|
|
? (__pgprot(pgprot_val(prot) | _PAGE_PCD | _PAGE_PWT)) : (prot))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Conversion functions: convert a page and protection to a page entry,
|
|
|
|
* and a page entry and page directory to the page they refer to.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), (pgprot))
|
|
|
|
|
|
|
|
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
|
|
|
{
|
|
|
|
pte.pte_low &= _PAGE_CHG_MASK;
|
|
|
|
pte.pte_low |= pgprot_val(newprot);
|
|
|
|
#ifdef CONFIG_X86_PAE
|
|
|
|
/*
|
|
|
|
* Chop off the NX bit (if present), and add the NX portion of
|
|
|
|
* the newprot (if present):
|
|
|
|
*/
|
|
|
|
pte.pte_high &= ~(1 << (_PAGE_BIT_NX - 32));
|
|
|
|
pte.pte_high |= (pgprot_val(newprot) >> 32) & \
|
|
|
|
(__supported_pte_mask >> 32);
|
|
|
|
#endif
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define pmd_large(pmd) \
|
|
|
|
((pmd_val(pmd) & (_PAGE_PSE|_PAGE_PRESENT)) == (_PAGE_PSE|_PAGE_PRESENT))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the pgd page can be thought of an array like this: pgd_t[PTRS_PER_PGD]
|
|
|
|
*
|
|
|
|
* this macro returns the index of the entry in the pgd page which would
|
|
|
|
* control the given virtual address
|
|
|
|
*/
|
|
|
|
#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
|
|
|
|
#define pgd_index_k(addr) pgd_index(addr)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pgd_offset() returns a (pgd_t *)
|
|
|
|
* pgd_index() is used get the offset into the pgd page's array of pgd_t's;
|
|
|
|
*/
|
|
|
|
#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a shortcut which implies the use of the kernel's pgd, instead
|
|
|
|
* of a process's
|
|
|
|
*/
|
|
|
|
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD]
|
|
|
|
*
|
|
|
|
* this macro returns the index of the entry in the pmd page which would
|
|
|
|
* control the given virtual address
|
|
|
|
*/
|
|
|
|
#define pmd_index(address) \
|
|
|
|
(((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the pte page can be thought of an array like this: pte_t[PTRS_PER_PTE]
|
|
|
|
*
|
|
|
|
* this macro returns the index of the entry in the pte page which would
|
|
|
|
* control the given virtual address
|
|
|
|
*/
|
|
|
|
#define pte_index(address) \
|
|
|
|
(((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
|
|
|
|
#define pte_offset_kernel(dir, address) \
|
2006-09-26 08:31:48 +02:00
|
|
|
((pte_t *) pmd_page_vaddr(*(dir)) + pte_index(address))
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-10-30 23:59:31 +01:00
|
|
|
#define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT))
|
|
|
|
|
2006-09-26 08:31:48 +02:00
|
|
|
#define pmd_page_vaddr(pmd) \
|
2005-10-30 23:59:31 +01:00
|
|
|
((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* Helper function that returns the kernel pagetable entry controlling
|
|
|
|
* the virtual address 'address'. NULL means no pagetable entry present.
|
|
|
|
* NOTE: the return type is pte_t but if the pmd is PSE then we return it
|
|
|
|
* as a pte too.
|
|
|
|
*/
|
|
|
|
extern pte_t *lookup_address(unsigned long address);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make a given kernel text page executable/non-executable.
|
|
|
|
* Returns the previous executability setting of that page (which
|
|
|
|
* is used to restore the previous state). Used by the SMP bootup code.
|
|
|
|
* NOTE: this is an __init function for security reasons.
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_X86_PAE
|
|
|
|
extern int set_kernel_exec(unsigned long vaddr, int enable);
|
|
|
|
#else
|
|
|
|
static inline int set_kernel_exec(unsigned long vaddr, int enable) { return 0;}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_HIGHPTE)
|
|
|
|
#define pte_offset_map(dir, address) \
|
|
|
|
((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address))
|
|
|
|
#define pte_offset_map_nested(dir, address) \
|
|
|
|
((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address))
|
|
|
|
#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0)
|
|
|
|
#define pte_unmap_nested(pte) kunmap_atomic(pte, KM_PTE1)
|
|
|
|
#else
|
|
|
|
#define pte_offset_map(dir, address) \
|
|
|
|
((pte_t *)page_address(pmd_page(*(dir))) + pte_index(address))
|
|
|
|
#define pte_offset_map_nested(dir, address) pte_offset_map(dir, address)
|
|
|
|
#define pte_unmap(pte) do { } while (0)
|
|
|
|
#define pte_unmap_nested(pte) do { } while (0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The i386 doesn't have any external MMU info: the kernel page
|
|
|
|
* tables contain all the necessary information.
|
|
|
|
*/
|
|
|
|
#define update_mmu_cache(vma,address,pte) do { } while (0)
|
|
|
|
#endif /* !__ASSEMBLY__ */
|
|
|
|
|
2005-06-23 09:07:57 +02:00
|
|
|
#ifdef CONFIG_FLATMEM
|
2005-04-17 00:20:36 +02:00
|
|
|
#define kern_addr_valid(addr) (1)
|
2005-06-23 09:07:57 +02:00
|
|
|
#endif /* CONFIG_FLATMEM */
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
|
|
|
|
remap_pfn_range(vma, vaddr, pfn, size, prot)
|
|
|
|
|
|
|
|
#define MK_IOSPACE_PFN(space, pfn) (pfn)
|
|
|
|
#define GET_IOSPACE(pfn) 0
|
|
|
|
#define GET_PFN(pfn) (pfn)
|
|
|
|
|
|
|
|
#include <asm-generic/pgtable.h>
|
|
|
|
|
|
|
|
#endif /* _I386_PGTABLE_H */
|