On Fri, Sep 6, 2024 at 7:12 AM Andrii Nakryiko <andrii@xxxxxxxxxx> wrote: > +static inline bool mmap_lock_speculation_end(struct mm_struct *mm, int seq) > +{ > + /* Pairs with RELEASE semantics in inc_mm_lock_seq(). */ > + return seq == smp_load_acquire(&mm->mm_lock_seq); > +} A load-acquire can't provide "end of locked section" semantics - a load-acquire is a one-way barrier, you can basically use it for "acquire lock" semantics but not for "release lock" semantics, because the CPU will prevent reordering the load with *later* loads but not with *earlier* loads. So if you do: mmap_lock_speculation_start() [locked reads go here] mmap_lock_speculation_end() then the CPU is allowed to reorder your instructions like this: mmap_lock_speculation_start() mmap_lock_speculation_end() [locked reads go here] so the lock is broken. > static inline void mmap_write_lock(struct mm_struct *mm) > { > __mmap_lock_trace_start_locking(mm, true); > down_write(&mm->mmap_lock); > + inc_mm_lock_seq(mm); > __mmap_lock_trace_acquire_returned(mm, true, true); > } Similarly, inc_mm_lock_seq(), which does a store-release, can only provide "release lock" semantics, not "take lock" semantics, because the CPU can reorder it with later stores; for example, this code: inc_mm_lock_seq() [locked stuff goes here] inc_mm_lock_seq() can be reordered into this: [locked stuff goes here] inc_mm_lock_seq() inc_mm_lock_seq() so the lock is broken. For "taking a lock" with a memory store, or "dropping a lock" with a memory load, you need heavier memory barriers, see Documentation/memory-barriers.txt.