On 3/6/2024 10:37 PM, Paul E. McKenney wrote:
> On Wed, Mar 06, 2024 at 10:06:21PM -0500, Mathieu Desnoyers wrote:
>> On 2024-03-06 21:43, Linus Torvalds wrote:
>> [...]
>>>
>>> Honestly, this all makes me think that we'd be *much* better off
>>> showing the real "handoff" with smp_store_release() and
>>> smp_load_acquire().
>>
>> We've done something similar in liburcu (userspace code) to allow
>> Thread Sanitizer to understand the happens-before relationships
>> within the RCU implementations and lock-free data structures.
>>
>> Moving to load-acquire/store-release (C11 model in our case)
>> allowed us to provide enough happens-before relationship for
>> Thread Sanitizer to understand what is happening under the
>> hood in liburcu and perform relevant race detection of user
>> code.
> 
> Good point!
> 
> In the kernel, though, KCSAN understands the Linux-kernel memory model,
> and so we don't get that sort of false positive.
> 
>> As far as the WRITE_ONCE(x, READ_ONCE(x) + 1) pattern
>> is concerned, the only valid use-case I can think of is
>> split counters or RCU implementations where there is a
>> single updater doing the increment, and one or more
>> concurrent reader threads that need to snapshot a
>> consistent value with READ_ONCE().
> 
> It is wrong here.  OK, not wrong from a functional viewpoint, but it
> is at best very confusing.  I am applying patches to fix this.  I will
> push out a new "dev" branch on -rcu that will make this function appear
> as shown below.
> 
> So what would you use that pattern for?
> 
> One possibility is a per-CPU statistical counter in userspace on a
> fastpath, in cases where losing the occasional count is OK.  Then learning
> your CPU (and possibly being immediately migrated to some other CPU),
> READ_ONCE() of the count, increment, and WRITE_ONCE() might (or might not)
> make sense.
> 
> I suppose the same in the kernel if there was a fastpath so extreme you
> could not afford to disable preemption.
> 
> At best, very niche.
> 
> Or am I suffering a failure of imagination yet again?  ;-)
> 
>                                                       Thanx, Paul
> 
> ------------------------------------------------------------------------
> 
> static bool
> rcu_torture_pipe_update_one(struct rcu_torture *rp)
> {
>       int i;
>       struct rcu_torture_reader_check *rtrcp = READ_ONCE(rp->rtort_chkp);
> 
>       if (rtrcp) {
>               WRITE_ONCE(rp->rtort_chkp, NULL);
>               smp_store_release(&rtrcp->rtc_ready, 1); // Pair with 
> smp_load_acquire().
>       }
>       i = rp->rtort_pipe_count;
>       if (i > RCU_TORTURE_PIPE_LEN)
>               i = RCU_TORTURE_PIPE_LEN;
>       atomic_inc(&rcu_torture_wcount[i]);
>       WRITE_ONCE(rp->rtort_pipe_count, i + 1);
>       ASSERT_EXCLUSIVE_WRITER(rp->rtort_pipe_count);

I was going to say to add a comment here for the future reader, that update-side
->rtort_pipe_count READ/WRITE are already mutually exclusive, but this ASSERT
already documents it ;-)

Also FWIW I confirmed after starting at code that indeed only one update-side
access is possible at a time! Thanks,
Reviewed-by: Joel Fernandes (Google) <[email protected]>

 - Joel


Reply via email to