--- linux-2.6.11.orig/arch/xen/i386/mm/pgtable.c 2005-06-22 16:25:17.000000000 +0200 +++ linux-2.6.11/arch/xen/i386/mm/pgtable.c 2005-06-23 18:22:05.000000000 +0200 @@ -274,14 +274,14 @@ void pgd_ctor(void *pgd, kmem_cache_t *c { unsigned long flags; - if (PTRS_PER_PMD == 1) + if (!HAVE_SHARED_KERNEL_PMD) spin_lock_irqsave(&pgd_lock, flags); memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, swapper_pg_dir + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); - if (PTRS_PER_PMD > 1) + if (HAVE_SHARED_KERNEL_PMD) return; pgd_list_add(pgd); @@ -289,12 +289,11 @@ void pgd_ctor(void *pgd, kmem_cache_t *c memset(pgd, 0, USER_PTRS_PER_PGD*sizeof(pgd_t)); } -/* never called when PTRS_PER_PMD > 1 */ void pgd_dtor(void *pgd, kmem_cache_t *cache, unsigned long unused) { unsigned long flags; /* can be called from interrupt context */ - if (PTRS_PER_PMD > 1) + if (HAVE_SHARED_KERNEL_PMD) return; spin_lock_irqsave(&pgd_lock, flags); @@ -304,12 +303,30 @@ void pgd_dtor(void *pgd, kmem_cache_t *c pgd_t *pgd_alloc(struct mm_struct *mm) { - int i; + int i = 0; pgd_t *pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL); if (PTRS_PER_PMD == 1 || !pgd) return pgd; + if (!HAVE_SHARED_KERNEL_PMD) { + /* alloc and copy kernel pmd */ + unsigned long flags; + pgd_t *copy_pgd = pgd_offset_k(PAGE_OFFSET); + pud_t *copy_pud = pud_offset(copy_pgd, PAGE_OFFSET); + pmd_t *copy_pmd = pmd_offset(copy_pud, PAGE_OFFSET); + pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); + if (0 == pmd) + goto out_oom; + + spin_lock_irqsave(&pgd_lock, flags); + memcpy(pmd, copy_pmd, PAGE_SIZE); + spin_unlock_irqrestore(&pgd_lock, flags); + make_page_readonly(pmd); + set_pgd(&pgd[USER_PTRS_PER_PGD], __pgd(1 + __pa(pmd))); + } + + /* alloc user pmds */ for (i = 0; i < USER_PTRS_PER_PGD; ++i) { pmd_t *pmd = kmem_cache_alloc(pmd_cache, GFP_KERNEL); if (!pmd) @@ -339,9 +356,17 @@ void pgd_free(pgd_t *pgd) } /* in the PAE case user pgd entries are overwritten before usage */ - if (PTRS_PER_PMD > 1) - for (i = 0; i < USER_PTRS_PER_PGD; ++i) - kmem_cache_free(pmd_cache, (void *)__va(pgd_val(pgd[i])-1)); + if (PTRS_PER_PMD > 1) { + for (i = 0; i < USER_PTRS_PER_PGD; ++i) { + pmd_t *pmd = (void *)__va(pgd_val(pgd[i])-1); + kmem_cache_free(pmd_cache, pmd); + } + if (!HAVE_SHARED_KERNEL_PMD) { + pmd_t *pmd = (void *)__va(pgd_val(pgd[USER_PTRS_PER_PGD])-1); + make_page_writable(pmd); + kmem_cache_free(pmd_cache, pmd); + } + } /* in the non-PAE case, clear_page_range() clears user pgd entries */ kmem_cache_free(pgd_cache, pgd); } --- linux-2.6.11.orig/include/asm-xen/asm-i386/pgtable-2level-defs.h 2005-06-22 16:25:17.000000000 +0200 +++ linux-2.6.11/include/asm-xen/asm-i386/pgtable-2level-defs.h 2005-06-22 16:25:17.000000000 +0200 @@ -1,6 +1,8 @@ #ifndef _I386_PGTABLE_2LEVEL_DEFS_H #define _I386_PGTABLE_2LEVEL_DEFS_H +#define HAVE_SHARED_KERNEL_PMD 0 + /* * traditional i386 two-level paging structure: */