--- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -680,6 +680,27 @@ static inline void vma_start_write(struc up_write(&vma->vm_lock->lock); } +static inline bool vma_start_trywrite(struct vm_area_struct *vma) +{ + int mm_lock_seq; + + mmap_assert_write_locked(vma->vm_mm); + + /* + * current task is holding mmap_write_lock, both vma->vm_lock_seq and + * mm->mm_lock_seq can't be concurrently modified. + */ + mm_lock_seq = READ_ONCE(vma->vm_mm->mm_lock_seq); + if (vma->vm_lock_seq == mm_lock_seq) + return true; + + if (!down_write_trylock(&vma->vm_lock->lock)) + return false; + vma->vm_lock_seq = mm_lock_seq; + up_write(&vma->vm_lock->lock); + return true; +} + static inline void vma_assert_write_locked(struct vm_area_struct *vma) { mmap_assert_write_locked(vma->vm_mm); @@ -708,6 +729,10 @@ static inline bool vma_start_read(struct { return false; } static inline void vma_end_read(struct vm_area_struct *vma) {} static inline void vma_start_write(struct vm_area_struct *vma) {} +static inline bool vma_start_trywrite(struct vm_area_struct *vma) +{ + return true; +} static inline void vma_assert_write_locked(struct vm_area_struct *vma) {} static inline void vma_mark_detached(struct vm_area_struct *vma, bool detached) {} --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1823,7 +1823,10 @@ static int retract_page_tables(struct ad result = SCAN_PTE_UFFD_WP; goto unlock_next; } - vma_start_write(vma); + if (!vma_start_trywrite(vma)) { + result = SCAN_FAIL; + goto unlock_next; + } collapse_and_free_pmd(mm, vma, addr, pmd); if (!cc->is_khugepaged && is_target) result = set_huge_pmd(vma, addr, pmd, hpage);