diff options
author | Joerg Roedel <joro@8bytes.org> | 2018-04-11 11:24:38 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-04-12 03:41:41 -0400 |
commit | e3e288121408c3abeed5af60b87b95c847143845 (patch) | |
tree | 2da19d6732a6ab8250ad19467fb1d2f3e3b9cac9 | |
parent | 8c06c7740d191b9055cb9be920579d5ecdd26303 (diff) |
x86/pgtable: Don't set huge PUD/PMD on non-leaf entries
The pmd_set_huge() and pud_set_huge() functions are used from
the generic ioremap() code to establish large mappings where this
is possible.
But the generic ioremap() code does not check whether the
PMD/PUD entries are already populated with a non-leaf entry,
so that any page-table pages these entries point to will be
lost.
Further, on x86-32 with SHARED_KERNEL_PMD=0, this causes a
BUG_ON() in vmalloc_sync_one() when PMD entries are synced
from swapper_pg_dir to the current page-table. This happens
because the PMD entry from swapper_pg_dir was promoted to a
huge-page entry while the current PGD still contains the
non-leaf entry. Because both entries are present and point
to a different page, the BUG_ON() triggers.
This was actually triggered with pti-x32 enabled in a KVM
virtual machine by the graphics driver.
A real and better fix for that would be to improve the
page-table handling in the generic ioremap() code. But that is
out-of-scope for this patch-set and left for later work.
Reported-by: David H. Gutteridge <dhgutteridge@sympatico.ca>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: David Laight <David.Laight@aculab.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Eduardo Valentin <eduval@amazon.com>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Jiri Kosina <jkosina@suse.cz>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Waiman Long <llong@redhat.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: aliguori@amazon.com
Cc: daniel.gruss@iaik.tugraz.at
Cc: hughd@google.com
Cc: keescook@google.com
Cc: linux-mm@kvack.org
Link: http://lkml.kernel.org/r/20180411152437.GC15462@8bytes.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/mm/pgtable.c | 9 |
1 files changed, 9 insertions, 0 deletions
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index d10a40aceeaa..ffc8c13c50e4 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c | |||
@@ -1,6 +1,7 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/mm.h> | 2 | #include <linux/mm.h> |
3 | #include <linux/gfp.h> | 3 | #include <linux/gfp.h> |
4 | #include <linux/hugetlb.h> | ||
4 | #include <asm/pgalloc.h> | 5 | #include <asm/pgalloc.h> |
5 | #include <asm/pgtable.h> | 6 | #include <asm/pgtable.h> |
6 | #include <asm/tlb.h> | 7 | #include <asm/tlb.h> |
@@ -639,6 +640,10 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot) | |||
639 | (mtrr != MTRR_TYPE_WRBACK)) | 640 | (mtrr != MTRR_TYPE_WRBACK)) |
640 | return 0; | 641 | return 0; |
641 | 642 | ||
643 | /* Bail out if we are we on a populated non-leaf entry: */ | ||
644 | if (pud_present(*pud) && !pud_huge(*pud)) | ||
645 | return 0; | ||
646 | |||
642 | prot = pgprot_4k_2_large(prot); | 647 | prot = pgprot_4k_2_large(prot); |
643 | 648 | ||
644 | set_pte((pte_t *)pud, pfn_pte( | 649 | set_pte((pte_t *)pud, pfn_pte( |
@@ -667,6 +672,10 @@ int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot) | |||
667 | return 0; | 672 | return 0; |
668 | } | 673 | } |
669 | 674 | ||
675 | /* Bail out if we are we on a populated non-leaf entry: */ | ||
676 | if (pmd_present(*pmd) && !pmd_huge(*pmd)) | ||
677 | return 0; | ||
678 | |||
670 | prot = pgprot_4k_2_large(prot); | 679 | prot = pgprot_4k_2_large(prot); |
671 | 680 | ||
672 | set_pte((pte_t *)pmd, pfn_pte( | 681 | set_pte((pte_t *)pmd, pfn_pte( |