arch/x86/include/asm/pgtable-3level.h | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/x86/include/asm/pgtable-3level.h b/arch/x86/include/asm/pgtable-3level.h index 43876f16caf1..4d6bf9f54bad 100644 --- a/arch/x86/include/asm/pgtable-3level.h +++ b/arch/x86/include/asm/pgtable-3level.h @@ -53,33 +53,34 @@ static inline void native_set_pte(pte_t *ptep, pte_t pte) * * With THP if the mmap_sem is hold for reading, the pmd can become * THP or null or point to a pte (and in turn become "stable") at any - * time under pmd_read_atomic, so it's mandatory to read it atomically - * with cmpxchg8b. + * time under pmd_read_atomic, so we have to be really careful. We'll + * re-read the low word to check that it hasn't become NULL or turned + * into a pte. */ -#ifndef CONFIG_TRANSPARENT_HUGEPAGE static inline pmd_t pmd_read_atomic(pmd_t *pmdp) { pmdval_t ret; - u32 *tmp = (u32 *)pmdp; + u32 low, hight, *tmp = (u32 *)pmdp; - ret = (pmdval_t) (*tmp); - if (ret) { +repeat: + low = tmp[0]; + high = 0; + if (low) { /* * If the low part is null, we must not read the high part * or we can end up with a partial pmd. */ smp_rmb(); - ret |= ((pmdval_t)*(tmp + 1)) << 32; + high = tmp[1]; + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { + smp_rmb(); + if (low != tmp[0]) + goto repeat; + } } - return (pmd_t) { ret }; + return (pmd_t) { low + ((u64)high << 32) }; } -#else /* CONFIG_TRANSPARENT_HUGEPAGE */ -static inline pmd_t pmd_read_atomic(pmd_t *pmdp) -{ - return (pmd_t) { atomic64_read((atomic64_t *)pmdp) }; -} -#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte) {