diff mbox

[v2,4/9] clocksource/cadence_ttc: Use enable/disable_irq

Message ID 1385514296-26702-5-git-send-email-soren.brinkmann@xilinx.com (mailing list archive)
State New, archived
Headers show

Commit Message

Soren Brinkmann Nov. 27, 2013, 1:04 a.m. UTC
To ensure that the timer interrupt is properly enabled/disabled across
the whole CPU cluster use enable/disable_irq() instead of
local_irq_disable().

Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
---
 drivers/clocksource/cadence_ttc_timer.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

Comments

Daniel Lezcano Nov. 28, 2013, 11:55 a.m. UTC | #1
On 11/27/2013 02:04 AM, Soren Brinkmann wrote:
> To ensure that the timer interrupt is properly enabled/disabled across
> the whole CPU cluster use enable/disable_irq() instead of
> local_irq_disable().
>
> Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> ---
>   drivers/clocksource/cadence_ttc_timer.c | 6 ++----
>   1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
> index a92350b55d32..246d018d1e63 100644
> --- a/drivers/clocksource/cadence_ttc_timer.c
> +++ b/drivers/clocksource/cadence_ttc_timer.c
> @@ -322,18 +322,16 @@ static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
>   	switch (event) {
>   	case POST_RATE_CHANGE:
>   	{
> -		unsigned long flags;
> -
>   		/*
>   		 * clockevents_update_freq should be called with IRQ disabled on
>   		 * the CPU the timer provides events for. The timer we use is
>   		 * common to both CPUs, not sure if we need to run on both
>   		 * cores.
>   		 */
> -		local_irq_save(flags);
> +		disable_irq(ttcce->ce.irq);
>   		clockevents_update_freq(&ttcce->ce,
>   				ndata->new_rate / PRESCALE);
> -		local_irq_restore(flags);
> +		enable_irq(ttcce->ce.irq);
>
>   		/* update cached frequency */
>   		ttc->freq = ndata->new_rate;
>

I am worried about the 'disable_irq' function calling 'synchronize_irq'. 
Isn't possible to deadlock with the ondemand cpufreq governor ? I added 
Viresh in Cc, he knows better than me the code path.
Thomas Gleixner Nov. 28, 2013, 2:18 p.m. UTC | #2
On Thu, 28 Nov 2013, Daniel Lezcano wrote:

> On 11/27/2013 02:04 AM, Soren Brinkmann wrote:
> > To ensure that the timer interrupt is properly enabled/disabled across
> > the whole CPU cluster use enable/disable_irq() instead of
> > local_irq_disable().
> > 
> > Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> > ---
> >   drivers/clocksource/cadence_ttc_timer.c | 6 ++----
> >   1 file changed, 2 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/clocksource/cadence_ttc_timer.c
> > b/drivers/clocksource/cadence_ttc_timer.c
> > index a92350b55d32..246d018d1e63 100644
> > --- a/drivers/clocksource/cadence_ttc_timer.c
> > +++ b/drivers/clocksource/cadence_ttc_timer.c
> > @@ -322,18 +322,16 @@ static int ttc_rate_change_clockevent_cb(struct
> > notifier_block *nb,
> >   	switch (event) {
> >   	case POST_RATE_CHANGE:
> >   	{
> > -		unsigned long flags;
> > -
> >   		/*
> >   		 * clockevents_update_freq should be called with IRQ disabled
> > on
> >   		 * the CPU the timer provides events for. The timer we use is
> >   		 * common to both CPUs, not sure if we need to run on both
> >   		 * cores.

Can we adjust that bogus comment as well, please?

> >   		 */
> > -		local_irq_save(flags);
> > +		disable_irq(ttcce->ce.irq);
> >   		clockevents_update_freq(&ttcce->ce,
> >   				ndata->new_rate / PRESCALE);
> > -		local_irq_restore(flags);
> > +		enable_irq(ttcce->ce.irq);
> > 
> >   		/* update cached frequency */
> >   		ttc->freq = ndata->new_rate;
> > 
> 
> I am worried about the 'disable_irq' function calling 'synchronize_irq'. Isn't
> possible to deadlock with the ondemand cpufreq governor ? I added Viresh in
> Cc, he knows better than me the code path.

disable_irq() will only deadlock if called from the interrupt handler
of the device interrupt itself or when the calling code is holding
locks which prevent the function to proceed.

But what's more important is, that the patch violates the calling
convention of clockevents_update_freq(). It must be called with
interrupts disabled. 

Now the problem with this device is that it is not a per cpu
device. It's a global device, so this update can conflict with a
parallel access on the other CPU. Now the disable_irq() only prevents
that the other CPU can handle a device interrupt from that timer. But
it does not prevent any parallel access from e.g. the idle code path
which will try to reprogram it.

Soren, is that timer used as the broadcast device ?

Thanks,

	tglx
Soren Brinkmann Nov. 28, 2013, 6:36 p.m. UTC | #3
Hi Thomas,

On Thu, Nov 28, 2013 at 03:18:50PM +0100, Thomas Gleixner wrote:
> On Thu, 28 Nov 2013, Daniel Lezcano wrote:
> 
> > On 11/27/2013 02:04 AM, Soren Brinkmann wrote:
> > > To ensure that the timer interrupt is properly enabled/disabled across
> > > the whole CPU cluster use enable/disable_irq() instead of
> > > local_irq_disable().
> > > 
> > > Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> > > ---
> > >   drivers/clocksource/cadence_ttc_timer.c | 6 ++----
> > >   1 file changed, 2 insertions(+), 4 deletions(-)
> > > 
> > > diff --git a/drivers/clocksource/cadence_ttc_timer.c
> > > b/drivers/clocksource/cadence_ttc_timer.c
> > > index a92350b55d32..246d018d1e63 100644
> > > --- a/drivers/clocksource/cadence_ttc_timer.c
> > > +++ b/drivers/clocksource/cadence_ttc_timer.c
> > > @@ -322,18 +322,16 @@ static int ttc_rate_change_clockevent_cb(struct
> > > notifier_block *nb,
> > >   	switch (event) {
> > >   	case POST_RATE_CHANGE:
> > >   	{
> > > -		unsigned long flags;
> > > -
> > >   		/*
> > >   		 * clockevents_update_freq should be called with IRQ disabled
> > > on
> > >   		 * the CPU the timer provides events for. The timer we use is
> > >   		 * common to both CPUs, not sure if we need to run on both
> > >   		 * cores.
> 
> Can we adjust that bogus comment as well, please?
I can remove that comment. I'll see if including it in one of the
existing patches or adding another patch for it makes sense.

> 
> > >   		 */
> > > -		local_irq_save(flags);
> > > +		disable_irq(ttcce->ce.irq);
> > >   		clockevents_update_freq(&ttcce->ce,
> > >   				ndata->new_rate / PRESCALE);
> > > -		local_irq_restore(flags);
> > > +		enable_irq(ttcce->ce.irq);
> > > 
> > >   		/* update cached frequency */
> > >   		ttc->freq = ndata->new_rate;
> > > 
> > 
> > I am worried about the 'disable_irq' function calling 'synchronize_irq'. Isn't
> > possible to deadlock with the ondemand cpufreq governor ? I added Viresh in
> > Cc, he knows better than me the code path.
> 
> disable_irq() will only deadlock if called from the interrupt handler
> of the device interrupt itself or when the calling code is holding
> locks which prevent the function to proceed.
> 
> But what's more important is, that the patch violates the calling
> convention of clockevents_update_freq(). It must be called with
> interrupts disabled. 
> 
> Now the problem with this device is that it is not a per cpu
> device. It's a global device, so this update can conflict with a
> parallel access on the other CPU. Now the disable_irq() only prevents
> that the other CPU can handle a device interrupt from that timer. But
> it does not prevent any parallel access from e.g. the idle code path
> which will try to reprogram it.
Does that mean interrupts need to be disabled globally? Also, does the
cpuidle path depend on interrupts or can it interfere no matter what?

> 
> Soren, is that timer used as the broadcast device ?
Yes, this is the only broadcast capable timer on Zynq, AFAIK. Other than
the TTC we only have the arm_global_timer and smp_twd timers, which both
are per_cpu devices and thus not broadcast capable.

	Sören
diff mbox

Patch

diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index a92350b55d32..246d018d1e63 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -322,18 +322,16 @@  static int ttc_rate_change_clockevent_cb(struct notifier_block *nb,
 	switch (event) {
 	case POST_RATE_CHANGE:
 	{
-		unsigned long flags;
-
 		/*
 		 * clockevents_update_freq should be called with IRQ disabled on
 		 * the CPU the timer provides events for. The timer we use is
 		 * common to both CPUs, not sure if we need to run on both
 		 * cores.
 		 */
-		local_irq_save(flags);
+		disable_irq(ttcce->ce.irq);
 		clockevents_update_freq(&ttcce->ce,
 				ndata->new_rate / PRESCALE);
-		local_irq_restore(flags);
+		enable_irq(ttcce->ce.irq);
 
 		/* update cached frequency */
 		ttc->freq = ndata->new_rate;