linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] ARM: OMAP: dmtimer: scheduling while atomic fixes
@ 2011-11-25  4:12 Omar Ramirez Luna
  2011-11-25  4:12 ` [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context Omar Ramirez Luna
  2011-11-25  4:12 ` [PATCH 2/2] ARM: OMAP: dmtimer: reorganize omap_dm_timer_request_* Omar Ramirez Luna
  0 siblings, 2 replies; 7+ messages in thread
From: Omar Ramirez Luna @ 2011-11-25  4:12 UTC (permalink / raw)
  To: linux-arm-kernel

Changes in dmtimer framework have introduced scheduling-while-atomic
and other lock state BUGs, these are related to the usage of clk_get
(internal path holds a mutex_lock) while holding a spin_lock_irqsave.

The other inconsistent lock state BUGs are caused when calling
omap_dm_timer_request* on softirq or hardirq, because code handling source
parent clocks is still using clk_get, since there is only one user of those
APIs that acquires a lock in a softirq context (tidspbridge) for now it
can be changed.

Omar Ramirez Luna (2):
  ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  ARM: OMAP: dmtimer: reorganize omap_dm_timer_request_*

 arch/arm/plat-omap/dmtimer.c              |  104 +++++++++++------------------
 arch/arm/plat-omap/include/plat/dmtimer.h |    6 +-
 2 files changed, 44 insertions(+), 66 deletions(-)

-- 
1.7.5.4

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  2011-11-25  4:12 [PATCH 0/2] ARM: OMAP: dmtimer: scheduling while atomic fixes Omar Ramirez Luna
@ 2011-11-25  4:12 ` Omar Ramirez Luna
  2011-12-09 21:34   ` Tony Lindgren
  2011-11-25  4:12 ` [PATCH 2/2] ARM: OMAP: dmtimer: reorganize omap_dm_timer_request_* Omar Ramirez Luna
  1 sibling, 1 reply; 7+ messages in thread
From: Omar Ramirez Luna @ 2011-11-25  4:12 UTC (permalink / raw)
  To: linux-arm-kernel

omap_dm_timer_request* holds a spin_lock_irqsave while inner routines call
clk_get_sys which holds a mutex_lock, given that mutex can be put to sleep
a BUG message is triggered. This occurs in 2 ocassions.

1. When the fck is gotten at the beginning of omap_dm_timer_prepare by using
   clk_get (which will call clk_get_sys), this was fixed by getting the clock
   handles on probe.

2. When omap_dm_timer_set_source tries to get the clock handles (with clk_get)
   for the fck and source clock, this was moved to be made after
   spin_unlock_irqsave when the context is not atomic anymore.

Signed-off-by: Omar Ramirez Luna <omar.ramirez@ti.com>
---
 arch/arm/plat-omap/dmtimer.c |   69 +++++++++++++++++++++++++----------------
 1 files changed, 42 insertions(+), 27 deletions(-)

diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index af3b92b..2acd4de 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -134,22 +134,13 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
 int omap_dm_timer_prepare(struct omap_dm_timer *timer)
 {
 	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
-	int ret;
-
-	timer->fclk = clk_get(&timer->pdev->dev, "fck");
-	if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
-		timer->fclk = NULL;
-		dev_err(&timer->pdev->dev, ": No fclk handle.\n");
-		return -EINVAL;
-	}
 
 	if (pdata->needs_manual_reset)
 		omap_dm_timer_reset(timer);
 
-	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
-
 	timer->posted = 1;
-	return ret;
+
+	return 0;
 }
 
 struct omap_dm_timer *omap_dm_timer_request(void)
@@ -168,19 +159,26 @@ struct omap_dm_timer *omap_dm_timer_request(void)
 		break;
 	}
 
-	if (timer) {
-		ret = omap_dm_timer_prepare(timer);
-		if (ret) {
-			timer->reserved = 0;
-			timer = NULL;
-		}
+	if (!timer) {
+		spin_unlock_irqrestore(&dm_timer_lock, flags);
+		goto err_no_timer;
 	}
+
+	omap_dm_timer_prepare(timer);
+
 	spin_unlock_irqrestore(&dm_timer_lock, flags);
 
-	if (!timer)
-		pr_debug("%s: timer request failed!\n", __func__);
+	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
+	if (ret) {
+		timer->reserved = 0;
+		goto err_no_timer;
+	}
 
 	return timer;
+
+err_no_timer:
+	pr_debug("%s: timer request failed!\n", __func__);
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(omap_dm_timer_request);
 
@@ -199,19 +197,26 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)
 		}
 	}
 
-	if (timer) {
-		ret = omap_dm_timer_prepare(timer);
-		if (ret) {
-			timer->reserved = 0;
-			timer = NULL;
-		}
+	if (!timer) {
+		spin_unlock_irqrestore(&dm_timer_lock, flags);
+		goto err_no_timer;
 	}
+
+	omap_dm_timer_prepare(timer);
+
 	spin_unlock_irqrestore(&dm_timer_lock, flags);
 
-	if (!timer)
-		pr_debug("%s: timer%d request failed!\n", __func__, id);
+	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
+	if (ret) {
+		timer->reserved = 0;
+		goto err_no_timer;
+	}
 
 	return timer;
+
+err_no_timer:
+	pr_debug("%s: timer%d request failed!\n", __func__, id);
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
 
@@ -658,6 +663,14 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
 		goto err_free_mem;
 	}
 
+	timer->fclk = clk_get(&pdev->dev, "fck");
+	if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
+		timer->fclk = NULL;
+		dev_err(&pdev->dev, ": No fclk handle for id %d.\n", pdev->id);
+		ret = -EINVAL;
+		goto err_clk_handle;
+	}
+
 	timer->id = pdev->id;
 	timer->irq = irq->start;
 	timer->reserved = pdata->reserved;
@@ -686,6 +699,8 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
 
 	return 0;
 
+err_clk_handle:
+	iounmap(timer->io_base);
 err_free_mem:
 	kfree(timer);
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] ARM: OMAP: dmtimer: reorganize omap_dm_timer_request_*
  2011-11-25  4:12 [PATCH 0/2] ARM: OMAP: dmtimer: scheduling while atomic fixes Omar Ramirez Luna
  2011-11-25  4:12 ` [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context Omar Ramirez Luna
@ 2011-11-25  4:12 ` Omar Ramirez Luna
  1 sibling, 0 replies; 7+ messages in thread
From: Omar Ramirez Luna @ 2011-11-25  4:12 UTC (permalink / raw)
  To: linux-arm-kernel

omap_dm_timer_request and omap_dm_timer_request_specific have almost
the same code except for a conditional check and the parameters they
receive.

omap_dm_timer_request_specific can be sent a -1 parameter to contain
the behavior of omap_dm_timer_request and thus get rid of some lines
of code.

Signed-off-by: Omar Ramirez Luna <omar.ramirez@ti.com>
---
 arch/arm/plat-omap/dmtimer.c              |   83 ++++++++---------------------
 arch/arm/plat-omap/include/plat/dmtimer.h |    6 ++-
 2 files changed, 26 insertions(+), 63 deletions(-)

diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 2acd4de..aa7e6da 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -131,88 +131,49 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
 	timer->posted = 1;
 }
 
-int omap_dm_timer_prepare(struct omap_dm_timer *timer)
-{
-	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
-
-	if (pdata->needs_manual_reset)
-		omap_dm_timer_reset(timer);
-
-	timer->posted = 1;
-
-	return 0;
-}
-
-struct omap_dm_timer *omap_dm_timer_request(void)
+struct omap_dm_timer *omap_dm_timer_request_specific(int id)
 {
-	struct omap_dm_timer *timer = NULL, *t;
+	struct omap_dm_timer *t = NULL;
+	struct dmtimer_platform_data *pdata;
 	unsigned long flags;
 	int ret = 0;
 
 	spin_lock_irqsave(&dm_timer_lock, flags);
-	list_for_each_entry(t, &omap_timer_list, node) {
-		if (t->reserved)
-			continue;
-
-		timer = t;
-		timer->reserved = 1;
-		break;
-	}
 
-	if (!timer) {
-		spin_unlock_irqrestore(&dm_timer_lock, flags);
-		goto err_no_timer;
-	}
-
-	omap_dm_timer_prepare(timer);
-
-	spin_unlock_irqrestore(&dm_timer_lock, flags);
-
-	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
-	if (ret) {
-		timer->reserved = 0;
-		goto err_no_timer;
-	}
-
-	return timer;
-
-err_no_timer:
-	pr_debug("%s: timer request failed!\n", __func__);
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(omap_dm_timer_request);
-
-struct omap_dm_timer *omap_dm_timer_request_specific(int id)
-{
-	struct omap_dm_timer *timer = NULL, *t;
-	unsigned long flags;
-	int ret = 0;
+	if (id == -1) {
+		list_for_each_entry(t, &omap_timer_list, node) {
+			if (!t->reserved)
+				break;
+		}
 
-	spin_lock_irqsave(&dm_timer_lock, flags);
-	list_for_each_entry(t, &omap_timer_list, node) {
-		if (t->pdev->id == id && !t->reserved) {
-			timer = t;
-			timer->reserved = 1;
-			break;
+	} else {
+		list_for_each_entry(t, &omap_timer_list, node) {
+			if (t->pdev->id == id && !t->reserved)
+				break;
 		}
 	}
 
-	if (!timer) {
+	if (!t) {
 		spin_unlock_irqrestore(&dm_timer_lock, flags);
 		goto err_no_timer;
 	}
 
-	omap_dm_timer_prepare(timer);
+	t->reserved = 1;
+	t->posted = 1;
+
+	pdata = t->pdev->dev.platform_data;
+	if (pdata->needs_manual_reset)
+		omap_dm_timer_reset(t);
 
 	spin_unlock_irqrestore(&dm_timer_lock, flags);
 
-	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
+	ret = omap_dm_timer_set_source(t, OMAP_TIMER_SRC_32_KHZ);
 	if (ret) {
-		timer->reserved = 0;
+		t->reserved = 0;
 		goto err_no_timer;
 	}
 
-	return timer;
+	return t;
 
 err_no_timer:
 	pr_debug("%s: timer%d request failed!\n", __func__, id);
diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
index 9418f00..b8b563d 100644
--- a/arch/arm/plat-omap/include/plat/dmtimer.h
+++ b/arch/arm/plat-omap/include/plat/dmtimer.h
@@ -107,7 +107,6 @@ struct dmtimer_platform_data {
 	int (*get_context_loss_count)(struct device *dev);
 };
 
-struct omap_dm_timer *omap_dm_timer_request(void);
 struct omap_dm_timer *omap_dm_timer_request_specific(int timer_id);
 int omap_dm_timer_free(struct omap_dm_timer *timer);
 void omap_dm_timer_enable(struct omap_dm_timer *timer);
@@ -282,7 +281,10 @@ struct omap_dm_timer {
 	int (*get_context_loss_count)(struct device *dev);
 };
 
-int omap_dm_timer_prepare(struct omap_dm_timer *timer);
+static inline struct omap_dm_timer *omap_dm_timer_request(void)
+{
+	return omap_dm_timer_request_specific(-1);
+}
 
 static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
 						int posted)
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  2011-11-25  4:12 ` [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context Omar Ramirez Luna
@ 2011-12-09 21:34   ` Tony Lindgren
  2011-12-09 22:10     ` Ramirez Luna, Omar
  0 siblings, 1 reply; 7+ messages in thread
From: Tony Lindgren @ 2011-12-09 21:34 UTC (permalink / raw)
  To: linux-arm-kernel

* Omar Ramirez Luna <omar.ramirez@ti.com> [111124 19:37]:
> omap_dm_timer_request* holds a spin_lock_irqsave while inner routines call
> clk_get_sys which holds a mutex_lock, given that mutex can be put to sleep
> a BUG message is triggered. This occurs in 2 ocassions.
> 
> 1. When the fck is gotten at the beginning of omap_dm_timer_prepare by using
>    clk_get (which will call clk_get_sys), this was fixed by getting the clock
>    handles on probe.
> 
> 2. When omap_dm_timer_set_source tries to get the clock handles (with clk_get)
>    for the fck and source clock, this was moved to be made after
>    spin_unlock_irqsave when the context is not atomic anymore.
> 
> @@ -168,19 +159,26 @@ struct omap_dm_timer *omap_dm_timer_request(void)
>  		break;
>  	}
>  
> -	if (timer) {
> -		ret = omap_dm_timer_prepare(timer);
> -		if (ret) {
> -			timer->reserved = 0;
> -			timer = NULL;
> -		}
> +	if (!timer) {
> +		spin_unlock_irqrestore(&dm_timer_lock, flags);
> +		goto err_no_timer;
>  	}
> +
> +	omap_dm_timer_prepare(timer);
> +
>  	spin_unlock_irqrestore(&dm_timer_lock, flags);
>  
> -	if (!timer)
> -		pr_debug("%s: timer request failed!\n", __func__);
> +	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
> +	if (ret) {
> +		timer->reserved = 0;
> +		goto err_no_timer;
> +	}
>  
>  	return timer;
> +
> +err_no_timer:
> +	pr_debug("%s: timer request failed!\n", __func__);
> +	return NULL;
>  }
>  EXPORT_SYMBOL_GPL(omap_dm_timer_request);

This does not seem right.. It seems that you're hardcoding the source
clock to 32 KiHz clock while other sources are available too?
  
> @@ -199,19 +197,26 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)
>  		}
>  	}
>  
> -	if (timer) {
> -		ret = omap_dm_timer_prepare(timer);
> -		if (ret) {
> -			timer->reserved = 0;
> -			timer = NULL;
> -		}
> +	if (!timer) {
> +		spin_unlock_irqrestore(&dm_timer_lock, flags);
> +		goto err_no_timer;
>  	}
> +
> +	omap_dm_timer_prepare(timer);
> +
>  	spin_unlock_irqrestore(&dm_timer_lock, flags);
>  
> -	if (!timer)
> -		pr_debug("%s: timer%d request failed!\n", __func__, id);
> +	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
> +	if (ret) {
> +		timer->reserved = 0;
> +		goto err_no_timer;
> +	}

And here too?

Regards,

Tony

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  2011-12-09 21:34   ` Tony Lindgren
@ 2011-12-09 22:10     ` Ramirez Luna, Omar
  2011-12-12 23:08       ` Tony Lindgren
  0 siblings, 1 reply; 7+ messages in thread
From: Ramirez Luna, Omar @ 2011-12-09 22:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Dec 9, 2011 at 3:34 PM, Tony Lindgren <tony@atomide.com> wrote:
>> + ? ? ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
...
>> ?EXPORT_SYMBOL_GPL(omap_dm_timer_request);
>
> This does not seem right.. It seems that you're hardcoding the source
> clock to 32 KiHz clock while other sources are available too?

Agree, but... (below)

>> + ? ? ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
...
> And here too?

Agree but that is the default behavior set by dm timer framework:

@@ -134,22 +134,13 @@ static void omap_dm_timer_reset(struct
omap_dm_timer *timer)
 int omap_dm_timer_prepare(struct omap_dm_timer *timer)
 {
...
-       ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
...

All clocks requested are set to 32 KHz first, even with the current
approach, there exists another API to set a new source.

To be honest I don't know why the clocks are set to 32 KHz first,
maybe the default call path for users should be:

omap_dm_timer_request
omap_dm_timer_set_source (new explicit call)
omap_dm_timer_start

And remove setting the source to 32 KHz by default in omap_dm_timer_request.

Regards,

Omar

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  2011-12-09 22:10     ` Ramirez Luna, Omar
@ 2011-12-12 23:08       ` Tony Lindgren
  2011-12-13  1:49         ` Ramirez Luna, Omar
  0 siblings, 1 reply; 7+ messages in thread
From: Tony Lindgren @ 2011-12-12 23:08 UTC (permalink / raw)
  To: linux-arm-kernel

* Ramirez Luna, Omar <omar.ramirez@ti.com> [111209 13:38]:
> On Fri, Dec 9, 2011 at 3:34 PM, Tony Lindgren <tony@atomide.com> wrote:
> >> + ? ? ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
> ...
> >> ?EXPORT_SYMBOL_GPL(omap_dm_timer_request);
> >
> > This does not seem right.. It seems that you're hardcoding the source
> > clock to 32 KiHz clock while other sources are available too?
> 
> Agree, but... (below)
> 
> >> + ? ? ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
> ...
> > And here too?
> 
> Agree but that is the default behavior set by dm timer framework:
> 
> @@ -134,22 +134,13 @@ static void omap_dm_timer_reset(struct
> omap_dm_timer *timer)
>  int omap_dm_timer_prepare(struct omap_dm_timer *timer)
>  {
> ...
> -       ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
> ...
> 
> All clocks requested are set to 32 KHz first, even with the current
> approach, there exists another API to set a new source.
> 
> To be honest I don't know why the clocks are set to 32 KHz first,
> maybe the default call path for users should be:

You need a functional clock for the timer registers to work
I believe.

> omap_dm_timer_request

Yes this would make sense. The omap_timer_list should be protected
by a mutex. There should not be a need for spinlock there as
omap_dm_timer_request should be only called during init. If that's
not the case, the the driver using it is broken.

> omap_dm_timer_set_source (new explicit call)
> omap_dm_timer_start

Once the timer has been requested, there should not be a need
for locking as there should be only one timer user using the
timer specific registers.
 
> And remove setting the source to 32 KHz by default in omap_dm_timer_request.

That you may need to be able to do anything with the timer :)

Regards,

Tony

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context
  2011-12-12 23:08       ` Tony Lindgren
@ 2011-12-13  1:49         ` Ramirez Luna, Omar
  0 siblings, 0 replies; 7+ messages in thread
From: Ramirez Luna, Omar @ 2011-12-13  1:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Dec 12, 2011 at 5:08 PM, Tony Lindgren <tony@atomide.com> wrote:
...
>> @@ -134,22 +134,13 @@ static void omap_dm_timer_reset(struct
>> omap_dm_timer *timer)
>> ?int omap_dm_timer_prepare(struct omap_dm_timer *timer)
>> ?{
>> ...
>> - ? ? ? ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
>> ...
>>
>> All clocks requested are set to 32 KHz first, even with the current
>> approach, there exists another API to set a new source.
>>
>> To be honest I don't know why the clocks are set to 32 KHz first,
>> maybe the default call path for users should be:
>
> You need a functional clock for the timer registers to work
> I believe.

Sounds logic :)

>> omap_dm_timer_request
>
> Yes this would make sense. The omap_timer_list should be protected
> by a mutex. There should not be a need for spinlock there as
> omap_dm_timer_request should be only called during init. If that's
> not the case, the the driver using it is broken.

Ok, I made this patch thinking that 'request' could be called from any
context, but if that is not the case mutex should be fine.

>> omap_dm_timer_set_source (new explicit call)
>> omap_dm_timer_start
>
> Once the timer has been requested, there should not be a need
> for locking as there should be only one timer user using the
> timer specific registers.
>
>> And remove setting the source to 32 KHz by default in omap_dm_timer_request.
>
> That you may need to be able to do anything with the timer :)

Well the intention was for the user to call it explicitly so it didn't
look as a hard coded setting, but I can keep it.

IIUC, mutex should be held instead of spin lock, I can do the change
instead of this patch and see how it goes.

Thanks,

Omar

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-12-13  1:49 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-25  4:12 [PATCH 0/2] ARM: OMAP: dmtimer: scheduling while atomic fixes Omar Ramirez Luna
2011-11-25  4:12 ` [PATCH 1/2] ARM: OMAP: dmtimer: fix sleeping function called from invalid context Omar Ramirez Luna
2011-12-09 21:34   ` Tony Lindgren
2011-12-09 22:10     ` Ramirez Luna, Omar
2011-12-12 23:08       ` Tony Lindgren
2011-12-13  1:49         ` Ramirez Luna, Omar
2011-11-25  4:12 ` [PATCH 2/2] ARM: OMAP: dmtimer: reorganize omap_dm_timer_request_* Omar Ramirez Luna

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).