[Devel] [RFC PATCH vz9 v6 06/62] dm-ploop: reduce the time lock is hold, taking it only to protect data
Alexander Atanasov
alexander.atanasov at virtuozzo.com
Thu Jan 16 12:36:28 MSK 2025
On 16.01.25 11:31, Pavel Tikhomirov wrote:
>
>
> On 1/14/25 20:05, Alexander Atanasov wrote:
>> On 14.01.25 4:27, Pavel Tikhomirov wrote:
>>>
>>>
>>> On 1/14/25 00:27, Alexander Atanasov wrote:
>>>> On 9.01.25 5:52, Pavel Tikhomirov wrote:
>>>>>
>>>>>
>>>>> On 12/6/24 05:55, Alexander Atanasov wrote:
>>>>>> Currently lock is taken once for all pios submitted(code block),
>>>>>> move it to protect only the list insertion (data).
>>>>>> The goal is to reduce lock contention on the deferred lock as
>>>>>> it is in the top of lock stats.
>>>>>>
>>>>>> https://virtuozzo.atlassian.net/browse/VSTOR-91820
>>>>>> Signed-off-by: Alexander Atanasov <alexander.atanasov at virtuozzo.com>
>>>>>> ---
>>>>>> drivers/md/dm-ploop-map.c | 8 ++++----
>>>>>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/md/dm-ploop-map.c b/drivers/md/dm-ploop-map.c
>>>>>> index 4b9e000e0f98..a0fe92a592a1 100644
>>>>>> --- a/drivers/md/dm-ploop-map.c
>>>>>> +++ b/drivers/md/dm-ploop-map.c
>>>>>> @@ -340,8 +340,9 @@ static void ploop_dispatch_pio(struct ploop
>>>>>> *ploop, struct pio *pio,
>>>>>> bool *is_data, bool *is_flush)
>>>>>> {
>>>>>> struct list_head *list = &ploop->pios[pio->queue_list_id];
>>>>>> + unsigned long flags;
>>>>>> - lockdep_assert_held(&ploop->deferred_lock);
>>>>>> + lockdep_assert_not_held(&ploop->deferred_lock);
>>>>>> WARN_ON_ONCE(pio->queue_list_id >= PLOOP_LIST_COUNT);
>>>>>> if (pio->queue_list_id == PLOOP_LIST_FLUSH)
>>>>>> @@ -349,23 +350,22 @@ static void ploop_dispatch_pio(struct ploop
>>>>>> *ploop, struct pio *pio,
>>>>>> else
>>>>>> *is_data = true;
>>>>>> + spin_lock_irqsave(&ploop->deferred_lock, flags);
>>>>>> list_add_tail(&pio->list, list);
>>>>>> + spin_unlock_irqrestore(&ploop->deferred_lock, flags);
>>>>>
>>>>> This patch can be merged to "[RFC PATCH vz9 v6 10/62] dm-ploop:
>>>>> convert the rest of the lists to use llist variant".
>>>>
>>>> I try to extra careful with list conversions, they bring hard to
>>>> find issues. So i prefer conversions to be in small steps.
>>>>
>>>>>
>>>>> Also I think that having one lock around the whole loop introduces
>>>>> much less lock contention than doing multiple take/release on each
>>>>> iteration of the loop.
>>>>
>>>>
>>>> This lock is gone later with move to llist.
>>>
>>> That is the main reason why it should be merged. There should be no
>>> reverts inside one series, moving lock unnecessarily and then
>>> removing it completely only brings unneeded confusion for the future
>>> reader of the code. [1]
>>
>> I checked various guides, do you have a reference for this ?
>>
>> The requirements listed are that:
>> - each patch is separate logical change
>
> > Each patch should be justifiable on its own merits.
>
> https://www.kernel.org/doc/html/v5.1/process/submitting-patches.html#separate-your-changes
>
> I don't see how adding code in one commit and then removing this code in
> another is justifiable, especially when adding code does not help in any
> way in the patch which removes it. I see it just as excess code.
>
It is not adding and then removing. It is moving a lock from one
function to another(logical change) then this lock is no longer required
and removed(logical change). Doing this in one pass is a shortcut.
>> - each patch compiles to a working kernel
>>
>> For me locking data instead of code is a logical change,
>> ,changing list usage is separate logical change and so on.
>>
>> If lock and list change are in one place it is easier to see.
>>
>> https://www.kernel.org/doc/html/v4.11/process/coding-style.html#codingstyle
>> https://www.ozlabs.org/~akpm/stuff/tpp.txt
>> https://halobates.de/on-submitting-patches.pdf
>>
>>>
>>> Usually there is just one
>>>> entry in the list most users dispatch single pio - locking the list
>>>> when it is necessary reduces the time lock is held - locking data
>>>> instead of locking code .
>>>
>>> This kind of optimization does not feel right to me. We should either
>>> optimize general case or have explicit optimizations for edge cases,
>>> e.g. in ploop_dispatch_pios "if (likely(pio))
>>> ploop_dispatch_pio_locked()", explicitly marking function which takes
>>> lock by itself with _locked suffix.
>>
>> This is not a optimisation. General case is one pio, exception is many
>> pios. difference is neglectable, in the end this function is only used
>> for more than one pio one some abnormal condition. So i do not see a
>> point to optimise it. Fine graine locking on data is better than
>> locking a 100 lines function code, just like your example for the
>> locks - you can loose sense of why a lock is necessary.
>>
>>>
>>> But all this is unimportant due to [1], should be just merged.
>>
>> Ok. i will check.
>>
>>>
>>>>
>>>>>
>>>>>> }
>>>>>> void ploop_dispatch_pios(struct ploop *ploop, struct pio *pio,
>>>>>> struct list_head *pio_list)
>>>>>> {
>>>>>> bool is_data = false, is_flush = false;
>>>>>> - unsigned long flags;
>>>>>> - spin_lock_irqsave(&ploop->deferred_lock, flags);
>>>>>> if (pio)
>>>>>> ploop_dispatch_pio(ploop, pio, &is_data, &is_flush);
>>>>>> if (pio_list) {
>>>>>> while ((pio = ploop_pio_list_pop(pio_list)) != NULL)
>>>>>> ploop_dispatch_pio(ploop, pio, &is_data, &is_flush);
>>>>>> }
>>>>>> - spin_unlock_irqrestore(&ploop->deferred_lock, flags);
>>>>>> if (is_data)
>>>>>> queue_work(ploop->wq, &ploop->worker);
>>>>>
>>>>
>>>
>>
>
--
Regards,
Alexander Atanasov
More information about the Devel
mailing list