--- /dev/null
+From stable-bounces@linux.kernel.org Mon Sep 19 11:06:30 2005
+Date: Mon, 19 Sep 2005 20:02:32 +0200
+From: "Paolo 'Blaisorblade' Giarrusso" <blaisorblade@yahoo.it>
+To: stable@kernel.org
+Cc: jdike@addtoit.com, Paolo Blaisorblade Giarrusso <blaisorblade@yahoo.it>,
+ linux-kernel@vger.kernel.org,
+ user-mode-linux-devel@lists.sourceforge.net
+Subject: [PATCH] uml - Fix x86_64 page leak
+
+We were leaking pmd pages when 3_LEVEL_PGTABLES was enabled. This fixes that,
+has been well tested and is included in mainline tree. Please include in -stable
+as well.
+
+Signed-off-by: Jeff Dike <jdike@addtoit.com>
+Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Signed-off-by: Chris Wright <chrisw@osdl.org>
+---
+ arch/um/kernel/skas/include/mmu-skas.h | 4 ++++
+ arch/um/kernel/skas/mmu.c | 9 ++++++++-
+ include/asm-um/pgalloc.h | 12 +++++++-----
+ include/asm-um/pgtable-3level.h | 9 +++------
+ 4 files changed, 22 insertions(+), 12 deletions(-)
+
+Index: linux-2.6.13.y/arch/um/kernel/skas/include/mmu-skas.h
+===================================================================
+--- linux-2.6.13.y.orig/arch/um/kernel/skas/include/mmu-skas.h
++++ linux-2.6.13.y/arch/um/kernel/skas/include/mmu-skas.h
+@@ -6,11 +6,15 @@
+ #ifndef __SKAS_MMU_H
+ #define __SKAS_MMU_H
+
++#include "linux/config.h"
+ #include "mm_id.h"
+
+ struct mmu_context_skas {
+ struct mm_id id;
+ unsigned long last_page_table;
++#ifdef CONFIG_3_LEVEL_PGTABLES
++ unsigned long last_pmd;
++#endif
+ };
+
+ extern void switch_mm_skas(struct mm_id * mm_idp);
+Index: linux-2.6.13.y/arch/um/kernel/skas/mmu.c
+===================================================================
+--- linux-2.6.13.y.orig/arch/um/kernel/skas/mmu.c
++++ linux-2.6.13.y/arch/um/kernel/skas/mmu.c
+@@ -56,6 +56,9 @@ static int init_stub_pte(struct mm_struc
+ */
+
+ mm->context.skas.last_page_table = pmd_page_kernel(*pmd);
++#ifdef CONFIG_3_LEVEL_PGTABLES
++ mm->context.skas.last_pmd = (unsigned long) __va(pud_val(*pud));
++#endif
+
+ *pte = mk_pte(virt_to_page(kernel), __pgprot(_PAGE_PRESENT));
+ *pte = pte_mkexec(*pte);
+@@ -140,6 +143,10 @@ void destroy_context_skas(struct mm_stru
+ else {
+ os_kill_ptraced_process(mmu->id.u.pid, 1);
+ free_page(mmu->id.stack);
+- free_page(mmu->last_page_table);
++ pte_free_kernel((pte_t *) mmu->last_page_table);
++ dec_page_state(nr_page_table_pages);
++#ifdef CONFIG_3_LEVEL_PGTABLES
++ pmd_free((pmd_t *) mmu->last_pmd);
++#endif
+ }
+ }
+Index: linux-2.6.13.y/include/asm-um/pgalloc.h
+===================================================================
+--- linux-2.6.13.y.orig/include/asm-um/pgalloc.h
++++ linux-2.6.13.y/include/asm-um/pgalloc.h
+@@ -42,11 +42,13 @@ static inline void pte_free(struct page
+ #define __pte_free_tlb(tlb,pte) tlb_remove_page((tlb),(pte))
+
+ #ifdef CONFIG_3_LEVEL_PGTABLES
+-/*
+- * In the 3-level case we free the pmds as part of the pgd.
+- */
+-#define pmd_free(x) do { } while (0)
+-#define __pmd_free_tlb(tlb,x) do { } while (0)
++
++extern __inline__ void pmd_free(pmd_t *pmd)
++{
++ free_page((unsigned long)pmd);
++}
++
++#define __pmd_free_tlb(tlb,x) tlb_remove_page((tlb),virt_to_page(x))
+ #endif
+
+ #define check_pgt_cache() do { } while (0)
+Index: linux-2.6.13.y/include/asm-um/pgtable-3level.h
+===================================================================
+--- linux-2.6.13.y.orig/include/asm-um/pgtable-3level.h
++++ linux-2.6.13.y/include/asm-um/pgtable-3level.h
+@@ -98,14 +98,11 @@ static inline pmd_t *pmd_alloc_one(struc
+ return pmd;
+ }
+
+-static inline void pmd_free(pmd_t *pmd){
+- free_page((unsigned long) pmd);
++extern inline void pud_clear (pud_t *pud)
++{
++ set_pud(pud, __pud(0));
+ }
+
+-#define __pmd_free_tlb(tlb,x) do { } while (0)
+-
+-static inline void pud_clear (pud_t * pud) { }
+-
+ #define pud_page(pud) \
+ ((struct page *) __va(pud_val(pud) & PAGE_MASK))
+