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
691e95fd73
("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: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Этот коммит содержится в:

коммит произвёл
Michael Ellerman

родитель
ec2640b114
Коммит
891121e6c0
@@ -190,6 +190,7 @@ void tlb_flush(struct mmu_gather *tlb)
|
||||
void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
bool is_thp;
|
||||
int hugepage_shift;
|
||||
unsigned long flags;
|
||||
|
||||
@@ -208,21 +209,21 @@ void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
|
||||
local_irq_save(flags);
|
||||
arch_enter_lazy_mmu_mode();
|
||||
for (; start < end; start += PAGE_SIZE) {
|
||||
pte_t *ptep = find_linux_pte_or_hugepte(mm->pgd, start,
|
||||
pte_t *ptep = find_linux_pte_or_hugepte(mm->pgd, start, &is_thp,
|
||||
&hugepage_shift);
|
||||
unsigned long pte;
|
||||
|
||||
if (ptep == NULL)
|
||||
continue;
|
||||
pte = pte_val(*ptep);
|
||||
if (hugepage_shift)
|
||||
if (is_thp)
|
||||
trace_hugepage_invalidate(start, pte);
|
||||
if (!(pte & _PAGE_HASHPTE))
|
||||
continue;
|
||||
if (unlikely(hugepage_shift && pmd_trans_huge(*(pmd_t *)pte)))
|
||||
if (unlikely(is_thp))
|
||||
hpte_do_hugepage_flush(mm, start, (pmd_t *)ptep, pte);
|
||||
else
|
||||
hpte_need_flush(mm, start, ptep, pte, 0);
|
||||
hpte_need_flush(mm, start, ptep, pte, hugepage_shift);
|
||||
}
|
||||
arch_leave_lazy_mmu_mode();
|
||||
local_irq_restore(flags);
|
||||
|
Ссылка в новой задаче
Block a user