diff mbox

[RFC,v1,2/5] clk: add missing lock when call clk_core_enable in clk_set_parent

Message ID 1429107999-24413-3-git-send-email-aisheng.dong@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dong Aisheng April 15, 2015, 2:26 p.m. UTC
clk_core_enable is executed without &enable_clock in clk_set_parent function.
Adding it to avoid potential race condition issue.

Fixes: 035a61c314eb ("clk: Make clk API return per-user struct clk instances")
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Dong Aisheng <aisheng.dong@freescale.com>
---
 drivers/clk/clk.c | 8 ++++++++
 1 file changed, 8 insertions(+)

Comments

Stephen Boyd April 30, 2015, 7:07 p.m. UTC | #1
On 04/15/15 07:26, Dong Aisheng wrote:
> clk_core_enable is executed without &enable_clock in clk_set_parent function.
> Adding it to avoid potential race condition issue.
>
> Fixes: 035a61c314eb ("clk: Make clk API return per-user struct clk instances")
> Cc: Mike Turquette <mturquette@linaro.org>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Signed-off-by: Dong Aisheng <aisheng.dong@freescale.com>
> ---

Can you please describe the race condition? From what I can tell there
is not a race condition here and we've gone around on this part of the
code before to fix any race conditions.
Aisheng Dong May 4, 2015, 8:35 a.m. UTC | #2
On Thu, Apr 30, 2015 at 12:07:47PM -0700, Stephen Boyd wrote:
> On 04/15/15 07:26, Dong Aisheng wrote:
> > clk_core_enable is executed without &enable_clock in clk_set_parent function.
> > Adding it to avoid potential race condition issue.
> >
> > Fixes: 035a61c314eb ("clk: Make clk API return per-user struct clk instances")
> > Cc: Mike Turquette <mturquette@linaro.org>
> > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > Signed-off-by: Dong Aisheng <aisheng.dong@freescale.com>
> > ---
> 
> Can you please describe the race condition? From what I can tell there
> is not a race condition here and we've gone around on this part of the
> code before to fix any race conditions.
> 

Do you mean we do not need to acquire enable lock when execute clk_core_enable
in set_parent function? Can you help explain a bit more why?

The clk doc looks to me says the enable lock should be held across calls to
the .enable, .disable and .is_enabled operations.

And before the commit
035a61c314eb ("clk: Make clk API return per-user struct clk instances"),
all the clk_enable/disable in set_parent() is executed with lock.

A rough thinking of race condition is assuming Thread A calls
clk_set_parent(x, y) while Thread B calls clk_enable(x), clock x is disabled
but prepared initially, due to clk_core_enable in set_parent() is not
executed with enable clock, the clk_core_enable may be reentrant during
the locking time executed by B.
Won't this be a race condition?

Regards
Dong Aisheng

> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
Stephen Boyd May 7, 2015, 12:01 a.m. UTC | #3
On 05/04, Dong Aisheng wrote:
> On Thu, Apr 30, 2015 at 12:07:47PM -0700, Stephen Boyd wrote:
> > On 04/15/15 07:26, Dong Aisheng wrote:
> > > clk_core_enable is executed without &enable_clock in clk_set_parent function.
> > > Adding it to avoid potential race condition issue.
> > >
> > > Fixes: 035a61c314eb ("clk: Make clk API return per-user struct clk instances")
> > > Cc: Mike Turquette <mturquette@linaro.org>
> > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > Signed-off-by: Dong Aisheng <aisheng.dong@freescale.com>
> > > ---
> > 
> > Can you please describe the race condition? From what I can tell there
> > is not a race condition here and we've gone around on this part of the
> > code before to fix any race conditions.
> > 
> 
> Do you mean we do not need to acquire enable lock when execute clk_core_enable
> in set_parent function? Can you help explain a bit more why?
> 
> The clk doc looks to me says the enable lock should be held across calls to
> the .enable, .disable and .is_enabled operations.
> 
> And before the commit
> 035a61c314eb ("clk: Make clk API return per-user struct clk instances"),
> all the clk_enable/disable in set_parent() is executed with lock.
> 
> A rough thinking of race condition is assuming Thread A calls
> clk_set_parent(x, y) while Thread B calls clk_enable(x), clock x is disabled
> but prepared initially, due to clk_core_enable in set_parent() is not
> executed with enable clock, the clk_core_enable may be reentrant during
> the locking time executed by B.
> Won't this be a race condition?
> 

Ah I see now. The commit text could say something like this:

Before commit 035a61c314eb ("clk: Make clk API return per-user
struct clk instances") we acquired the enable_lock in
__clk_set_parent_{before,after}() by means of calling
clk_enable(). After commit 035a61c314eb we use clk_core_enable()
in place of the clk_enable(), and clk_core_enable() doesn't
acquire the enable_lock. This opens up a race condition between
clk_set_parent() and clk_enable().

I've replaced the commit text and applied it to clk-fixes.
Aisheng Dong May 13, 2015, 9:21 a.m. UTC | #4
On Wed, May 06, 2015 at 05:01:54PM -0700, Stephen Boyd wrote:
> On 05/04, Dong Aisheng wrote:
> > On Thu, Apr 30, 2015 at 12:07:47PM -0700, Stephen Boyd wrote:
> > > On 04/15/15 07:26, Dong Aisheng wrote:
> > > > clk_core_enable is executed without &enable_clock in clk_set_parent function.
> > > > Adding it to avoid potential race condition issue.
> > > >
> > > > Fixes: 035a61c314eb ("clk: Make clk API return per-user struct clk instances")
> > > > Cc: Mike Turquette <mturquette@linaro.org>
> > > > Cc: Stephen Boyd <sboyd@codeaurora.org>
> > > > Signed-off-by: Dong Aisheng <aisheng.dong@freescale.com>
> > > > ---
> > > 
> > > Can you please describe the race condition? From what I can tell there
> > > is not a race condition here and we've gone around on this part of the
> > > code before to fix any race conditions.
> > > 
> > 
> > Do you mean we do not need to acquire enable lock when execute clk_core_enable
> > in set_parent function? Can you help explain a bit more why?
> > 
> > The clk doc looks to me says the enable lock should be held across calls to
> > the .enable, .disable and .is_enabled operations.
> > 
> > And before the commit
> > 035a61c314eb ("clk: Make clk API return per-user struct clk instances"),
> > all the clk_enable/disable in set_parent() is executed with lock.
> > 
> > A rough thinking of race condition is assuming Thread A calls
> > clk_set_parent(x, y) while Thread B calls clk_enable(x), clock x is disabled
> > but prepared initially, due to clk_core_enable in set_parent() is not
> > executed with enable clock, the clk_core_enable may be reentrant during
> > the locking time executed by B.
> > Won't this be a race condition?
> > 
> 
> Ah I see now. The commit text could say something like this:
> 
> Before commit 035a61c314eb ("clk: Make clk API return per-user
> struct clk instances") we acquired the enable_lock in
> __clk_set_parent_{before,after}() by means of calling
> clk_enable(). After commit 035a61c314eb we use clk_core_enable()
> in place of the clk_enable(), and clk_core_enable() doesn't
> acquire the enable_lock. This opens up a race condition between
> clk_set_parent() and clk_enable().
> 
> I've replaced the commit text and applied it to clk-fixes.
> 

Got it.
Thanks for the change.

Regards
Dong Aisheng

> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
diff mbox

Patch

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index cc56ba2..492644f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1475,8 +1475,10 @@  static struct clk_core *__clk_set_parent_before(struct clk_core *clk,
 	 */
 	if (clk->prepare_count) {
 		clk_core_prepare(parent);
+		flags = clk_enable_lock();
 		clk_core_enable(parent);
 		clk_core_enable(clk);
+		clk_enable_unlock(flags);
 	}
 
 	/* update the clk tree topology */
@@ -1491,13 +1493,17 @@  static void __clk_set_parent_after(struct clk_core *clk,
 				   struct clk_core *parent,
 				   struct clk_core *old_parent)
 {
+	unsigned long flags;
+
 	/*
 	 * Finish the migration of prepare state and undo the changes done
 	 * for preventing a race with clk_enable().
 	 */
 	if (clk->prepare_count) {
+		flags = clk_enable_lock();
 		clk_core_disable(clk);
 		clk_core_disable(old_parent);
+		clk_enable_unlock(flags);
 		clk_core_unprepare(old_parent);
 	}
 }
@@ -1525,8 +1531,10 @@  static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent,
 		clk_enable_unlock(flags);
 
 		if (clk->prepare_count) {
+			flags = clk_enable_lock();
 			clk_core_disable(clk);
 			clk_core_disable(parent);
+			clk_enable_unlock(flags);
 			clk_core_unprepare(parent);
 		}
 		return ret;