On Mon, 9 Jan 2023 12:53:36 -0800 Suren Baghdasaryan <surenb@xxxxxxxxxx> > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -627,12 +627,16 @@ static inline void vma_write_lock(struct vm_area_struct *vma) > * 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) > + if (vma->vm_lock->lock_seq == mm_lock_seq) > return; lock acquire for write to info lockdep. > > - down_write(&vma->vm_lock->lock); > - vma->vm_lock_seq = mm_lock_seq; > - up_write(&vma->vm_lock->lock); > + if (atomic_cmpxchg(&vma->vm_lock->count, 0, -1)) > + wait_event(vma->vm_mm->vma_writer_wait, > + atomic_cmpxchg(&vma->vm_lock->count, 0, -1) == 0); > + vma->vm_lock->lock_seq = mm_lock_seq; > + /* Write barrier to ensure lock_seq change is visible before count */ > + smp_wmb(); > + atomic_set(&vma->vm_lock->count, 0); > } > > /* > @@ -643,20 +647,28 @@ static inline void vma_write_lock(struct vm_area_struct *vma) > static inline bool vma_read_trylock(struct vm_area_struct *vma) > { > /* Check before locking. A race might cause false locked result. */ > - if (vma->vm_lock_seq == READ_ONCE(vma->vm_mm->mm_lock_seq)) > + if (vma->vm_lock->lock_seq == READ_ONCE(vma->vm_mm->mm_lock_seq)) > return false; Add mb to pair with the above wmb like if (READ_ONCE(vma->vm_lock->lock_seq) == READ_ONCE(vma->vm_mm->mm_lock_seq)) { smp_acquire__after_ctrl_dep(); return false; } > > - if (unlikely(down_read_trylock(&vma->vm_lock->lock) == 0)) > + if (unlikely(!atomic_inc_unless_negative(&vma->vm_lock->count))) > return false; > > + /* If atomic_t overflows, restore and fail to lock. */ > + if (unlikely(atomic_read(&vma->vm_lock->count) < 0)) { > + if (atomic_dec_and_test(&vma->vm_lock->count)) > + wake_up(&vma->vm_mm->vma_writer_wait); > + return false; > + } > + > /* > * Overflow might produce false locked result. > * False unlocked result is impossible because we modify and check > * vma->vm_lock_seq under vma->vm_lock protection and mm->mm_lock_seq > * modification invalidates all existing locks. > */ > - if (unlikely(vma->vm_lock_seq == READ_ONCE(vma->vm_mm->mm_lock_seq))) { > - up_read(&vma->vm_lock->lock); > + if (unlikely(vma->vm_lock->lock_seq == READ_ONCE(vma->vm_mm->mm_lock_seq))) { > + if (atomic_dec_and_test(&vma->vm_lock->count)) > + wake_up(&vma->vm_mm->vma_writer_wait); > return false; > } Simpler way to detect write lock owner and count overflow like int count = atomic_read(&vma->vm_lock->count); for (;;) { int new = count + 1; if (count < 0 || new < 0) return false; new = atomic_cmpxchg(&vma->vm_lock->count, count, new); if (new == count) break; count = new; cpu_relax(); } (wake up waiting readers after taking the lock; but the write lock owner before this read trylock should be responsible for waking waiters up.) lock acquire for read. > return true; > @@ -664,7 +676,8 @@ static inline bool vma_read_trylock(struct vm_area_struct *vma) > > static inline void vma_read_unlock(struct vm_area_struct *vma) > { lock release for read. > - up_read(&vma->vm_lock->lock); > + if (atomic_dec_and_test(&vma->vm_lock->count)) > + wake_up(&vma->vm_mm->vma_writer_wait); > }