__cont_access_flags_changed was originally introduced to avoid making unnecessary changes to the PTEs. Consider the following case: all the PTEs in the contiguous group have PTE_DIRTY | PTE_RDONLY | PTE_WRITE, and we are running on a system without HAFDBS. When writing via these PTEs, we will get a page fault, and hugetlb_fault will (rightly) attempt to update the PTEs with PTE_DIRTY | PTE_WRITE, but, as both the original PTEs and the new PTEs are pte_dirty(), __cont_access_flags_changed prevents the pgprot update from occurring. To avoid the page fault loop that we get ourselves into, distinguish between hardware-dirty and software-dirty for this check. Non-contiguous PTEs aren't broken in the same way, as we will always write a new PTE unless the new PTE is exactly equal to the old one. Fixes: 031e6e6b4e12 ("arm64: hugetlb: Avoid unnecessary clearing in huge_ptep_set_access_flags") Signed-off-by: James Houghton <jthoughton@xxxxxxxxxx> Cc: <stable@xxxxxxxxxxxxxxx> diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index f5aae342632c..87a9564976fa 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -437,7 +437,10 @@ static int __cont_access_flags_changed(pte_t *ptep, pte_t pte, int ncontig) for (i = 0; i < ncontig; i++) { pte_t orig_pte = ptep_get(ptep + i); - if (pte_dirty(pte) != pte_dirty(orig_pte)) + if (pte_sw_dirty(pte) != pte_sw_dirty(orig_pte)) + return 1; + + if (pte_hw_dirty(pte) != pte_hw_dirty(orig_pte)) return 1; if (pte_young(pte) != pte_young(orig_pte)) -- 2.43.0.rc2.451.g8631bc7472-goog