ARM: implement highpte
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Mon, 17 Aug 2009 19:02:06 +0000 (20:02 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 17 Aug 2009 19:02:06 +0000 (20:02 +0100)
Add the ARM implementation of highpte, which allows PTE tables to be
placed in highmem.  Unfortunately, we do not offer highpte support
when support for L2 cache is enabled.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/Kconfig
arch/arm/include/asm/pgalloc.h
arch/arm/include/asm/pgtable.h
arch/arm/mm/fault.c

index aef63c8..370f477 100644 (file)
@@ -1054,6 +1054,11 @@ config HIGHMEM
 
          If unsure, say n.
 
+config HIGHPTE
+       bool "Allocate 2nd-level pagetables from highmem"
+       depends on HIGHMEM
+       depends on !OUTER_CACHE
+
 source "mm/Kconfig"
 
 config LEDS
index 3dcd64b..b12cc98 100644 (file)
@@ -36,6 +36,8 @@ extern void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd);
 #define pgd_alloc(mm)                  get_pgd_slow(mm)
 #define pgd_free(mm, pgd)              free_pgd_slow(mm, pgd)
 
+#define PGALLOC_GFP    (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
+
 /*
  * Allocate one PTE table.
  *
@@ -57,7 +59,7 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
 {
        pte_t *pte;
 
-       pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
+       pte = (pte_t *)__get_free_page(PGALLOC_GFP);
        if (pte) {
                clean_dcache_area(pte, sizeof(pte_t) * PTRS_PER_PTE);
                pte += PTRS_PER_PTE;
@@ -71,10 +73,16 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
 {
        struct page *pte;
 
-       pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
+#ifdef CONFIG_HIGHPTE
+       pte = alloc_pages(PGALLOC_GFP | __GFP_HIGHMEM, 0);
+#else
+       pte = alloc_pages(PGALLOC_GFP, 0);
+#endif
        if (pte) {
-               void *page = page_address(pte);
-               clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
+               if (!PageHighMem(pte)) {
+                       void *page = page_address(pte);
+                       clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
+               }
                pgtable_page_ctor(pte);
        }
 
index 9655bce..201ccaa 100644 (file)
@@ -262,10 +262,19 @@ extern struct page *empty_zero_page;
 #define pte_clear(mm,addr,ptep)        set_pte_ext(ptep, __pte(0), 0)
 #define pte_page(pte)          (pfn_to_page(pte_pfn(pte)))
 #define pte_offset_kernel(dir,addr)    (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_offset_map(dir,addr)       (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_offset_map_nested(dir,addr)        (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_unmap(pte)         do { } while (0)
-#define pte_unmap_nested(pte)  do { } while (0)
+
+#define pte_offset_map(dir,addr)       (__pte_map(dir, KM_PTE0) + __pte_index(addr))
+#define pte_offset_map_nested(dir,addr)        (__pte_map(dir, KM_PTE1) + __pte_index(addr))
+#define pte_unmap(pte)                 __pte_unmap(pte, KM_PTE0)
+#define pte_unmap_nested(pte)          __pte_unmap(pte, KM_PTE1)
+
+#ifndef CONFIG_HIGHPTE
+#define __pte_map(dir,km)      pmd_page_vaddr(*(dir))
+#define __pte_unmap(pte,km)    do { } while (0)
+#else
+#define __pte_map(dir,km)      ((pte_t *)kmap_atomic(pmd_page(*(dir)), km) + PTRS_PER_PTE)
+#define __pte_unmap(pte,km)    kunmap_atomic((pte - PTRS_PER_PTE), km)
+#endif
 
 #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
 
index 6fdcbb7..5fa8dea 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/page-flags.h>
+#include <linux/highmem.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>