Skip to content

Commit d551aaa

Browse files
toshikaniKAGA-KOKO
authored andcommitted
x86/mm: Fix __split_large_page() to handle large PAT bit
__split_large_page() is called from __change_page_attr() to change the mapping attribute by splitting a given large page into smaller pages. This function uses pte_pfn() and pte_pgprot() for PUD/PMD, which do not handle the large PAT bit properly. Fix __split_large_page() by using the corresponding pud/pmd pfn/ pgprot interfaces. Also remove '#ifdef CONFIG_X86_64', which is not necessary. Signed-off-by: Toshi Kani <toshi.kani@hpe.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Juergen Gross <jgross@suse.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Borislav Petkov <bp@alien8.de> Cc: Konrad Wilk <konrad.wilk@oracle.com> Cc: Robert Elliot <elliott@hpe.com> Cc: linux-mm@kvack.org Link: http://lkml.kernel.org/r/1442514264-12475-11-git-send-email-toshi.kani@hpe.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
1 parent 3a19109 commit d551aaa

File tree

1 file changed

+19
-12
lines changed

1 file changed

+19
-12
lines changed

arch/x86/mm/pageattr.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
605605
struct page *base)
606606
{
607607
pte_t *pbase = (pte_t *)page_address(base);
608-
unsigned long pfn, pfninc = 1;
608+
unsigned long ref_pfn, pfn, pfninc = 1;
609609
unsigned int i, level;
610610
pte_t *tmp;
611611
pgprot_t ref_prot;
@@ -622,26 +622,33 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
622622
}
623623

624624
paravirt_alloc_pte(&init_mm, page_to_pfn(base));
625-
ref_prot = pte_pgprot(pte_clrhuge(*kpte));
626625

627-
/* promote PAT bit to correct position */
628-
if (level == PG_LEVEL_2M)
626+
switch (level) {
627+
case PG_LEVEL_2M:
628+
ref_prot = pmd_pgprot(*(pmd_t *)kpte);
629+
/* clear PSE and promote PAT bit to correct position */
629630
ref_prot = pgprot_large_2_4k(ref_prot);
631+
ref_pfn = pmd_pfn(*(pmd_t *)kpte);
632+
break;
630633

631-
#ifdef CONFIG_X86_64
632-
if (level == PG_LEVEL_1G) {
634+
case PG_LEVEL_1G:
635+
ref_prot = pud_pgprot(*(pud_t *)kpte);
636+
ref_pfn = pud_pfn(*(pud_t *)kpte);
633637
pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
638+
634639
/*
635-
* Set the PSE flags only if the PRESENT flag is set
640+
* Clear the PSE flags if the PRESENT flag is not set
636641
* otherwise pmd_present/pmd_huge will return true
637642
* even on a non present pmd.
638643
*/
639-
if (pgprot_val(ref_prot) & _PAGE_PRESENT)
640-
pgprot_val(ref_prot) |= _PAGE_PSE;
641-
else
644+
if (!(pgprot_val(ref_prot) & _PAGE_PRESENT))
642645
pgprot_val(ref_prot) &= ~_PAGE_PSE;
646+
break;
647+
648+
default:
649+
spin_unlock(&pgd_lock);
650+
return 1;
643651
}
644-
#endif
645652

646653
/*
647654
* Set the GLOBAL flags only if the PRESENT flag is set
@@ -657,7 +664,7 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
657664
/*
658665
* Get the target pfn from the original entry:
659666
*/
660-
pfn = pte_pfn(*kpte);
667+
pfn = ref_pfn;
661668
for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
662669
set_pte(&pbase[i], pfn_pte(pfn, canon_pgprot(ref_prot)));
663670

0 commit comments

Comments
 (0)