[PATCH 6/6] regulator: core: simplify lock_two()

Michał Mirosław posted 6 patches 2 years, 3 months ago
[PATCH 6/6] regulator: core: simplify lock_two()
Posted by Michał Mirosław 2 years, 3 months ago
Make regulator_lock_two() shorter by observing that we have only two
locks and when swapped earlier the retry code becomes identical to the
normal (optimistic) path.

Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 drivers/regulator/core.c | 28 ++++++++++------------------
 1 file changed, 10 insertions(+), 18 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d8c2277cea36..9736507b62ff 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -201,37 +201,29 @@ static int regulator_lock_two(struct regulator_dev *rdev1,
 			      struct regulator_dev *rdev2,
 			      struct ww_acquire_ctx *ww_ctx)
 {
-	struct regulator_dev *held, *contended;
 	int ret;
 
 	ww_acquire_init(ww_ctx, &regulator_ww_class);
 
-	/* Try to just grab both of them */
 	ret = regulator_lock_nested(rdev1, ww_ctx);
 	if (WARN_ON(ret))
 		goto exit;
-	ret = regulator_lock_nested(rdev2, ww_ctx);
-	if (!ret)
-		return 0;
-	if (WARN_ON(ret != -EDEADLOCK)) {
-		regulator_unlock(rdev1);
-		goto exit;
-	}
 
-	held = rdev1;
-	contended = rdev2;
 	while (true) {
-		regulator_unlock(held);
-
-		ww_mutex_lock_slow(&contended->mutex, ww_ctx);
-		contended->ref_cnt++;
-		contended->mutex_owner = current;
-		swap(held, contended);
-		ret = regulator_lock_nested(contended, ww_ctx);
+		ret = regulator_lock_nested(rdev2, ww_ctx);
 		if (!ret)
 			return 0;
+
+		regulator_unlock(rdev1);
+
 		if (WARN_ON(ret != -EDEADLOCK))
 			break;
+
+		swap(rdev1, rdev2);
+
+		ww_mutex_lock_slow(&rdev1->mutex, ww_ctx);
+		rdev1->ref_cnt++;
+		rdev1->mutex_owner = current;
 	}
 
 exit:
-- 
2.39.2

Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Doug Anderson 2 years, 3 months ago
Hi,

On Sat, Aug 19, 2023 at 3:46 PM Michał Mirosław <mirq-linux@rere.qmqm.pl> wrote:
>
> Make regulator_lock_two() shorter by observing that we have only two
> locks and when swapped earlier the retry code becomes identical to the
> normal (optimistic) path.
>
> Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> ---
>  drivers/regulator/core.c | 28 ++++++++++------------------
>  1 file changed, 10 insertions(+), 18 deletions(-)

This is quite nearly a direct revert of commit 37473397b852
("regulator: core: Make regulator_lock_two() logic easier to follow"),
which was requested by Stephen in:

https://lore.kernel.org/r/CAE-0n53Eb1BeDPmjBycXUaQAF4ppiAM6UDWje_jiB9GAmR8MMw@mail.gmail.com

I don't personally have a strong opinion, but do prefer not to flip-flop. ;-)


-Doug
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Michał Mirosław 2 years, 3 months ago
On Mon, Aug 21, 2023 at 09:51:08AM -0700, Doug Anderson wrote:
> Hi,
> 
> On Sat, Aug 19, 2023 at 3:46 PM Michał Mirosław <mirq-linux@rere.qmqm.pl> wrote:
> >
> > Make regulator_lock_two() shorter by observing that we have only two
> > locks and when swapped earlier the retry code becomes identical to the
> > normal (optimistic) path.
> >
> > Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
> > ---
> >  drivers/regulator/core.c | 28 ++++++++++------------------
> >  1 file changed, 10 insertions(+), 18 deletions(-)
> 
> This is quite nearly a direct revert of commit 37473397b852
> ("regulator: core: Make regulator_lock_two() logic easier to follow"),
> which was requested by Stephen in:
> 
> https://lore.kernel.org/r/CAE-0n53Eb1BeDPmjBycXUaQAF4ppiAM6UDWje_jiB9GAmR8MMw@mail.gmail.com
> 
> I don't personally have a strong opinion, but do prefer not to flip-flop. ;-)

Indeed they are quite similar. I did remove a bit more code than that,
though: in this case there is no early success return before the loop.

Instead of saying:

lock A
lock B
if ok return
if that failed, loop:
  unlock A
  lock B harder
  lock A
  if ok return
  swap A <-> B
  lock B

Now it's:

lock A
loop forever:
  lock B
  if ok, return
  unlock A
  swap them
  lock A harder

With the same condition 'A held' at the start of an iteration.

Best Regards
Michał Mirosław
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Stephen Boyd 2 years, 3 months ago
Quoting Michał Mirosław (2023-08-28 13:26:54)
> Indeed they are quite similar. I did remove a bit more code than that,
> though: in this case there is no early success return before the loop.
>
> Instead of saying:
>
> lock A
> lock B
> if ok return
> if that failed, loop:
>   unlock A
>   lock B harder
>   lock A
>   if ok return
>   swap A <-> B
>   lock B
>
> Now it's:
>
> lock A
> loop forever:
>   lock B
>   if ok, return
>   unlock A
>   swap them
>   lock A harder
>
> With the same condition 'A held' at the start of an iteration.
>

Removing duplicate code is great! I'm primarily concerned with
readability. The terms 'A' and 'B' doesn't make it easy for me. Can you
maintain the 'held' and 'contended' names for the variables?

That would be

 lock 'held'
 loop forever:
   lock 'contended'
   if ok, return
   unlock 'held'
   swap them
   lock 'held' harder

per the psuedo-code.
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Michał Mirosław 2 years, 3 months ago
On Tue, Aug 29, 2023 at 03:52:19PM -0500, Stephen Boyd wrote:
> Quoting Michał Mirosław (2023-08-28 13:26:54)
> > Indeed they are quite similar. I did remove a bit more code than that,
> > though: in this case there is no early success return before the loop.
> >
> > Instead of saying:
> >
> > lock A
> > lock B
> > if ok return
> > if that failed, loop:
> >   unlock A
> >   lock B harder
> >   lock A
> >   if ok return
> >   swap A <-> B
> >   lock B
> >
> > Now it's:
> >
> > lock A
> > loop forever:
> >   lock B
> >   if ok, return
> >   unlock A
> >   swap them
> >   lock A harder
> >
> > With the same condition 'A held' at the start of an iteration.
> >
> 
> Removing duplicate code is great! I'm primarily concerned with
> readability. The terms 'A' and 'B' doesn't make it easy for me. Can you
> maintain the 'held' and 'contended' names for the variables?
> 
> That would be
> 
> 1.  lock 'held'
> 2.  loop forever:
> 3.    lock 'contended'
> 4.    if ok, return
> 5.    unlock 'held'
> 6.    swap them
> 7.    lock 'held' harder

Doesn't this make it more confusing? The lock is 'held' only in lines
2-5 and looses this trait (but not the name) on the other lines.
'contended' is more problematic: the contended lock is called 'held'
before locking it at line 7.

The algorithm is basically: Take the locks in sequence. If that failed,
swap the order and try again.

Would a comment like the sentence above help with readability?

Or we could wrap the final lines of the iteration in a
'regulator_lock_contended()' to make it self-documenting?

Best Regards
Michał Mirosław
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Stephen Boyd 2 years, 3 months ago
Quoting Michał Mirosław (2023-08-29 14:25:46)
> On Tue, Aug 29, 2023 at 03:52:19PM -0500, Stephen Boyd wrote:
> > Quoting Michał Mirosław (2023-08-28 13:26:54)
> > > Indeed they are quite similar. I did remove a bit more code than that,
> > > though: in this case there is no early success return before the loop.
> > >
> > > Instead of saying:
> > >
> > > lock A
> > > lock B
> > > if ok return
> > > if that failed, loop:
> > >   unlock A
> > >   lock B harder
> > >   lock A
> > >   if ok return
> > >   swap A <-> B
> > >   lock B
> > >
> > > Now it's:
> > >
> > > lock A
> > > loop forever:
> > >   lock B
> > >   if ok, return
> > >   unlock A
> > >   swap them
> > >   lock A harder
> > >
> > > With the same condition 'A held' at the start of an iteration.
> > >
> >
> > Removing duplicate code is great! I'm primarily concerned with
> > readability. The terms 'A' and 'B' doesn't make it easy for me. Can you
> > maintain the 'held' and 'contended' names for the variables?
> >
> > That would be
> >
> > 1.  lock 'held'
> > 2.  loop forever:
> > 3.    lock 'contended'
> > 4.    if ok, return
> > 5.    unlock 'held'
> > 6.    swap them
> > 7.    lock 'held' harder
>
> Doesn't this make it more confusing? The lock is 'held' only in lines
> 2-5 and looses this trait (but not the name) on the other lines.
> 'contended' is more problematic: the contended lock is called 'held'
> before locking it at line 7.
>
> The algorithm is basically: Take the locks in sequence. If that failed,
> swap the order and try again.
>
> Would a comment like the sentence above help with readability?
>
> Or we could wrap the final lines of the iteration in a
> 'regulator_lock_contended()' to make it self-documenting?
>

Squash this in?

---8<---
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 9736507b62ff..39205cf00fb7 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -201,6 +201,7 @@ static int regulator_lock_two(struct regulator_dev *rdev1,
 			      struct regulator_dev *rdev2,
 			      struct ww_acquire_ctx *ww_ctx)
 {
+	struct regulator_dev *held, *contended;
 	int ret;

 	ww_acquire_init(ww_ctx, &regulator_ww_class);
@@ -208,22 +209,24 @@ static int regulator_lock_two(struct regulator_dev *rdev1,
 	ret = regulator_lock_nested(rdev1, ww_ctx);
 	if (WARN_ON(ret))
 		goto exit;
+	held = rdev1;
+	contended = rdev2;

 	while (true) {
-		ret = regulator_lock_nested(rdev2, ww_ctx);
+		ret = regulator_lock_nested(contended, ww_ctx);
 		if (!ret)
-			return 0;
+			break;

-		regulator_unlock(rdev1);
+		regulator_unlock(held);

 		if (WARN_ON(ret != -EDEADLOCK))
 			break;

-		swap(rdev1, rdev2);
+		ww_mutex_lock_slow(&contended->mutex, ww_ctx);
+		contended->ref_cnt++;
+		contended->mutex_owner = current;

-		ww_mutex_lock_slow(&rdev1->mutex, ww_ctx);
-		rdev1->ref_cnt++;
-		rdev1->mutex_owner = current;
+		swap(held, contended);
 	}

 exit:
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Michał Mirosław 2 years, 3 months ago
On Tue, Aug 29, 2023 at 05:57:31PM -0500, Stephen Boyd wrote:
> Quoting Michał Mirosław (2023-08-29 14:25:46)
> > On Tue, Aug 29, 2023 at 03:52:19PM -0500, Stephen Boyd wrote:
> > > Quoting Michał Mirosław (2023-08-28 13:26:54)
> > > > Indeed they are quite similar. I did remove a bit more code than that,
> > > > though: in this case there is no early success return before the loop.
> > > >
> > > > Instead of saying:
> > > >
> > > > lock A
> > > > lock B
> > > > if ok return
> > > > if that failed, loop:
> > > >   unlock A
> > > >   lock B harder
> > > >   lock A
> > > >   if ok return
> > > >   swap A <-> B
> > > >   lock B
> > > >
> > > > Now it's:
> > > >
> > > > lock A
> > > > loop forever:
> > > >   lock B
> > > >   if ok, return
> > > >   unlock A
> > > >   swap them
> > > >   lock A harder
> > > >
> > > > With the same condition 'A held' at the start of an iteration.
> > > >
> > >
> > > Removing duplicate code is great! I'm primarily concerned with
> > > readability. The terms 'A' and 'B' doesn't make it easy for me. Can you
> > > maintain the 'held' and 'contended' names for the variables?
> > >
> > > That would be
> > >
> > > 1.  lock 'held'
> > > 2.  loop forever:
> > > 3.    lock 'contended'
> > > 4.    if ok, return
> > > 5.    unlock 'held'
> > > 6.    swap them
> > > 7.    lock 'held' harder
> >
> > Doesn't this make it more confusing? The lock is 'held' only in lines
> > 2-5 and looses this trait (but not the name) on the other lines.
> > 'contended' is more problematic: the contended lock is called 'held'
> > before locking it at line 7.
> >
> > The algorithm is basically: Take the locks in sequence. If that failed,
> > swap the order and try again.
> >
> > Would a comment like the sentence above help with readability?
> >
> > Or we could wrap the final lines of the iteration in a
> > 'regulator_lock_contended()' to make it self-documenting?
> >
> 
> Squash this in?

I see that you prefer the held/contended intermediary names. I'd like to
understand why we differ in the perception of readability of this part
of code so that the change is needed? The algorithm is simple enough,
and it would work even if the locks weren't swapped for each iteration
(even if slower to finish). What is missing in the context of the
function's code? Are there some assumptions not easily visible?

BTW, I went on to add the regulator_lock_contended() to see how it
would look. I'll send a v2 with it so we can apply your proposal if
needed on top.

Best Regards,
Michał Mirosław
Re: [PATCH 6/6] regulator: core: simplify lock_two()
Posted by Stephen Boyd 2 years, 3 months ago
Quoting Michał Mirosław (2023-08-30 10:25:22)
>
> I see that you prefer the held/contended intermediary names. I'd like to
> understand why we differ in the perception of readability of this part
> of code so that the change is needed? The algorithm is simple enough,
> and it would work even if the locks weren't swapped for each iteration
> (even if slower to finish).

The locks need to be swapped per the documentation above
ww_mutex_lock_slow(). This is how we ensure forward progress.

> What is missing in the context of the
> function's code? Are there some assumptions not easily visible?

Yes, there are assumptions that are harder to keep track of with the
names 'rdev1' and 'rdev2'. We have to make sure that we call
ww_mutex_lock_slow() on the contended mutex, not the one that we could
get first. I find it easier to keep track of which regulator is
contended and which regulator is held by having the local variable
names. It's not up to me though as I'm not the maintainer here.

>
> BTW, I went on to add the regulator_lock_contended() to see how it
> would look. I'll send a v2 with it so we can apply your proposal if
> needed on top.

Got it.