Commit 891121e6 authored by Aneesh Kumar K.V's avatar Aneesh Kumar K.V Committed by Michael Ellerman
Browse files

powerpc/mm: Differentiate between hugetlb and THP during page walk



We need to properly identify whether a hugepage is an explicit or
a transparent hugepage in follow_huge_addr(). We used to depend
on hugepage shift argument to do that. But in some case that can
result in wrong results. For ex:

On finding a transparent hugepage we set hugepage shift to PMD_SHIFT.
But we can end up clearing the thp pte, via pmdp_huge_get_and_clear.
We do prevent reusing the pfn page via the usage of
kick_all_cpus_sync(). But that happens after we updated the pte to 0.
Hence in follow_huge_addr() we can find hugepage shift set, but transparent
huge page check fail for a thp pte.

NOTE: We fixed a variant of this race against thp split in commit
691e95fd
("powerpc/mm/thp: Make page table walk safe against thp split/collapse")

Without this patch, we may hit the BUG_ON(flags & FOLL_GET) in
follow_page_mask occasionally.

In the long term, we may want to switch ppc64 64k page size config to
enable CONFIG_ARCH_WANT_GENERAL_HUGETLB

Reported-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent ec2640b1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

#include <asm/asm-compat.h>
#include <asm/page.h>
#include <asm/bug.h>

/*
 * This is necessary to get the definition of PGTABLE_RANGE which we
+9 −1
Original line number Diff line number Diff line
@@ -437,9 +437,9 @@ static inline char *get_hpte_slot_array(pmd_t *pmdp)

}

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
extern void hpte_do_hugepage_flush(struct mm_struct *mm, unsigned long addr,
				   pmd_t *pmdp, unsigned long old_pmd);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
extern pmd_t pfn_pmd(unsigned long pfn, pgprot_t pgprot);
extern pmd_t mk_pmd(struct page *page, pgprot_t pgprot);
extern pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot);
@@ -479,6 +479,14 @@ static inline int pmd_trans_splitting(pmd_t pmd)
}

extern int has_transparent_hugepage(void);
#else
static inline void hpte_do_hugepage_flush(struct mm_struct *mm,
					  unsigned long addr, pmd_t *pmdp,
					  unsigned long old_pmd)
{

	WARN(1, "%s called with THP disabled\n", __func__);
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */

static inline int pmd_large(pmd_t pmd)
+3 −3
Original line number Diff line number Diff line
@@ -259,15 +259,15 @@ extern int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
#define has_transparent_hugepage() 0
#endif
pte_t *__find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea,
				 unsigned *shift);
				   bool *is_thp, unsigned *shift);
static inline pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea,
					       unsigned *shift)
					       bool *is_thp, unsigned *shift)
{
	if (!arch_irqs_disabled()) {
		pr_info("%s called with irq enabled\n", __func__);
		dump_stack();
	}
	return __find_linux_pte_or_hugepte(pgdir, ea, shift);
	return __find_linux_pte_or_hugepte(pgdir, ea, is_thp, shift);
}
#endif /* __ASSEMBLY__ */

+2 −1
Original line number Diff line number Diff line
@@ -351,7 +351,8 @@ static inline unsigned long eeh_token_to_phys(unsigned long token)
	 * worried about _PAGE_SPLITTING/collapse. Also we will not hit
	 * page table free, because of init_mm.
	 */
	ptep = __find_linux_pte_or_hugepte(init_mm.pgd, token, &hugepage_shift);
	ptep = __find_linux_pte_or_hugepte(init_mm.pgd, token,
					   NULL, &hugepage_shift);
	if (!ptep)
		return token;
	WARN_ON(hugepage_shift);
+1 −1
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
		 * a page table free due to init_mm
		 */
		ptep = __find_linux_pte_or_hugepte(init_mm.pgd, vaddr,
						 &hugepage_shift);
						   NULL, &hugepage_shift);
		if (ptep == NULL)
			paddr = 0;
		else {
Loading