Re: [PATCH v3] CodeSamples/count: add necessary partial memory barriers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 29 Mar 2023 16:18:43 -0700, Paul E. McKenney wrote:
> On Wed, Mar 29, 2023 at 08:43:33AM +0900, Akira Yokosawa wrote:
>> On Tue, 28 Mar 2023 10:58:24 -0400, Alan Huang wrote:
>>> This patch add several necessary partial memory barriers, the first one
>>> change READ_ONCE to smp_load_acquire to makes sure the reading from theftp[t]
>>> happens before the reading from counterp[t]. The litmus testing below
>>> represents the original code pattern, and the result is "Sometimes":
>>>
>>> C counter_sig
>>>
>>> {}
>>>
>>> P0(int *theft, int *counter)
>>> {
>>>     int r0;
>>>     int r1;
>>>
>>>     r0 = READ_ONCE(*theft);
>>>     r1 = READ_ONCE(*counter);
>>> }
>>>
>>> P1(int *theft, int *counter)
>>> {
>>>     WRITE_ONCE(*counter, 1);
>>>     smp_mb();
>>>     WRITE_ONCE(*theft, 1);
>>> }
>>>
>>> exists (0:r0=1 /\ 0:r1=0)
>>>
>>> Second one change WRITE_ONCE to smp_store_release to make sure that setting
>>> counterp[t] happens before the setting theftp[p] to THEFT_IDLE. Here is the
>>> litmus testing, The result is "Sometimes":
>>>
>>> C counter_sig_2
>>>
>>> {
>>>     int theft = 1;
>>>     int counter = 1;
>>> }
>>>
>>> P0(int *theft, int *counter)
>>> {
>>>     WRITE_ONCE(*counter, 0);
>>>     WRITE_ONCE(*theft, 0);
>>> }
>>>
>>> P1(int *theft, int *counter)
>>> {
>>>     if (READ_ONCE(*theft) == 0)
>>>     {
>>>         WRITE_ONCE(*counter, READ_ONCE(*counter)+1);
>>>     }
>>> }
>>>
>>> P2(int *counter)
>>> {
>>>     int r0;
>>>
>>>     r0 = READ_ONCE(*counter);
>>> }
>>>
>>> exists (2:r0=2)
>>>
>>> Note that I also changed the reading of theft variable
>>> to smp_load_acquire in add_count/sub_count's fast path
>>> to make sure that reading theft happens before reading
>>> counter.
>>>
>>> Signed-off-by: Alan Huang <mmpgouride@xxxxxxxxx>
>>
>> Looks good to me.
>> Reviewed-by: Akira Yokosawa <akiyks@xxxxxxxxx>

Paul, somehow commit c72371753921 ("CodeSamples/count: Add necessary
partial memory barriers") missed the "Reviewd-by:" part.

Can you fill it in if it's not too late?

> 
> Thank you both!
> 
>> Paul, there is a question for you.
>> Please see below.
>>
>>> ---
>>>  CodeSamples/count/count_lim_sig.c | 8 ++++----
>>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/CodeSamples/count/count_lim_sig.c b/CodeSamples/count/count_lim_sig.c
>>> index 023d6215..59da8077 100644
>>> --- a/CodeSamples/count/count_lim_sig.c
>>> +++ b/CodeSamples/count/count_lim_sig.c
>>> @@ -81,7 +81,7 @@ static void flush_local_count(void)			//\lnlbl{flush:b}
>>>  	for_each_tid(t, tid) {				//\lnlbl{flush:loop2:b}
>>>  		if (theftp[t] == NULL)			//\lnlbl{flush:skip:nonexist}
>>>  			continue;			//\lnlbl{flush:next2}
>>> -		while (READ_ONCE(*theftp[t]) != THEFT_READY) {//\lnlbl{flush:loop3:b}
>>> +		while (smp_load_acquire(theftp[t]) != THEFT_READY) {//\lnlbl{flush:loop3:b}
>>>  			poll(NULL, 0, 1);		//\lnlbl{flush:block}
>>>  			if (READ_ONCE(*theftp[t]) == THEFT_REQ)//\lnlbl{flush:check:REQ}
>>>  				pthread_kill(tid, SIGUSR1);//\lnlbl{flush:signal2}
>>> @@ -90,7 +90,7 @@ static void flush_local_count(void)			//\lnlbl{flush:b}
>>>  		*counterp[t] = 0;
>>>  		globalreserve -= *countermaxp[t];
>>>  		*countermaxp[t] = 0;			//\lnlbl{flush:thiev:e}
>>> -		WRITE_ONCE(*theftp[t], THEFT_IDLE);	//\lnlbl{flush:IDLE}
>>> +		smp_store_release(theftp[t], THEFT_IDLE);	//\lnlbl{flush:IDLE}
>>>  	}						//\lnlbl{flush:loop2:e}
>>>  }							//\lnlbl{flush:e}
>>>  
>>> @@ -116,7 +116,7 @@ int add_count(unsigned long delta)			//\lnlbl{b}
>>>  
>>>  	WRITE_ONCE(counting, 1);			//\lnlbl{fast:b}
>>>  	barrier();					//\lnlbl{barrier:1}
>>> -	if (READ_ONCE(theft) <= THEFT_REQ &&		//\lnlbl{check:b}
>>> +	if (smp_load_acquire(&theft) <= THEFT_REQ &&		//\lnlbl{check:b}
>>>  	    countermax - counter >= delta) {		//\lnlbl{check:e}
>>>  		WRITE_ONCE(counter, counter + delta);	//\lnlbl{add:f}
>>>  		fastpath = 1;				//\lnlbl{fasttaken}
>>> @@ -155,7 +155,7 @@ int sub_count(unsigned long delta)
>>>  
>>>  	WRITE_ONCE(counting, 1);
>>>  	barrier();
>>> -	if (READ_ONCE(theft) <= THEFT_REQ &&
>>> +	if (smp_load_acquire(&theft) <= THEFT_REQ &&
>>>  	    counter >= delta) {
>>>  		WRITE_ONCE(counter, counter - delta);
>>>  		fastpath = 1;
>>
>> In add_count() and sub_count(), is the plain accesses to counter
>> guaranteed to see the value stored in flush_local_count()?
>>
>> I'm asking because counter is declared as a thread-local variable:
>>
>> In this code, as each of the plain loads of counter is the first
>> access to the variable in each function, there is not much room
>> for compilers to exploit the plainness, I suppose.>>
>> What do you think?
> 
> Well, gitk thinks that the last deliberate change to this code was in
> 2010, before READ_ONCE() and WRITE_ONCE(), let alone LKMM.  So it needs
> careful scrutiny, along with the other counting algorithms.

I see.

FWIW, the documentation of GCC [1] states that thread-local variables
are allowed to be referenced via their addresses from any thread.
So compilers are _not_ allowed to exclude the possibility of updates
from other threads, it seems.

[1]: https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html

        Thanks, Akira

> 
> This commit looks like a step in the right direction, so I am applying
> it, thank you both!
> 
> Alan, would you like to drag the counting code kicking and screaming
> into the 2020s?  ;-)> 
> 							Thanx, Paul



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux