This routine is to be used for updating in-use, leaf-level PMDs in the vmemmap without introducing a window where vmemmap accesses might fault. Implementing this on arm64 requires some care: use the same access flag trick that is used for vmemmap PTE updates. HAFT is not needed and the TLB flushing routine remains identical, as we are not overwriting a non-leaf PMD. For systems that support BBML2_NOABORT, there is no need to use the AF trick. Signed-off-by: James Houghton --- arch/arm64/include/asm/pgalloc.h | 51 ++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index c8946250d431..26b05f8b70cd 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -125,9 +125,54 @@ pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep) static inline int try_populate_vmemmap_pmd(pmd_t *pmdp, pte_t *pgtable, unsigned long addr) { - /* BBML2_NOABORT is required. Its presence has been checked. */ - pmd_populate_kernel(&init_mm, pmdp, pgtable); - return 0; + const int max_attempts = 16; + int attempts = 0; + pmd_t old_pmd, new_pmd; + + if (!system_supports_hvo()) + return -EOPNOTSUPP; + + if (system_supports_bbml2_noabort()) { + /* + * BBML2_NOABORT allows block->table transitions if the PTEs + * underneath do not conflict with existing, potentially cached + * translations. + */ + pmd_populate_kernel(&init_mm, pmdp, pgtable); + return 0; + } + + new_pmd = __pmd(__phys_to_pmd_val(__pa(pgtable)) | + PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_UXN); + + old_pmd = pmdp_get(pmdp); + + do { + if (WARN_ON_ONCE(!pmd_valid(old_pmd))) + return -EINVAL; + + if (WARN_ON_ONCE(!pmd_leaf(old_pmd))) + return -EINVAL; + + /* We should never get a contiguous PMD here. */ + if (WARN_ON_ONCE(pmd_cont(old_pmd))) + return -EINVAL; + + if (pmd_young(old_pmd)) { + /* __ptep_clear_young() returns the overwritten PTE */ + old_pmd = pte_pmd(pte_mkold(__ptep_clear_young((pte_t *)pmdp))); + + flush_tlb_kernel_range(addr, addr + PMD_SIZE); + } + /* + * Translations without AF cannot be cached, so we can replace + * them without BBM. + */ + } while (!try_cmpxchg_relaxed(&pmd_val(*pmdp), &pmd_val(old_pmd), + pmd_val(new_pmd)) && + ++attempts < max_attempts); + + return attempts == max_attempts ? -EAGAIN : 0; } #endif -- 2.55.0.795.g602f6c329a-goog