[Devel] [PATCH vz9] dm-ploop: fix and rework md updates

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Wed Feb 12 13:14:25 MSK 2025



On 2/12/25 17:42, Alexander Atanasov wrote:
>>>
>>>> If you really want to read md->piwb to piwb variable BEFORE 
>>>> ploop_write_cluster_sync, you should use READ_ONCE, or other 
>>>> appropriately used memory barrier.
>>>>
>>>
>>> READ_ONCE is not a memory barrier - it is a COMPILER BARRIER.
>>> https://docs.kernel.org/core-api/wrappers/memory-barriers.html
>>> see COMPILER BARRIER section for indeep explanation. There you will 
>>> see that infact READ_ONCE forces variable to be re-read , and not 
>>> read once as in one time.
>>
>> Unrelated to the patch, as you've proved that patch is ok. But we've 
>> already had this discussion https://lists.openvz.org/pipermail/ 
>> devel/2025-January/081790.html , and I've provided you with citation 
>> from documentation from "COMPILER BARRIER". READ_ONCE disables 
>> different compiler optimisations, including that it does not allow 
>> local variable to be optimized by instead directly using the global 
>> variable which can be changed in concurrent thread, which looks from 
>> the user perspective as if local variable is read twice from global 
>> variable. So it actually makes reads "one time" reads.
> 
> No, it actually marks the variable volatile /i missed that/ and forces
> access as a whole. Which results in variable to be read every time 
> instead of cached one to be used, so it is not a one time read.
> 
> 
> The example from the docs:
> 
> 
>   (*) The compiler is within its rights to merge successive loads from
>       the same variable.  Such merging can cause the compiler to "optimize"
>       the following code:
> 
>          while (tmp = a)
>                  do_something_with(tmp);
> 
>       into the following code, which, although in some sense legitimate
>       for single-threaded code, is almost certainly not what the developer
>       intended:
> 
>          if (tmp = a)
>                  for (;;)
>                          do_something_with(tmp);
> 
>       Use READ_ONCE() to prevent the compiler from doing this to you:
> 
>          while (tmp = READ_ONCE(a))
>                  do_something_with(tmp);
> 
> 
> 
> READ_ONCE(a) -> forces read every time - cast to volatile
> and the side efect is it can not split access in two
> 
> The last paragaph is what you missed:
> 
> "All that aside, it is never necessary to use READ_ONCE() and
> WRITE_ONCE() on a variable that has been marked volatile.  For example,
> because 'jiffies' is marked volatile, it is never necessary to
> say READ_ONCE(jiffies).  The reason for this is that READ_ONCE() and
> WRITE_ONCE() are implemented as volatile casts, which has no effect when
> its argument is already marked volatile.
> 
> Please note that these compiler barriers have no direct effect on the 
> CPU, which may then reorder things however it wishes."
> 
> 
> Yes we did but let's get on the same page.

Yes, I agree that the repeated READ_ONCE forces variable to update each 
time. As I previously say "READ_ONCE disables different compiler 
optimisations", so I don't argue that READ_ONCE does many things. But 
ALSO the single READ_ONCE forces variable to never be reloaded more then 
once. As in:

  (*) The compiler is within its rights to reload a variable, for example,
      in cases where high register pressure prevents the compiler from
      keeping all data of interest in registers.  The compiler might
      therefore optimize the variable 'tmp' out of our previous example:

         while (tmp = a)
                 do_something_with(tmp);

      This could result in the following code, which is perfectly safe in
      single-threaded code, but can be fatal in concurrent code:

         while (a)
                 do_something_with(a);

      For example, the optimized version of this code could result in
      passing a zero to do_something_with() in the case where the variable
      a was modified by some other CPU between the "while" statement and
      the call to do_something_with().

      Again, use READ_ONCE() to prevent the compiler from doing this:

         while (tmp = READ_ONCE(a))
                 do_something_with(tmp);

      Note that if the compiler runs short of registers, it might save
      tmp onto the stack.  The overhead of this saving and later restoring
      is why compilers reload variables.  Doing so is perfectly safe for
      single-threaded code, so you need to tell the compiler about cases
      where it is not safe.

-- 
Best regards, Tikhomirov Pavel
Senior Software Developer, Virtuozzo.



More information about the Devel mailing list