Commit 6f5c8ac9 authored by Damian Eppel's avatar Damian Eppel Committed by Luis Henriques

clocksource: exynos_mct: Avoid blocking calls in the cpu hotplug notifier

commit 56a94f13 upstream.

Whilst testing cpu hotplug events on kernel configured with
DEBUG_PREEMPT and DEBUG_ATOMIC_SLEEP we get following BUG message,
caused by calling request_irq() and free_irq() in the context of
hotplug notification (which is in this case atomic context).

[   40.785859] CPU1: Software reset
[   40.786660] BUG: sleeping function called from invalid context at mm/slub.c:1241
[   40.786668] in_atomic(): 1, irqs_disabled(): 128, pid: 0, name: swapper/1
[   40.786678] Preemption disabled at:[<  (null)>]   (null)
[   40.786681]
[   40.786692] CPU: 1 PID: 0 Comm: swapper/1 Not tainted 3.19.0-rc4-00024-g7dca860 #36
[   40.786698] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[   40.786728] [<c0014a00>] (unwind_backtrace) from [<c0011980>] (show_stack+0x10/0x14)
[   40.786747] [<c0011980>] (show_stack) from [<c0449ba0>] (dump_stack+0x70/0xbc)
[   40.786767] [<c0449ba0>] (dump_stack) from [<c00c6124>] (kmem_cache_alloc+0xd8/0x170)
[   40.786785] [<c00c6124>] (kmem_cache_alloc) from [<c005d6f8>] (request_threaded_irq+0x64/0x128)
[   40.786804] [<c005d6f8>] (request_threaded_irq) from [<c0350b8c>] (exynos4_local_timer_setup+0xc0/0x13c)
[   40.786820] [<c0350b8c>] (exynos4_local_timer_setup) from [<c0350ca8>] (exynos4_mct_cpu_notify+0x30/0xa8)
[   40.786838] [<c0350ca8>] (exynos4_mct_cpu_notify) from [<c003b330>] (notifier_call_chain+0x44/0x84)
[   40.786857] [<c003b330>] (notifier_call_chain) from [<c0022fd4>] (__cpu_notify+0x28/0x44)
[   40.786873] [<c0022fd4>] (__cpu_notify) from [<c0013714>] (secondary_start_kernel+0xec/0x150)
[   40.786886] [<c0013714>] (secondary_start_kernel) from [<40008764>] (0x40008764)

Interrupts cannot be requested/freed in the CPU_STARTING/CPU_DYING
notifications which run on the hotplugged cpu with interrupts and
preemption disabled.

To avoid the issue, request the interrupts for all possible cpus in
the boot code. The interrupts are marked NO_AUTOENABLE to avoid a racy
request_irq/disable_irq() sequence. The flag prevents the
request_irq() code from enabling the interrupt immediately.

The interrupt is then enabled in the CPU_STARTING notifier of the
hotplugged cpu and again disabled with disable_irq_nosync() in the
CPU_DYING notifier.

[ tglx: Massaged changelog to match the patch ]

Fixes: 7114cd74 ("clocksource: exynos_mct: use (request/free)_irq calls for local timer registration")
Reported-by: default avatarKrzysztof Kozlowski <k.kozlowski@samsung.com>
Reviewed-by: default avatarKrzysztof Kozlowski <k.kozlowski@samsung.com>
Tested-by: default avatarKrzysztof Kozlowski <k.kozlowski@samsung.com>
Tested-by: default avatarMarcin Jabrzyk <m.jabrzyk@samsung.com>
Signed-off-by: default avatarDamian Eppel <d.eppel@samsung.com>
Cc: m.szyprowski@samsung.com
Cc: kyungmin.park@samsung.com
Cc: daniel.lezcano@linaro.org
Cc: kgene@kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lkml.kernel.org/r/1435324984-7328-1-git-send-email-d.eppel@samsung.comSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 6cc99fc4
...@@ -441,15 +441,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt) ...@@ -441,15 +441,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
if (mct_int_type == MCT_INT_SPI) { if (mct_int_type == MCT_INT_SPI) {
evt->irq = mct_irqs[MCT_L0_IRQ + cpu];
if (request_irq(evt->irq, exynos4_mct_tick_isr, if (evt->irq == -1)
IRQF_TIMER | IRQF_NOBALANCING,
evt->name, mevt)) {
pr_err("exynos-mct: cannot register IRQ %d\n",
evt->irq);
return -EIO; return -EIO;
}
irq_force_affinity(mct_irqs[MCT_L0_IRQ + cpu], cpumask_of(cpu)); irq_force_affinity(evt->irq, cpumask_of(cpu));
enable_irq(evt->irq);
} else { } else {
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
} }
...@@ -462,10 +459,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt) ...@@ -462,10 +459,12 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt)
static void exynos4_local_timer_stop(struct clock_event_device *evt) static void exynos4_local_timer_stop(struct clock_event_device *evt)
{ {
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
if (mct_int_type == MCT_INT_SPI) if (mct_int_type == MCT_INT_SPI) {
free_irq(evt->irq, this_cpu_ptr(&percpu_mct_tick)); if (evt->irq != -1)
else disable_irq_nosync(evt->irq);
} else {
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
}
} }
static int exynos4_mct_cpu_notify(struct notifier_block *self, static int exynos4_mct_cpu_notify(struct notifier_block *self,
...@@ -497,7 +496,7 @@ static struct notifier_block exynos4_mct_cpu_nb = { ...@@ -497,7 +496,7 @@ static struct notifier_block exynos4_mct_cpu_nb = {
static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base) static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{ {
int err; int err, cpu;
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
struct clk *mct_clk, *tick_clk; struct clk *mct_clk, *tick_clk;
...@@ -524,7 +523,25 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem ...@@ -524,7 +523,25 @@ static void __init exynos4_timer_resources(struct device_node *np, void __iomem
WARN(err, "MCT: can't request IRQ %d (%d)\n", WARN(err, "MCT: can't request IRQ %d (%d)\n",
mct_irqs[MCT_L0_IRQ], err); mct_irqs[MCT_L0_IRQ], err);
} else { } else {
irq_set_affinity(mct_irqs[MCT_L0_IRQ], cpumask_of(0)); for_each_possible_cpu(cpu) {
int mct_irq = mct_irqs[MCT_L0_IRQ + cpu];
struct mct_clock_event_device *pcpu_mevt =
per_cpu_ptr(&percpu_mct_tick, cpu);
pcpu_mevt->evt.irq = -1;
irq_set_status_flags(mct_irq, IRQ_NOAUTOEN);
if (request_irq(mct_irq,
exynos4_mct_tick_isr,
IRQF_TIMER | IRQF_NOBALANCING,
pcpu_mevt->name, pcpu_mevt)) {
pr_err("exynos-mct: cannot register IRQ (cpu%d)\n",
cpu);
continue;
}
pcpu_mevt->evt.irq = mct_irq;
}
} }
err = register_cpu_notifier(&exynos4_mct_cpu_nb); err = register_cpu_notifier(&exynos4_mct_cpu_nb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment