[parisc-linux] Re: [parisc-linux-cvs] linux-2.6 jejb
James Bottomley
James.Bottomley at steeleye.com
Mon May 3 14:51:09 MDT 2004
On Sun, 2004-05-02 at 11:16, James Bottomley wrote:
> CVSROOT: /var/cvs
> Module name: linux-2.6
> Changes by: jejb 04/05/02 10:16:01
>
> Modified files:
> . : Makefile
> arch/parisc/kernel: asm-offsets.c entry.S init_task.c
> include/asm-parisc: pgalloc.h pgtable.h
>
> Log message:
> Implement L2/L3 hybrid page tables for 64 bit kernels
Index: arch/parisc/kernel/asm-offsets.c
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/asm-offsets.c,v
retrieving revision 1.7
diff -u -r1.7 asm-offsets.c
--- a/arch/parisc/kernel/asm-offsets.c 1 May 2004 20:03:11 -0000 1.7
+++ b/arch/parisc/kernel/asm-offsets.c 2 May 2004 15:53:49 -0000
@@ -282,6 +282,7 @@
DEFINE(ASM_BITS_PER_PGD, BITS_PER_PGD);
DEFINE(ASM_BITS_PER_PMD, BITS_PER_PMD);
DEFINE(ASM_BITS_PER_PTE, BITS_PER_PTE);
+ DEFINE(ASM_PGD_PMD_OFFSET, -(PAGE_SIZE << PGD_ORDER));
DEFINE(ASM_PMD_ENTRY, ((PAGE_OFFSET & PMD_MASK) >> PMD_SHIFT));
DEFINE(ASM_PGD_ENTRY, PAGE_OFFSET >> PGDIR_SHIFT);
DEFINE(ASM_PGD_ENTRY_SIZE, PGD_ENTRY_SIZE);
Index: arch/parisc/kernel/entry.S
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/entry.S,v
retrieving revision 1.13
diff -u -r1.13 entry.S
--- a/arch/parisc/kernel/entry.S 1 May 2004 20:03:11 -0000 1.13
+++ b/arch/parisc/kernel/entry.S 2 May 2004 15:53:51 -0000
@@ -474,11 +474,24 @@
bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault
.endm
- /* Look up PTE in a 3-Level scheme */
+ /* Look up PTE in a 3-Level scheme.
+ *
+ * Here we implement a Hybrid L2/L3 scheme: we allocate the
+ * first pmd adjacent to the pgd. This means that we can
+ * subtract a constant offset to get to it. The pmd and pgd
+ * sizes are arranged so that a single pmd covers 4GB (giving
+ * a full LP64 process access to 8TB) so our lookups are
+ * effectively L2 for the first 4GB of the kernel (i.e. for
+ * all ILP32 processes and all the kernel for machines with
+ * under 4GB of memory) */
.macro L3_ptep pgd,pte,index,va,fault
extrd,u \va,63-ASM_PGDIR_SHIFT,ASM_BITS_PER_PGD,\index
copy %r0,\pte
+ extrd,u,*= \va,31,32,%r0
ldw,s \index(\pgd),\pgd
+ extrd,u,*<> \va,31,32,%r0
+ ldo ASM_PGD_PMD_OFFSET(\pgd),\pgd
+ extrd,u,*= \va,31,32,%r0
bb,>=,n \pgd,_PAGE_PRESENT_BIT,\fault
L2_ptep \pgd,\pte,\index,\va,\fault
.endm
Index: arch/parisc/kernel/init_task.c
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/init_task.c,v
retrieving revision 1.5
diff -u -r1.5 init_task.c
--- a/arch/parisc/kernel/init_task.c 1 May 2004 16:05:55 -0000 1.5
+++ b/arch/parisc/kernel/init_task.c 2 May 2004 15:53:51 -0000
@@ -52,11 +52,13 @@
__attribute__((aligned(128))) __attribute__((__section__(".data.init_task"))) =
{ INIT_THREAD_INFO(init_task) };
-pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__ ((aligned(PAGE_SIZE<<PGD_ORDER))) = { {0}, };
#ifdef __LP64__
-pmd_t pmd0[PTRS_PER_PMD] __attribute__ ((aligned(4096))) = { {0}, };
+/* NOTE: This layout exactly conforms to the hybrid L2/L3 page table layout
+ * with the first pmd adjacent to the pgd and below it */
+pmd_t pmd0[PTRS_PER_PMD] __attribute__ ((aligned(PAGE_SIZE))) = { {0}, };
#endif
-pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __attribute__ ((aligned(4096))) = { {0}, };
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__ ((aligned(PAGE_SIZE))) = { {0}, };
+pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __attribute__ ((aligned(PAGE_SIZE))) = { {0}, };
/*
* Initial task structure.
Index: include/asm-parisc/pgalloc.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/asm-parisc/pgalloc.h,v
retrieving revision 1.4
diff -u -r1.4 pgalloc.h
--- a/include/asm-parisc/pgalloc.h 1 May 2004 20:03:11 -0000 1.4
+++ b/include/asm-parisc/pgalloc.h 2 May 2004 15:54:10 -0000
@@ -10,18 +10,44 @@
#include <asm/pgtable.h>
#include <asm/cache.h>
+/* Allocate the top level pgd (page directory)
+ *
+ * Here (for 64 bit kernels) we implement a Hybrid L2/L3 scheme: we
+ * allocate the first pmd adjacent to the pgd. This means that we can
+ * subtract a constant offset to get to it. The pmd and pgd sizes are
+ * arranged so that a single pmd covers 4GB (giving a full LP64
+ * process access to 8TB) so our lookups are effectively L2 for the
+ * first 4GB of the kernel (i.e. for all ILP32 processes and all the
+ * kernel for machines with under 4GB of memory) */
static inline pgd_t *pgd_alloc(struct mm_struct *mm)
{
- pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD_ORDER);
-
- if (likely(pgd != NULL))
- memset(pgd, 0, PAGE_SIZE<<PGD_ORDER);
- return pgd;
+ pgd_t *pgd = (pgd_t *)__get_free_pages(GFP_KERNEL|GFP_DMA,
+ PGD_ALLOC_ORDER);
+ pgd_t *actual_pgd = pgd;
+
+ if (likely(pgd != NULL)) {
+ memset(pgd, 0, PAGE_SIZE<<PGD_ALLOC_ORDER);
+#ifdef __LP64__
+ actual_pgd += PTRS_PER_PGD;
+ /* Populate first pmd with allocated memory. We mark it
+ * with _PAGE_GATEWAY as a signal to the system that this
+ * pmd entry may not be cleared. */
+ pgd_val(*actual_pgd) = (_PAGE_TABLE | _PAGE_GATEWAY) +
+ (__u32)__pa((unsigned long)pgd);
+ /* The first pmd entry also is marked with _PAGE_GATEWAY as
+ * a signal that this pmd may not be freed */
+ pgd_val(*pgd) = _PAGE_GATEWAY;
+#endif
+ }
+ return actual_pgd;
}
static inline void pgd_free(pgd_t *pgd)
{
- free_pages((unsigned long)pgd, PGD_ORDER);
+#ifdef __LP64__
+ pgd -= PTRS_PER_PGD;
+#endif
+ free_pages((unsigned long)pgd, PGD_ALLOC_ORDER);
}
#if PT_NLEVELS == 3
@@ -46,6 +72,12 @@
static inline void pmd_free(pmd_t *pmd)
{
+#ifdef __LP64__
+ if(pmd_val(*pmd) & _PAGE_GATEWAY)
+ /* This is the permanent pmd attached to the pgd;
+ * cannot free it */
+ return;
+#endif
free_pages((unsigned long)pmd, PMD_ORDER);
}
@@ -67,7 +99,15 @@
static inline void
pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
{
- pmd_val(*pmd) = _PAGE_TABLE + (__u32)__pa((unsigned long)pte);
+#ifdef __LP64__
+ /* preserve the gateway marker if this is the beginning of
+ * the permanent pmd */
+ if(pmd_val(*pmd) & _PAGE_GATEWAY)
+ pmd_val(*pmd) = (_PAGE_TABLE | _PAGE_GATEWAY)
+ + (__u32)__pa((unsigned long)pte);
+ else
+#endif
+ pmd_val(*pmd) = _PAGE_TABLE + (__u32)__pa((unsigned long)pte);
}
#define pmd_populate(mm, pmd, pte_page) \
Index: include/asm-parisc/pgtable.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/asm-parisc/pgtable.h,v
retrieving revision 1.8
diff -u -r1.8 pgtable.h
--- a/include/asm-parisc/pgtable.h 1 May 2004 20:03:11 -0000 1.8
+++ b/include/asm-parisc/pgtable.h 2 May 2004 15:54:11 -0000
@@ -67,10 +67,12 @@
#define PT_INITIAL 4 /* Number of initial page tables */
#define PGD_ORDER 1 /* Number of pages per pgd */
#define PMD_ORDER 1 /* Number of pages per pmd */
+#define PGD_ALLOC_ORDER 2 /* first pgd contains pmd */
#else
#define PT_NLEVELS 2
#define PT_INITIAL 2 /* Number of initial page tables */
#define PGD_ORDER 0 /* Number of pages per pgd */
+#define PGD_ALLOC_ORDER PGD_ORDER
#endif
/* Definitions for 3rd level (we use PLD here for Page Lower directory
@@ -242,10 +244,26 @@
#define pte_present(x) (pte_val(x) & _PAGE_PRESENT)
#define pte_clear(xp) do { pte_val(*(xp)) = 0; } while (0)
+#ifdef __LP64__
+/* The first entry of the permanent pmd is not there if it contains
+ * the gateway marker */
+#define pmd_none(x) (!pmd_val(x) || pmd_val(x) == _PAGE_GATEWAY)
+#define pmd_bad(x) ((pmd_val(x) & ~PAGE_MASK) != _PAGE_TABLE && (pmd_val(x) & ~PAGE_MASK) != (_PAGE_TABLE | _PAGE_GATEWAY))
+#else
#define pmd_none(x) (!pmd_val(x))
#define pmd_bad(x) ((pmd_val(x) & ~PAGE_MASK) != _PAGE_TABLE)
+#endif
#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
-#define pmd_clear(xp) do { pmd_val(*(xp)) = 0; } while (0)
+static inline void pmd_clear(pmd_t *pmd) {
+#ifdef __LP64__
+ if(pmd_val(*pmd) & _PAGE_GATEWAY)
+ /* This is the entry pointing to the permanent pmd
+ * attached to the pgd; cannot clear it */
+ pmd_val(*pmd) = _PAGE_GATEWAY;
+ else
+#endif
+ pmd_val(*pmd) = 0;
+}
@@ -255,9 +273,21 @@
/* For 64 bit we have three level tables */
#define pgd_none(x) (!pgd_val(x))
+#ifdef __LP64__
+#define pgd_bad(x) ((pgd_val(x) & ~PAGE_MASK) != _PAGE_TABLE && (pgd_val(x) & ~PAGE_MASK) != (_PAGE_TABLE | _PAGE_GATEWAY))
+#else
#define pgd_bad(x) ((pgd_val(x) & ~PAGE_MASK) != _PAGE_TABLE)
+#endif
#define pgd_present(x) (pgd_val(x) & _PAGE_PRESENT)
-#define pgd_clear(xp) do { pgd_val(*(xp)) = 0; } while (0)
+static inline void pgd_clear(pgd_t *pgd) {
+#ifdef __LP64__
+ if(pgd_val(*pgd) & _PAGE_GATEWAY)
+ /* This is the permanent pmd attached to the pgd; cannot
+ * free it */
+ return;
+#endif
+ pgd_val(*pgd) = 0;
+}
#else
/*
* The "pgd_xxx()" functions here are trivial for a folded two-level
More information about the parisc-linux
mailing list