From: ak@suse.de (Andreas Kleen)
To: torvalds@osdl.org
Cc: linux-kernel@vger.kernel.org, akpm@osdl.org
Subject: [PATCH 3/17] 4level core patch
Date: Mon, 25 Oct 2004 09:23:49 +0200 [thread overview]
Message-ID: <417CAA05.mail3Y814D9ZM@wotan.suse.de> (raw)
4level core patch
This patch extends the Linux MM to 4level page tables.
The conversion is quite straight forward. The additional level
is called pml4. The biggest change is that I rewrote copy_page_range(),
because the old version just wasn't scalable anymore.
This is the core patch for mm/*, fs/*, include/linux/*
It breaks all architectures, which will be fixed in separate patches.
pgd_offset() and pgd_offset_k() have been renamed to make sure
all unconverted code causes an compile error. They are replaced
with pml4_offset()/pml4_offset_k() and pml4_pgd_offset()/pml4_pgd_offset_k()
Please note than when you use pml4_offset_k() you also have
to use pml4_pgd_offset_k() because IA64 requires a special case here.
Signed-off-by: Andi Kleen <ak@suse.de>
diff -urpN -X ../KDIFX linux-2.6.10rc1/fs/exec.c linux-2.6.10rc1-4level/fs/exec.c
--- linux-2.6.10rc1/fs/exec.c 2004-10-25 04:47:16.000000000 +0200
+++ linux-2.6.10rc1-4level/fs/exec.c 2004-10-25 06:22:28.000000000 +0200
@@ -299,6 +299,7 @@ void install_arg_page(struct vm_area_str
struct page *page, unsigned long address)
{
struct mm_struct *mm = vma->vm_mm;
+ pml4_t *pml4;
pgd_t * pgd;
pmd_t * pmd;
pte_t * pte;
@@ -307,9 +308,12 @@ void install_arg_page(struct vm_area_str
goto out_sig;
flush_dcache_page(page);
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
spin_lock(&mm->page_table_lock);
+ pgd = pgd_alloc(mm, pml4, address);
+ if (!pgd)
+ goto out;
pmd = pmd_alloc(mm, pgd, address);
if (!pmd)
goto out;
diff -urpN -X ../KDIFX linux-2.6.10rc1/include/linux/init_task.h linux-2.6.10rc1-4level/include/linux/init_task.h
--- linux-2.6.10rc1/include/linux/init_task.h 2004-10-25 04:47:37.000000000 +0200
+++ linux-2.6.10rc1-4level/include/linux/init_task.h 2004-10-25 04:48:10.000000000 +0200
@@ -34,7 +34,7 @@
#define INIT_MM(name) \
{ \
.mm_rb = RB_ROOT, \
- .pgd = swapper_pg_dir, \
+ .pml4 = swapper_pml4, \
.mm_users = ATOMIC_INIT(2), \
.mm_count = ATOMIC_INIT(1), \
.mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \
diff -urpN -X ../KDIFX linux-2.6.10rc1/include/linux/mm.h linux-2.6.10rc1-4level/include/linux/mm.h
--- linux-2.6.10rc1/include/linux/mm.h 2004-10-25 04:47:37.000000000 +0200
+++ linux-2.6.10rc1-4level/include/linux/mm.h 2004-10-25 04:48:10.000000000 +0200
@@ -564,7 +564,7 @@ int unmap_vmas(struct mmu_gather **tlbp,
struct vm_area_struct *start_vma, unsigned long start_addr,
unsigned long end_addr, unsigned long *nr_accounted,
struct zap_details *);
-void clear_page_tables(struct mmu_gather *tlb, unsigned long first, int nr);
+void clear_page_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end);
int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *vma);
int zeromap_page_range(struct vm_area_struct *vma, unsigned long from,
@@ -580,6 +580,7 @@ static inline void unmap_shared_mapping_
extern int vmtruncate(struct inode * inode, loff_t offset);
extern pmd_t *FASTCALL(__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address));
+extern pgd_t fastcall *__pgd_alloc(struct mm_struct *mm, pml4_t *pgd, unsigned long address);
extern pte_t *FASTCALL(pte_alloc_kernel(struct mm_struct *mm, pmd_t *pmd, unsigned long address));
extern pte_t *FASTCALL(pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address));
extern int install_page(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot);
@@ -635,6 +636,20 @@ static inline pmd_t *pmd_alloc(struct mm
return pmd_offset(pgd, address);
}
+static inline pgd_t *pgd_alloc(struct mm_struct *mm, pml4_t *pml4, unsigned long address)
+{
+ if (pml4_none(*pml4))
+ return __pgd_alloc(mm, pml4, address);
+ return pml4_pgd_offset(pml4, address);
+}
+
+static inline pgd_t *pgd_alloc_k(struct mm_struct *mm, pml4_t *pml4, unsigned long address)
+{
+ if (pml4_none(*pml4))
+ return __pgd_alloc(mm, pml4, address);
+ return pml4_pgd_offset_k(pml4, address);
+}
+
extern void free_area_init(unsigned long * zones_size);
extern void free_area_init_node(int nid, pg_data_t *pgdat,
unsigned long * zones_size, unsigned long zone_start_pfn,
diff -urpN -X ../KDIFX linux-2.6.10rc1/include/linux/sched.h linux-2.6.10rc1-4level/include/linux/sched.h
--- linux-2.6.10rc1/include/linux/sched.h 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/include/linux/sched.h 2004-10-25 04:48:10.000000000 +0200
@@ -211,7 +211,7 @@ struct mm_struct {
void (*unmap_area) (struct vm_area_struct *area);
unsigned long mmap_base; /* base of mmap area */
unsigned long free_area_cache; /* first hole */
- pgd_t * pgd;
+ pml4_t * pml4;
atomic_t mm_users; /* How many users with user space? */
atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
int map_count; /* number of VMAs */
diff -urpN -X ../KDIFX linux-2.6.10rc1/kernel/fork.c linux-2.6.10rc1-4level/kernel/fork.c
--- linux-2.6.10rc1/kernel/fork.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/kernel/fork.c 2004-10-25 04:48:10.000000000 +0200
@@ -261,15 +261,15 @@ fail_nomem:
static inline int mm_alloc_pgd(struct mm_struct * mm)
{
- mm->pgd = pgd_alloc(mm);
- if (unlikely(!mm->pgd))
+ mm->pml4 = pml4_alloc(mm);
+ if (unlikely(!mm->pml4))
return -ENOMEM;
return 0;
}
static inline void mm_free_pgd(struct mm_struct * mm)
{
- pgd_free(mm->pgd);
+ pml4_free(mm->pml4);
}
#else
#define dup_mmap(mm, oldmm) (0)
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/fremap.c linux-2.6.10rc1-4level/mm/fremap.c
--- linux-2.6.10rc1/mm/fremap.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/fremap.c 2004-10-25 04:48:10.000000000 +0200
@@ -64,9 +64,11 @@ int install_page(struct mm_struct *mm, s
pmd_t *pmd;
pte_t pte_val;
- pgd = pgd_offset(mm, addr);
spin_lock(&mm->page_table_lock);
-
+ pgd = pgd_alloc(mm, pml4_offset(mm, addr), addr);
+ if (!pgd)
+ goto err_unlock;
+
pmd = pmd_alloc(mm, pgd, addr);
if (!pmd)
goto err_unlock;
@@ -111,13 +113,18 @@ int install_file_pte(struct mm_struct *m
unsigned long addr, unsigned long pgoff, pgprot_t prot)
{
int err = -ENOMEM;
+ pml4_t *pml4;
pte_t *pte;
pgd_t *pgd;
pmd_t *pmd;
pte_t pte_val;
- pgd = pgd_offset(mm, addr);
- spin_lock(&mm->page_table_lock);
+ pml4 = pml4_offset(mm, addr);
+
+ spin_lock(&mm->page_table_lock);
+ pgd = pgd_alloc(mm, pml4, addr);
+ if (!pgd)
+ goto err_unlock;
pmd = pmd_alloc(mm, pgd, addr);
if (!pmd)
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/memory.c linux-2.6.10rc1-4level/mm/memory.c
--- linux-2.6.10rc1/mm/memory.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/memory.c 2004-10-25 05:17:14.000000000 +0200
@@ -34,6 +34,8 @@
*
* 16.07.99 - Support of BIGMEM added by Gerhard Wichert, Siemens AG
* (Gerhard.Wichert@pdb.siemens.de)
+ *
+ * Aug/Sep 2004 Changed to four level page tables (Andi Kleen)
*/
#include <linux/kernel_stat.h>
@@ -137,21 +139,62 @@ static inline void free_one_pgd(struct m
pmd_free_tlb(tlb, pmd);
}
+static inline void free_one_pml4(struct mmu_gather *tlb, pml4_t *pml4,
+ unsigned long addr, unsigned long end)
+{
+ int free;
+ pgd_t *pgd, *pgd_start;
+
+ if (pml4_none(*pml4))
+ return;
+ if (unlikely(pml4_bad(*pml4))) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return;
+ }
+ BUG_ON(addr & ~PGDIR_MASK);
+ BUG_ON(end & ~PGDIR_MASK);
+ pgd_start = pml4_pgd_offset(pml4, addr);
+ free = 0;
+ pgd = pgd_start;
+ do {
+ free_one_pgd(tlb, pgd);
+ free++;
+ pgd++;
+ if (pgd - pgd_start >= PTRS_PER_PGD)
+ break;
+ addr = (addr + PGDIR_SIZE) & PGDIR_MASK;
+ } while (addr && addr < end);
+ /* Could keep a counter in the struct page here too to handle
+ partials unmaps better. */
+ if (free == PTRS_PER_PGD) {
+ pml4_clear(pml4);
+ pgd_free_tlb(tlb, pgd_start);
+ }
+}
+
/*
- * This function clears all user-level page tables of a process - this
- * is needed by execve(), so that old pages aren't in the way.
+ * This function clears user-level page tables of a process.
*
* Must be called with pagetable lock held.
+ *
+ * This function is not exact and may clear less than the range if
+ * addr and end are not suitably aligned.
*/
-void clear_page_tables(struct mmu_gather *tlb, unsigned long first, int nr)
+void clear_page_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end)
{
- pgd_t * page_dir = tlb->mm->pgd;
+ int i;
+ pml4_t *pml4 = tlb->mm->pml4;
+ unsigned long next;
- page_dir += first;
- do {
- free_one_pgd(tlb, page_dir);
- page_dir++;
- } while (--nr);
+ for (i = pml4_index(addr); i <= pml4_index(end-1); i++) {
+ next = (addr + PML4_SIZE) & PML4_MASK;
+ if (next > end || next <= addr)
+ next = end;
+ if (!(addr & ~PML4_MASK))
+ free_one_pml4(tlb, pml4 + i, addr, next);
+ addr = next;
+ }
}
pte_t fastcall * pte_alloc_map(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
@@ -164,6 +207,7 @@ pte_t fastcall * pte_alloc_map(struct mm
spin_lock(&mm->page_table_lock);
if (!new)
return NULL;
+
/*
* Because we dropped the lock, we should re-check the
* entry, as somebody else could have populated it..
@@ -204,164 +248,210 @@ pte_t fastcall * pte_alloc_kernel(struct
out:
return pte_offset_kernel(pmd, address);
}
-#define PTE_TABLE_MASK ((PTRS_PER_PTE-1) * sizeof(pte_t))
-#define PMD_TABLE_MASK ((PTRS_PER_PMD-1) * sizeof(pmd_t))
/*
* copy one vm_area from one task to the other. Assumes the page tables
* already present in the new task to be cleared in the whole range
* covered by this vma.
- *
- * 08Jan98 Merged into one routine from several inline routines to reduce
- * variable count and make things faster. -jj
- *
+ *
* dst->page_table_lock is held on entry and exit,
- * but may be dropped within pmd_alloc() and pte_alloc_map().
+ * but may be dropped within p[mg]d_alloc() and pte_alloc_map().
*/
-int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
- struct vm_area_struct *vma)
-{
- pgd_t * src_pgd, * dst_pgd;
- unsigned long address = vma->vm_start;
- unsigned long end = vma->vm_end;
- unsigned long cow;
-
- if (is_vm_hugetlb_page(vma))
- return copy_hugetlb_page_range(dst, src, vma);
- cow = (vma->vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
- src_pgd = pgd_offset(src, address)-1;
- dst_pgd = pgd_offset(dst, address)-1;
+static inline void
+copy_swap_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, pte_t pte)
+{
+ if (pte_file(pte))
+ return;
+ swap_duplicate(pte_to_swp_entry(pte));
+ if (list_empty(&dst_mm->mmlist)) {
+ spin_lock(&mmlist_lock);
+ list_add(&dst_mm->mmlist, &src_mm->mmlist);
+ spin_unlock(&mmlist_lock);
+ }
+}
+
+static inline void
+copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ pte_t *dst_pte, pte_t *src_pte, unsigned long vm_flags,
+ unsigned long addr)
+{
+ pte_t pte = *src_pte;
+ struct page *page;
+ unsigned long pfn;
+
+ /* pte contains position in swap, so copy. */
+ if (!pte_present(pte)) {
+ copy_swap_pte(dst_mm, src_mm, pte);
+ set_pte(dst_pte, pte);
+ return;
+ }
+ pfn = pte_pfn(pte);
+ /* the pte points outside of valid memory, the
+ * mapping is assumed to be good, meaningful
+ * and not mapped via rmap - duplicate the
+ * mapping as is.
+ */
+ page = NULL;
+ if (pfn_valid(pfn))
+ page = pfn_to_page(pfn);
+
+ if (!page || PageReserved(page)) {
+ set_pte(dst_pte, pte);
+ return;
+ }
+
+ /*
+ * If it's a COW mapping, write protect it both
+ * in the parent and the child
+ */
+ if ((vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE) {
+ ptep_set_wrprotect(src_pte);
+ pte = *src_pte;
+ }
+
+ /*
+ * If it's a shared mapping, mark it clean in
+ * the child
+ */
+ if (vm_flags & VM_SHARED)
+ pte = pte_mkclean(pte);
+ pte = pte_mkold(pte);
+ get_page(page);
+ dst_mm->rss++;
+ set_pte(dst_pte, pte);
+ page_dup_rmap(page);
+}
+
+static int copy_pte_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ pmd_t *dst_pmd, pmd_t *src_pmd, struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+{
+ pte_t * src_pte, * dst_pte;
+ unsigned long vm_flags = vma->vm_flags;
+
+ dst_pte = pte_alloc_map(dst_mm, dst_pmd, addr);
+ if (!dst_pte)
+ return -ENOMEM;
+
+ spin_lock(&src_mm->page_table_lock);
+ src_pte = pte_offset_map_nested(src_pmd, addr);
+ for (;
+ addr < end;
+ addr += PAGE_SIZE, src_pte++, dst_pte++) {
+ if (pte_none(*src_pte))
+ continue;
+ copy_one_pte(dst_mm, src_mm, dst_pte, src_pte, vm_flags, addr);
+ }
+ pte_unmap_nested(src_pte);
+ pte_unmap(dst_pte);
+ spin_unlock(&src_mm->page_table_lock);
+ cond_resched_lock(&dst_mm->page_table_lock);
+ return 0;
+}
- for (;;) {
- pmd_t * src_pmd, * dst_pmd;
+static int copy_pmd_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ pgd_t *dst_pgd, pgd_t *src_pgd, struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+{
+ pmd_t *src_pmd, *dst_pmd;
+ int err = 0;
+ unsigned long next;
+
+ src_pmd = pmd_offset(src_pgd, addr);
+ dst_pmd = pmd_alloc(dst_mm, dst_pgd, addr);
+ if (!dst_pmd)
+ return -ENOMEM;
+
+ for (; addr < end; addr = next, src_pmd++, dst_pmd++) {
+ next = (addr + PMD_SIZE) & PMD_MASK;
+ if (next > end)
+ next = end;
+ if (pmd_none(*src_pmd))
+ continue;
+ if (pmd_bad(*src_pmd)) {
+ pmd_ERROR(*src_pmd);
+ pmd_clear(src_pmd);
+ continue;
+ }
+ err = copy_pte_range(dst_mm, src_mm, dst_pmd, src_pmd, vma, addr, next);
+ if (err)
+ break;
+ }
+ return err;
+}
- src_pgd++; dst_pgd++;
-
- /* copy_pmd_range */
-
+static int copy_pgd_range(struct mm_struct *dst_mm, struct mm_struct *src_mm,
+ pml4_t *dst, pml4_t *src, struct vm_area_struct *vma,
+ unsigned long addr, unsigned long end)
+{
+ pgd_t *src_pgd, *dst_pgd;
+ int err = 0;
+ unsigned long next;
+
+ src_pgd = pml4_pgd_offset(src, addr);
+ dst_pgd = pgd_alloc(dst_mm, dst, addr);
+ if (!dst_pgd)
+ return -ENOMEM;
+
+ for (; addr < end; addr = next, src_pgd++, dst_pgd++) {
+ next = (addr + PGDIR_SIZE) & PGDIR_MASK;
+ if (next > end)
+ next = end;
if (pgd_none(*src_pgd))
- goto skip_copy_pmd_range;
- if (unlikely(pgd_bad(*src_pgd))) {
+ continue;
+ if (pgd_bad(*src_pgd)) {
pgd_ERROR(*src_pgd);
pgd_clear(src_pgd);
-skip_copy_pmd_range: address = (address + PGDIR_SIZE) & PGDIR_MASK;
- if (!address || (address >= end))
- goto out;
continue;
}
+ err = copy_pmd_range(dst_mm, src_mm, dst_pgd, src_pgd, vma,
+ addr, next);
+ if (err)
+ break;
+ }
+ return err;
+}
- src_pmd = pmd_offset(src_pgd, address);
- dst_pmd = pmd_alloc(dst, dst_pgd, address);
- if (!dst_pmd)
- goto nomem;
-
- do {
- pte_t * src_pte, * dst_pte;
-
- /* copy_pte_range */
-
- if (pmd_none(*src_pmd))
- goto skip_copy_pte_range;
- if (unlikely(pmd_bad(*src_pmd))) {
- pmd_ERROR(*src_pmd);
- pmd_clear(src_pmd);
-skip_copy_pte_range:
- address = (address + PMD_SIZE) & PMD_MASK;
- if (address >= end)
- goto out;
- goto cont_copy_pmd_range;
- }
+int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma)
+{
+ pml4_t *src_pml4, *dst_pml4;
+ unsigned long addr, start, end, next;
+ int err = 0;
+ int i;
- dst_pte = pte_alloc_map(dst, dst_pmd, address);
- if (!dst_pte)
- goto nomem;
- spin_lock(&src->page_table_lock);
- src_pte = pte_offset_map_nested(src_pmd, address);
- do {
- pte_t pte = *src_pte;
- struct page *page;
- unsigned long pfn;
-
- /* copy_one_pte */
-
- if (pte_none(pte))
- goto cont_copy_pte_range_noset;
- /* pte contains position in swap, so copy. */
- if (!pte_present(pte)) {
- if (!pte_file(pte)) {
- swap_duplicate(pte_to_swp_entry(pte));
- if (list_empty(&dst->mmlist)) {
- spin_lock(&mmlist_lock);
- list_add(&dst->mmlist,
- &src->mmlist);
- spin_unlock(&mmlist_lock);
- }
- }
- set_pte(dst_pte, pte);
- goto cont_copy_pte_range_noset;
- }
- pfn = pte_pfn(pte);
- /* the pte points outside of valid memory, the
- * mapping is assumed to be good, meaningful
- * and not mapped via rmap - duplicate the
- * mapping as is.
- */
- page = NULL;
- if (pfn_valid(pfn))
- page = pfn_to_page(pfn);
-
- if (!page || PageReserved(page)) {
- set_pte(dst_pte, pte);
- goto cont_copy_pte_range_noset;
- }
+ if (is_vm_hugetlb_page(vma))
+ return copy_hugetlb_page_range(dst, src, vma);
- /*
- * If it's a COW mapping, write protect it both
- * in the parent and the child
- */
- if (cow) {
- ptep_set_wrprotect(src_pte);
- pte = *src_pte;
- }
+ start = vma->vm_start;
+ src_pml4 = pml4_offset(src, start);
+ dst_pml4 = pml4_offset(dst, start);
+
+ end = vma->vm_end;
+ addr = start;
+ /* Adding PML4_SIZE can wrap on ports with less than 4 levels. */
+ for (i = pml4_index(addr);
+ i <= pml4_index(end-1);
+ i++, addr = next, src_pml4++, dst_pml4++) {
+ next = (addr + PML4_SIZE) & PML4_MASK;
+ if (next > end || next <= addr)
+ next = end;
+ if (pml4_none(*src_pml4))
+ continue;
+ if (pml4_bad(*src_pml4)) {
+ pml4_ERROR(*src_pml4);
+ pml4_clear(src_pml4);
+ continue;
+ }
+ err = copy_pgd_range(dst, src,
+ dst_pml4, src_pml4, vma, addr,
+ next);
+ if (err)
+ break;
+ }
- /*
- * If it's a shared mapping, mark it clean in
- * the child
- */
- if (vma->vm_flags & VM_SHARED)
- pte = pte_mkclean(pte);
- pte = pte_mkold(pte);
- get_page(page);
- dst->rss++;
- set_pte(dst_pte, pte);
- page_dup_rmap(page);
-cont_copy_pte_range_noset:
- address += PAGE_SIZE;
- if (address >= end) {
- pte_unmap_nested(src_pte);
- pte_unmap(dst_pte);
- goto out_unlock;
- }
- src_pte++;
- dst_pte++;
- } while ((unsigned long)src_pte & PTE_TABLE_MASK);
- pte_unmap_nested(src_pte-1);
- pte_unmap(dst_pte-1);
- spin_unlock(&src->page_table_lock);
- cond_resched_lock(&dst->page_table_lock);
-cont_copy_pmd_range:
- src_pmd++;
- dst_pmd++;
- } while ((unsigned long)src_pmd & PMD_TABLE_MASK);
- }
-out_unlock:
- spin_unlock(&src->page_table_lock);
-out:
- return 0;
-nomem:
- return -ENOMEM;
-}
+ return err;
+}
static void zap_pte_range(struct mmu_gather *tlb,
pmd_t *pmd, unsigned long address,
@@ -469,20 +559,46 @@ static void zap_pmd_range(struct mmu_gat
} while (address && (address < end));
}
+static void zap_pgd_range(struct mmu_gather *tlb,
+ pml4_t *pml4, unsigned long address,
+ unsigned long end, struct zap_details *details)
+{
+ pgd_t * pgd;
+
+ if (pml4_none(*pml4))
+ return;
+ if (unlikely(pml4_bad(*pml4))) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return;
+ }
+ pgd = pml4_pgd_offset(pml4, address);
+ do {
+ zap_pmd_range(tlb, pgd, address, end - address, details);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+}
+
static void unmap_page_range(struct mmu_gather *tlb,
struct vm_area_struct *vma, unsigned long address,
unsigned long end, struct zap_details *details)
{
- pgd_t * dir;
+ unsigned long next;
+ pml4_t *pml4;
+ int i;
BUG_ON(address >= end);
- dir = pgd_offset(vma->vm_mm, address);
+ pml4 = pml4_offset(vma->vm_mm, address);
tlb_start_vma(tlb, vma);
- do {
- zap_pmd_range(tlb, dir, address, end - address, details);
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (address && (address < end));
+ for (i = pml4_index(address); i <= pml4_index(end-1); i++) {
+ next = (address + PML4_SIZE) & PML4_MASK;
+ if (next <= address || next > end)
+ next = end;
+ zap_pgd_range(tlb, pml4, address, next, details);
+ address = next;
+ pml4++;
+ }
tlb_end_vma(tlb, vma);
}
@@ -623,6 +739,7 @@ void zap_page_range(struct vm_area_struc
struct page *
follow_page(struct mm_struct *mm, unsigned long address, int write)
{
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
pte_t *ptep, pte;
@@ -633,7 +750,16 @@ follow_page(struct mm_struct *mm, unsign
if (! IS_ERR(page))
return page;
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ goto out;
+ }
+ if (pml4_none(*pml4))
+ goto out;
+
+ pgd = pml4_pgd_offset(pml4, address);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
goto out;
@@ -686,6 +812,7 @@ static inline int
untouched_anonymous_page(struct mm_struct* mm, struct vm_area_struct *vma,
unsigned long address)
{
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
@@ -694,7 +821,16 @@ untouched_anonymous_page(struct mm_struc
return 0;
/* Check if page directory entry exists. */
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return 1;
+ }
+ if (pml4_none(*pml4))
+ return 1;
+
+ pgd = pml4_pgd_offset(pml4, address);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
return 1;
@@ -733,17 +869,21 @@ int get_user_pages(struct task_struct *t
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
+ pml4_t *pml4;
if (write) /* user gate pages are read-only */
return i ? : -EFAULT;
- pgd = pgd_offset_gate(mm, pg);
- if (!pgd)
+ pml4 = pml4_offset_gate(mm, pg);
+ /* AK: BUG_ON would be more appropiate for the
+ _none checks below. */
+ if (pml4_none(*pml4))
+ return i ? : -EFAULT;
+ pgd = pml4_pgd_offset(pml4, pg);
+ if (pgd_none(*pgd))
return i ? : -EFAULT;
pmd = pmd_offset(pgd, pg);
- if (!pmd)
+ if (pmd_none(*pmd))
return i ? : -EFAULT;
pte = pte_offset_map(pmd, pg);
- if (!pte)
- return i ? : -EFAULT;
if (!pte_present(*pte)) {
pte_unmap(pte);
return i ? : -EFAULT;
@@ -879,31 +1019,56 @@ static inline int zeromap_pmd_range(stru
return 0;
}
+static inline int zeromap_pgd_range(struct mm_struct *mm, pgd_t * pgd,
+ unsigned long address,
+ unsigned long size, pgprot_t prot)
+{
+ unsigned long base, end;
+
+ base = address & PML4_MASK;
+ address &= ~PML4_MASK;
+ end = address + size;
+ do {
+ pmd_t * pmd = pmd_alloc(mm, pgd, base + address);
+ if (!pgd)
+ return -ENOMEM;
+ zeromap_pmd_range(mm, pmd, address, end - address, prot);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+ return 0;
+}
+
int zeromap_page_range(struct vm_area_struct *vma, unsigned long address, unsigned long size, pgprot_t prot)
{
+ int i;
int error = 0;
- pgd_t * dir;
+ pml4_t *pml4;
unsigned long beg = address;
unsigned long end = address + size;
+ unsigned long next;
struct mm_struct *mm = vma->vm_mm;
- dir = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
flush_cache_range(vma, beg, end);
- if (address >= end)
- BUG();
+ BUG_ON(address >= end);
+ BUG_ON(end > vma->vm_end);
spin_lock(&mm->page_table_lock);
- do {
- pmd_t *pmd = pmd_alloc(mm, dir, address);
+ for (i = pml4_index(address); i <= pml4_index(end-1); i++) {
+ pgd_t *pgd = pgd_alloc(mm, pml4, address);
error = -ENOMEM;
- if (!pmd)
+ if (!pgd)
break;
- error = zeromap_pmd_range(mm, pmd, address, end - address, prot);
+ next = (address + PML4_SIZE) & PML4_MASK;
+ if (next <= beg || next > end)
+ next = end;
+ error = zeromap_pgd_range(mm, pgd, address, next - address, prot);
if (error)
break;
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (address && (address < end));
+ address = next;
+ pml4++;
+ }
/*
* Why flush? zeromap_pte_range has a BUG_ON for !pte_none()
*/
@@ -917,8 +1082,9 @@ int zeromap_page_range(struct vm_area_st
* mappings are removed. any references to nonexistent pages results
* in null mappings (currently treated as "copy-on-access")
*/
-static inline void remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
- unsigned long pfn, pgprot_t prot)
+static inline void
+remap_pte_range(pte_t * pte, unsigned long address, unsigned long size,
+ unsigned long pfn, pgprot_t prot)
{
unsigned long end;
@@ -936,8 +1102,9 @@ static inline void remap_pte_range(pte_t
} while (address && (address < end));
}
-static inline int remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size,
- unsigned long pfn, pgprot_t prot)
+static inline int
+remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address,
+ unsigned long size, unsigned long pfn, pgprot_t prot)
{
unsigned long base, end;
@@ -946,12 +1113,13 @@ static inline int remap_pmd_range(struct
end = address + size;
if (end > PGDIR_SIZE)
end = PGDIR_SIZE;
- pfn -= address >> PAGE_SHIFT;
+ pfn -= (address >> PAGE_SHIFT);
do {
pte_t * pte = pte_alloc_map(mm, pmd, base + address);
if (!pte)
return -ENOMEM;
- remap_pte_range(pte, base + address, end - address, pfn + (address >> PAGE_SHIFT), prot);
+ remap_pte_range(pte, base + address, end - address,
+ (address >> PAGE_SHIFT) + pfn, prot);
pte_unmap(pte);
address = (address + PMD_SIZE) & PMD_MASK;
pmd++;
@@ -959,17 +1127,43 @@ static inline int remap_pmd_range(struct
return 0;
}
+static inline int remap_pgd_range(struct mm_struct *mm, pgd_t * pgd,
+ unsigned long address, unsigned long size,
+ unsigned long pfn, pgprot_t prot)
+{
+ unsigned long base, end;
+
+ base = address & PML4_MASK;
+ address &= ~PML4_MASK;
+ end = address + size;
+ pfn -= address >> PAGE_SHIFT;
+ do {
+ pmd_t *pmd = pmd_alloc(mm, pgd, base+address);
+ if (!pmd)
+ return -ENOMEM;
+ remap_pmd_range(mm, pmd, base + address, end - address,
+ (address >> PAGE_SHIFT) + pfn, prot);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+ return 0;
+}
+
/* Note: this is only safe if the mm semaphore is held when called. */
-int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot)
+int remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
+ unsigned long pfn, unsigned long size,
+ pgprot_t prot)
{
int error = 0;
- pgd_t * dir;
+ pml4_t *pml4;
unsigned long beg = from;
unsigned long end = from + size;
+ unsigned long next;
struct mm_struct *mm = vma->vm_mm;
+ int i;
pfn -= from >> PAGE_SHIFT;
- dir = pgd_offset(mm, from);
+ pml4 = pml4_offset(mm, from);
flush_cache_range(vma, beg, end);
if (from >= end)
BUG();
@@ -983,25 +1177,32 @@ int remap_pfn_range(struct vm_area_struc
* this region.
*/
vma->vm_flags |= VM_IO | VM_RESERVED;
+
spin_lock(&mm->page_table_lock);
- do {
- pmd_t *pmd = pmd_alloc(mm, dir, from);
+ for (i = pml4_index(beg); i <= pml4_index(end-1); i++) {
+ pgd_t *pgd = pgd_alloc(mm, pml4, from);
error = -ENOMEM;
- if (!pmd)
+ if (!pgd)
break;
- error = remap_pmd_range(mm, pmd, from, end - from, pfn + (from >> PAGE_SHIFT), prot);
+ next = (from + PML4_SIZE) & PML4_MASK;
+ if (next > end || next <= from)
+ next = end;
+ error = remap_pgd_range(mm, pgd, from, end - from,
+ pfn + (from >> PAGE_SHIFT), prot);
if (error)
break;
- from = (from + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (from && (from < end));
+ from = next;
+ pml4++;
+ }
/*
* Why flush? remap_pte_range has a BUG_ON for !pte_none()
*/
flush_tlb_range(vma, beg, end);
spin_unlock(&mm->page_table_lock);
+
return error;
}
+
EXPORT_SYMBOL(remap_pfn_range);
/*
@@ -1693,11 +1894,11 @@ static inline int handle_pte_fault(struc
int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct * vma,
unsigned long address, int write_access)
{
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
__set_current_state(TASK_RUNNING);
- pgd = pgd_offset(mm, address);
inc_page_state(pgfault);
@@ -1708,14 +1909,21 @@ int handle_mm_fault(struct mm_struct *mm
* We need the page table lock to synchronize with kswapd
* and the SMP-safe atomic PTE updates.
*/
+ pml4 = pml4_offset(mm, address);
spin_lock(&mm->page_table_lock);
- pmd = pmd_alloc(mm, pgd, address);
+ pgd = pgd_alloc(mm, pml4, address);
+ if (!pgd)
+ goto oom;
+
+ pmd = pmd_alloc(mm, pgd, address);
if (pmd) {
pte_t * pte = pte_alloc_map(mm, pmd, address);
if (pte)
return handle_pte_fault(mm, vma, address, write_access, pte, pmd);
}
+
+ oom:
spin_unlock(&mm->page_table_lock);
return VM_FAULT_OOM;
}
@@ -1752,6 +1960,29 @@ out:
return pmd_offset(pgd, address);
}
+#if PTRS_PER_PML4 > 1
+pgd_t fastcall *__pgd_alloc(struct mm_struct *mm, pml4_t *pml4, unsigned long address)
+{
+ pgd_t *new;
+
+ spin_unlock(&mm->page_table_lock);
+ new = pgd_alloc_one(mm, address);
+ spin_lock(&mm->page_table_lock);
+ if (!new)
+ return NULL;
+
+ /*
+ * Because we dropped the lock, we should re-check the
+ * entry, as somebody else could have populated it..
+ */
+ if (pml4_present(*pml4))
+ pgd_free(new);
+ else
+ pml4_populate(mm, pml4, new);
+ return pml4_pgd_offset(pml4, address);
+}
+#endif
+
int make_pages_present(unsigned long addr, unsigned long end)
{
int ret, len, write;
@@ -1780,10 +2011,15 @@ struct page * vmalloc_to_page(void * vma
{
unsigned long addr = (unsigned long) vmalloc_addr;
struct page *page = NULL;
- pgd_t *pgd = pgd_offset_k(addr);
+ pml4_t *pml4 = pml4_offset_k(addr);
+ pgd_t *pgd;
pmd_t *pmd;
pte_t *ptep, pte;
+ if (pml4_none(*pml4))
+ return NULL;
+ pgd = pml4_pgd_offset_k(pml4, addr);
+
if (!pgd_none(*pgd)) {
pmd = pmd_offset(pgd, addr);
if (!pmd_none(*pmd)) {
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/mempolicy.c linux-2.6.10rc1-4level/mm/mempolicy.c
--- linux-2.6.10rc1/mm/mempolicy.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/mempolicy.c 2004-10-25 04:48:10.000000000 +0200
@@ -233,13 +233,24 @@ static struct mempolicy *mpol_new(int mo
/* Ensure all existing pages follow the policy. */
static int
-verify_pages(unsigned long addr, unsigned long end, unsigned long *nodes)
+verify_pages(struct mm_struct *mm,
+ unsigned long addr, unsigned long end, unsigned long *nodes)
{
while (addr < end) {
struct page *p;
pte_t *pte;
pmd_t *pmd;
- pgd_t *pgd = pgd_offset_k(addr);
+ pgd_t *pgd;
+ pml4_t *pml4;
+ pml4 = pml4_offset(mm, addr);
+ if (pml4_none(*pml4)) {
+ unsigned long next = (addr + PML4_SIZE) & PML4_MASK;
+ if (next > addr)
+ break;
+ addr = next;
+ continue;
+ }
+ pgd = pml4_pgd_offset(pml4, addr);
if (pgd_none(*pgd)) {
addr = (addr + PGDIR_SIZE) & PGDIR_MASK;
continue;
@@ -282,7 +293,8 @@ check_range(struct mm_struct *mm, unsign
if (prev && prev->vm_end < vma->vm_start)
return ERR_PTR(-EFAULT);
if ((flags & MPOL_MF_STRICT) && !is_vm_hugetlb_page(vma)) {
- err = verify_pages(vma->vm_start, vma->vm_end, nodes);
+ err = verify_pages(vma->vm_mm,
+ vma->vm_start, vma->vm_end, nodes);
if (err) {
first = ERR_PTR(err);
break;
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/mmap.c linux-2.6.10rc1-4level/mm/mmap.c
--- linux-2.6.10rc1/mm/mmap.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/mmap.c 2004-10-25 04:48:10.000000000 +0200
@@ -1459,7 +1459,7 @@ find_extend_vma(struct mm_struct * mm, u
* the page tables themselves.
*
* Right now we try to free page tables if we have a nice
- * PGDIR-aligned area that got free'd up. We could be more
+ * PGD/PML4-aligned area that got free'd up. We could be more
* granular if we want to, but this is fast and simple,
* and covers the bad cases.
*
@@ -1471,7 +1471,7 @@ static void free_pgtables(struct mmu_gat
{
unsigned long first = start & PGDIR_MASK;
unsigned long last = end + PGDIR_SIZE - 1;
- unsigned long start_index, end_index;
+ unsigned long start_pml4_index, start_pgd_index;
struct mm_struct *mm = tlb->mm;
if (!prev) {
@@ -1495,24 +1495,26 @@ static void free_pgtables(struct mmu_gat
if (last > next->vm_start)
last = next->vm_start;
}
- if (prev->vm_end > first)
- first = prev->vm_end + PGDIR_SIZE - 1;
+ if (prev->vm_end > first) {
+ first = prev->vm_end + PML4_SIZE - 1;
+ if (first < prev->vm_end)
+ first = TASK_SIZE;
+ }
break;
}
no_mmaps:
- if (last < first) /* for arches with discontiguous pgd indices */
+ if (last < first) /* for arches with discontiguous indices */
return;
- /*
- * If the PGD bits are not consecutive in the virtual address, the
- * old method of shifting the VA >> by PGDIR_SHIFT doesn't work.
- */
- start_index = pgd_index(first);
- if (start_index < FIRST_USER_PGD_NR)
- start_index = FIRST_USER_PGD_NR;
- end_index = pgd_index(last);
- if (end_index > start_index) {
- clear_page_tables(tlb, start_index, end_index - start_index);
- flush_tlb_pgtables(mm, first & PGDIR_MASK, last & PGDIR_MASK);
+ start_pml4_index = pml4_index(first);
+ start_pgd_index = pgd_index(first);
+ if (start_pml4_index == 0 && start_pgd_index < FIRST_USER_PGD_NR) {
+ start_pgd_index = FIRST_USER_PGD_NR;
+ first = start_pgd_index * PGDIR_SIZE;
+ }
+ if (pml4_index(last) > start_pml4_index ||
+ pgd_index(last) > start_pgd_index) {
+ clear_page_range(tlb, first, last);
+ flush_tlb_pgtables(mm, first & PML4_MASK, last & PML4_MASK);
}
}
@@ -1841,7 +1843,8 @@ void exit_mmap(struct mm_struct *mm)
~0UL, &nr_accounted, NULL);
vm_unacct_memory(nr_accounted);
BUG_ON(mm->map_count); /* This is just debugging */
- clear_page_tables(tlb, FIRST_USER_PGD_NR, USER_PTRS_PER_PGD);
+ clear_page_range(tlb, FIRST_USER_PGD_NR * PGDIR_SIZE,
+ (TASK_SIZE + PGDIR_SIZE - 1) & ~(PGDIR_SIZE - 1));
tlb_finish_mmu(tlb, 0, MM_VM_SIZE(mm));
vma = mm->mmap;
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/mprotect.c linux-2.6.10rc1-4level/mm/mprotect.c
--- linux-2.6.10rc1/mm/mprotect.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/mprotect.c 2004-10-25 04:48:10.000000000 +0200
@@ -87,23 +87,51 @@ change_pmd_range(pgd_t *pgd, unsigned lo
} while (address && (address < end));
}
+static inline void
+change_pgd_range(pml4_t *pml4, unsigned long address,
+ unsigned long size, pgprot_t newprot)
+{
+ pgd_t * pgd;
+ unsigned long end;
+
+ if (pml4_none(*pml4))
+ return;
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return;
+ }
+ pgd = pml4_pgd_offset(pml4, address);
+ address &= ~PML4_MASK;
+ end = address + size;
+ do {
+ change_pmd_range(pgd, address, end - address, newprot);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+}
+
static void
change_protection(struct vm_area_struct *vma, unsigned long start,
unsigned long end, pgprot_t newprot)
{
- pgd_t *dir;
- unsigned long beg = start;
+ pml4_t *pml4;
+ unsigned long beg = start, next;
+ int i;
- dir = pgd_offset(current->mm, start);
+ pml4 = pml4_offset(current->mm, start);
flush_cache_range(vma, beg, end);
if (start >= end)
BUG();
spin_lock(¤t->mm->page_table_lock);
- do {
- change_pmd_range(dir, start, end - start, newprot);
- start = (start + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (start && (start < end));
+ for (i = pml4_index(start); i <= pml4_index(end-1); i++) {
+ next = (start + PML4_SIZE) & PML4_MASK;
+ if (next <= start || next > end)
+ next = end;
+ change_pgd_range(pml4, start, next - start, newprot);
+ start = next;
+ pml4++;
+ }
flush_tlb_range(vma, beg, end);
spin_unlock(¤t->mm->page_table_lock);
return;
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/mremap.c linux-2.6.10rc1-4level/mm/mremap.c
--- linux-2.6.10rc1/mm/mremap.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/mremap.c 2004-10-25 04:48:10.000000000 +0200
@@ -24,11 +24,16 @@
static pte_t *get_one_pte_map_nested(struct mm_struct *mm, unsigned long addr)
{
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte = NULL;
- pgd = pgd_offset(mm, addr);
+ pml4 = pml4_offset(mm, addr);
+ if (pml4_none(*pml4))
+ goto end;
+
+ pgd = pml4_pgd_offset(pml4, addr);
if (pgd_none(*pgd))
goto end;
if (pgd_bad(*pgd)) {
@@ -57,10 +62,15 @@ end:
static pte_t *get_one_pte_map(struct mm_struct *mm, unsigned long addr)
{
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
- pgd = pgd_offset(mm, addr);
+ pml4 = pml4_offset(mm, addr);
+ if (pml4_none(*pml4))
+ return NULL;
+
+ pgd = pml4_pgd_offset(pml4, addr);
if (pgd_none(*pgd))
return NULL;
pmd = pmd_offset(pgd, addr);
@@ -71,10 +81,15 @@ static pte_t *get_one_pte_map(struct mm_
static inline pte_t *alloc_one_pte_map(struct mm_struct *mm, unsigned long addr)
{
+ pgd_t *pgd;
pmd_t *pmd;
pte_t *pte = NULL;
- pmd = pmd_alloc(mm, pgd_offset(mm, addr), addr);
+ pgd = pgd_alloc(mm, pml4_offset(mm,addr), addr);
+ if (!pgd)
+ return NULL;
+
+ pmd = pmd_alloc(mm, pgd, addr);
if (pmd)
pte = pte_alloc_map(mm, pmd, addr);
return pte;
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/msync.c linux-2.6.10rc1-4level/mm/msync.c
--- linux-2.6.10rc1/mm/msync.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/msync.c 2004-10-25 04:48:10.000000000 +0200
@@ -93,11 +93,37 @@ static inline int filemap_sync_pmd_range
return error;
}
+static inline int filemap_sync_pgd_range(pml4_t *pml4,
+ unsigned long address, unsigned long end,
+ struct vm_area_struct *vma, unsigned int flags)
+{
+ pgd_t *pgd;
+ int error;
+
+ if (pml4_none(*pml4))
+ return 0;
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return 0;
+ }
+ pgd = pml4_pgd_offset(pml4, address);
+ error = 0;
+ do {
+ error |= filemap_sync_pmd_range(pgd, address, end, vma, flags);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+ return error;
+}
+
static int filemap_sync(struct vm_area_struct * vma, unsigned long address,
size_t size, unsigned int flags)
{
- pgd_t * dir;
+ pml4_t *pml4;
unsigned long end = address + size;
+ unsigned long next;
+ int i;
int error = 0;
/* Aquire the lock early; it may be possible to avoid dropping
@@ -105,7 +131,7 @@ static int filemap_sync(struct vm_area_s
*/
spin_lock(&vma->vm_mm->page_table_lock);
- dir = pgd_offset(vma->vm_mm, address);
+ pml4 = pml4_offset(vma->vm_mm, address);
flush_cache_range(vma, address, end);
/* For hugepages we can't go walking the page table normally,
@@ -116,11 +142,14 @@ static int filemap_sync(struct vm_area_s
if (address >= end)
BUG();
- do {
- error |= filemap_sync_pmd_range(dir, address, end, vma, flags);
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (address && (address < end));
+ for (i = pml4_index(address); i <= pml4_index(end-1); i++) {
+ next = (address + PML4_SIZE) & PML4_MASK;
+ if (next <= address || next > end)
+ next = end;
+ error |= filemap_sync_pgd_range(pml4, address, next, vma, flags);
+ address = next;
+ pml4++;
+ }
/*
* Why flush ? filemap_sync_pte already flushed the tlbs with the
* dirty bits.
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/rmap.c linux-2.6.10rc1-4level/mm/rmap.c
--- linux-2.6.10rc1/mm/rmap.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/rmap.c 2004-10-25 04:48:10.000000000 +0200
@@ -258,6 +258,7 @@ static int page_referenced_one(struct pa
{
struct mm_struct *mm = vma->vm_mm;
unsigned long address;
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
@@ -271,7 +272,11 @@ static int page_referenced_one(struct pa
spin_lock(&mm->page_table_lock);
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
+ if (!pml4_present(*pml4))
+ goto out_unlock;
+
+ pgd = pml4_pgd_offset(pml4, address);
if (!pgd_present(*pgd))
goto out_unlock;
@@ -496,6 +501,7 @@ static int try_to_unmap_one(struct page
{
struct mm_struct *mm = vma->vm_mm;
unsigned long address;
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
@@ -514,7 +520,11 @@ static int try_to_unmap_one(struct page
*/
spin_lock(&mm->page_table_lock);
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
+ if (!pml4_present(*pml4))
+ goto out_unlock;
+
+ pgd = pml4_pgd_offset(pml4, address);
if (!pgd_present(*pgd))
goto out_unlock;
@@ -624,6 +634,7 @@ static void try_to_unmap_cluster(unsigne
unsigned int *mapcount, struct vm_area_struct *vma)
{
struct mm_struct *mm = vma->vm_mm;
+ pml4_t *pml4;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
@@ -646,7 +657,11 @@ static void try_to_unmap_cluster(unsigne
if (end > vma->vm_end)
end = vma->vm_end;
- pgd = pgd_offset(mm, address);
+ pml4 = pml4_offset(mm, address);
+ if (!pml4_present(*pml4))
+ goto out_unlock;
+
+ pgd = pml4_pgd_offset(pml4, address);
if (!pgd_present(*pgd))
goto out_unlock;
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/swapfile.c linux-2.6.10rc1-4level/mm/swapfile.c
--- linux-2.6.10rc1/mm/swapfile.c 2004-10-25 04:47:38.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/swapfile.c 2004-10-25 04:48:10.000000000 +0200
@@ -487,11 +487,11 @@ static unsigned long unuse_pmd(struct vm
/* vma->vm_mm->page_table_lock is held */
static unsigned long unuse_pgd(struct vm_area_struct * vma, pgd_t *dir,
- unsigned long address, unsigned long size,
+ unsigned long address, unsigned long size, unsigned long offset,
swp_entry_t entry, struct page *page)
{
pmd_t * pmd;
- unsigned long offset, end;
+ unsigned long end;
unsigned long foundaddr;
if (pgd_none(*dir))
@@ -502,7 +502,7 @@ static unsigned long unuse_pgd(struct vm
return 0;
}
pmd = pmd_offset(dir, address);
- offset = address & PGDIR_MASK;
+ offset += address & PGDIR_MASK;
address &= ~PGDIR_MASK;
end = address + size;
if (end > PGDIR_SIZE)
@@ -521,12 +521,46 @@ static unsigned long unuse_pgd(struct vm
}
/* vma->vm_mm->page_table_lock is held */
+static unsigned long unuse_pml4(struct vm_area_struct * vma, pml4_t *pml4,
+ unsigned long address, unsigned long size,
+ swp_entry_t entry, struct page *page)
+{
+ pgd_t * pgd;
+ unsigned long offset;
+ unsigned long foundaddr;
+ unsigned long end;
+
+ if (pml4_none(*pml4))
+ return 0;
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return 0;
+ }
+ pgd = pml4_pgd_offset(pml4, address);
+ offset = address & PML4_MASK;
+ address &= ~PML4_MASK;
+ end = address + size;
+ BUG_ON (address >= end);
+ do {
+ foundaddr = unuse_pgd(vma, pgd, address, end - address,
+ offset, entry, page);
+ if (foundaddr)
+ return foundaddr;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+/* vma->vm_mm->page_table_lock is held */
static unsigned long unuse_vma(struct vm_area_struct * vma,
swp_entry_t entry, struct page *page)
{
- pgd_t *pgdir;
- unsigned long start, end;
+ pml4_t *pml4;
+ unsigned long start, end, next;
unsigned long foundaddr;
+ int i;
if (page->mapping) {
start = page_address_in_vma(page, vma);
@@ -538,15 +572,18 @@ static unsigned long unuse_vma(struct vm
start = vma->vm_start;
end = vma->vm_end;
}
- pgdir = pgd_offset(vma->vm_mm, start);
- do {
- foundaddr = unuse_pgd(vma, pgdir, start, end - start,
- entry, page);
+ pml4 = pml4_offset(vma->vm_mm, start);
+ for (i = pml4_index(start); i <= pml4_index(end-1); i++) {
+ next = (start + PML4_SIZE) & PML4_MASK;
+ if (next > end || next <= start)
+ next = end;
+ foundaddr = unuse_pml4(vma, pml4, start, next - start, entry, page);
if (foundaddr)
return foundaddr;
- start = (start + PGDIR_SIZE) & PGDIR_MASK;
- pgdir++;
- } while (start && (start < end));
+ start = next;
+ i++;
+ pml4++;
+ }
return 0;
}
diff -urpN -X ../KDIFX linux-2.6.10rc1/mm/vmalloc.c linux-2.6.10rc1-4level/mm/vmalloc.c
--- linux-2.6.10rc1/mm/vmalloc.c 2004-10-19 01:55:37.000000000 +0200
+++ linux-2.6.10rc1-4level/mm/vmalloc.c 2004-10-25 04:48:10.000000000 +0200
@@ -83,6 +83,31 @@ static void unmap_area_pmd(pgd_t *dir, u
} while (address < end);
}
+static void unmap_area_pgd(pml4_t *pml4, unsigned long address,
+ unsigned long size)
+{
+ pgd_t *pgd;
+ unsigned long end;
+
+ if (pml4_none(*pml4))
+ return;
+ if (pml4_bad(*pml4)) {
+ pml4_ERROR(*pml4);
+ pml4_clear(pml4);
+ return;
+ }
+
+ pgd = pml4_pgd_offset_k(pml4, address);
+ address &= ~PML4_MASK;
+ end = address + size;
+
+ do {
+ unmap_area_pmd(pgd, address, end - address);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && (address < end));
+}
+
static int map_area_pte(pte_t *pte, unsigned long address,
unsigned long size, pgprot_t prot,
struct page ***pages)
@@ -96,7 +121,6 @@ static int map_area_pte(pte_t *pte, unsi
do {
struct page *page = **pages;
-
WARN_ON(!pte_none(*pte));
if (!page)
return -ENOMEM;
@@ -134,19 +158,41 @@ static int map_area_pmd(pmd_t *pmd, unsi
return 0;
}
+static int map_area_pgd(pgd_t *pgd, unsigned long address,
+ unsigned long end, pgprot_t prot,
+ struct page ***pages)
+{
+ do {
+ pmd_t *pmd = pmd_alloc(&init_mm, pgd, address);
+ if (!pmd)
+ return -ENOMEM;
+ if (map_area_pmd(pmd, address, end - address, prot, pages))
+ return -ENOMEM;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ } while (address && address < end);
+
+ return 0;
+}
+
void unmap_vm_area(struct vm_struct *area)
{
unsigned long address = (unsigned long) area->addr;
unsigned long end = (address + area->size);
- pgd_t *dir;
+ unsigned long next;
+ pml4_t *pml4;
+ int i;
- dir = pgd_offset_k(address);
+ pml4 = pml4_offset_k(address);
flush_cache_vunmap(address, end);
- do {
- unmap_area_pmd(dir, address, end - address);
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (address && (address < end));
+ for (i = pml4_index(address); i <= pml4_index(end-1); i++) {
+ next = (address + PML4_SIZE) & PML4_MASK;
+ if (next <= address || next > end)
+ next = end;
+ unmap_area_pgd(pml4, address, next - address);
+ address = next;
+ pml4++;
+ }
flush_tlb_kernel_range((unsigned long) area->addr, end);
}
@@ -154,25 +200,30 @@ int map_vm_area(struct vm_struct *area,
{
unsigned long address = (unsigned long) area->addr;
unsigned long end = address + (area->size-PAGE_SIZE);
- pgd_t *dir;
+ unsigned long next;
+ pml4_t *pml4;
int err = 0;
+ int i;
- dir = pgd_offset_k(address);
+ pml4 = pml4_offset_k(address);
spin_lock(&init_mm.page_table_lock);
- do {
- pmd_t *pmd = pmd_alloc(&init_mm, dir, address);
- if (!pmd) {
+ for (i = pml4_index(address); i <= pml4_index(end-1); i++) {
+ pgd_t *pgd = pgd_alloc_k(&init_mm, pml4, address);
+ if (!pgd) {
err = -ENOMEM;
break;
}
- if (map_area_pmd(pmd, address, end - address, prot, pages)) {
+ next = (address + PGDIR_SIZE) & PGDIR_MASK;
+ if (next < address || next > end)
+ next = end;
+ if (map_area_pgd(pgd, address, next, prot, pages)) {
err = -ENOMEM;
break;
}
- address = (address + PGDIR_SIZE) & PGDIR_MASK;
- dir++;
- } while (address && (address < end));
+ address = next;
+ pml4++;
+ }
spin_unlock(&init_mm.page_table_lock);
flush_cache_vmap((unsigned long) area->addr, end);
reply other threads:[~2004-10-25 7:40 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=417CAA05.mail3Y814D9ZM@wotan.suse.de \
--to=ak@suse.de \
--cc=akpm@osdl.org \
--cc=linux-kernel@vger.kernel.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®