Commit da64c2a8 authored by Paul Mundt's avatar Paul Mundt

clocksource: Fix up a registration/IRQ race in the sh drivers.

All of the SH clocksource drivers follow the scheme that the IRQ is setup
prior to registering the clockevent. The interrupt handler in the
clockevent cases looks to the event handler function pointer being filled
in by the registration code, permitting us to get in to situations where
asserted IRQs step in to the handler before registration has had a chance
to complete and hitting a NULL pointer deref.

In practice this is not an issue for most platforms, but some of them
with fairly special loaders (or that are chain-loading from another
kernel) may enter in to this situation. This fixes up the oops reported
by Rafael on hp6xx.
Reported-and-tested-by: default avatarRafael Ignacio Zurita <rafaelignacio.zurita@gmail.com>
Cc: stable@kernel.org
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 29463c28
...@@ -603,18 +603,13 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) ...@@ -603,18 +603,13 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->irqaction.handler = sh_cmt_interrupt; p->irqaction.handler = sh_cmt_interrupt;
p->irqaction.dev_id = p; p->irqaction.dev_id = p;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
ret = setup_irq(irq, &p->irqaction);
if (ret) {
pr_err("sh_cmt: failed to request irq %d\n", irq);
goto err1;
}
/* get hold of clock */ /* get hold of clock */
p->clk = clk_get(&p->pdev->dev, cfg->clk); p->clk = clk_get(&p->pdev->dev, cfg->clk);
if (IS_ERR(p->clk)) { if (IS_ERR(p->clk)) {
pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk); pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
ret = PTR_ERR(p->clk); ret = PTR_ERR(p->clk);
goto err2; goto err1;
} }
if (resource_size(res) == 6) { if (resource_size(res) == 6) {
...@@ -627,14 +622,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) ...@@ -627,14 +622,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->clear_bits = ~0xc000; p->clear_bits = ~0xc000;
} }
return sh_cmt_register(p, cfg->name, ret = sh_cmt_register(p, cfg->name,
cfg->clockevent_rating, cfg->clockevent_rating,
cfg->clocksource_rating); cfg->clocksource_rating);
err2: if (ret) {
remove_irq(irq, &p->irqaction); pr_err("sh_cmt: registration failed\n");
err1: goto err1;
}
ret = setup_irq(irq, &p->irqaction);
if (ret) {
pr_err("sh_cmt: failed to request irq %d\n", irq);
goto err1;
}
return 0;
err1:
iounmap(p->mapbase); iounmap(p->mapbase);
err0: err0:
return ret; return ret;
} }
......
...@@ -221,15 +221,15 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p, ...@@ -221,15 +221,15 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
ced->cpumask = cpumask_of(0); ced->cpumask = cpumask_of(0);
ced->set_mode = sh_mtu2_clock_event_mode; ced->set_mode = sh_mtu2_clock_event_mode;
pr_info("sh_mtu2: %s used for clock events\n", ced->name);
clockevents_register_device(ced);
ret = setup_irq(p->irqaction.irq, &p->irqaction); ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) { if (ret) {
pr_err("sh_mtu2: failed to request irq %d\n", pr_err("sh_mtu2: failed to request irq %d\n",
p->irqaction.irq); p->irqaction.irq);
return; return;
} }
pr_info("sh_mtu2: %s used for clock events\n", ced->name);
clockevents_register_device(ced);
} }
static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name, static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
......
...@@ -323,15 +323,15 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p, ...@@ -323,15 +323,15 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
ced->set_next_event = sh_tmu_clock_event_next; ced->set_next_event = sh_tmu_clock_event_next;
ced->set_mode = sh_tmu_clock_event_mode; ced->set_mode = sh_tmu_clock_event_mode;
pr_info("sh_tmu: %s used for clock events\n", ced->name);
clockevents_register_device(ced);
ret = setup_irq(p->irqaction.irq, &p->irqaction); ret = setup_irq(p->irqaction.irq, &p->irqaction);
if (ret) { if (ret) {
pr_err("sh_tmu: failed to request irq %d\n", pr_err("sh_tmu: failed to request irq %d\n",
p->irqaction.irq); p->irqaction.irq);
return; return;
} }
pr_info("sh_tmu: %s used for clock events\n", ced->name);
clockevents_register_device(ced);
} }
static int sh_tmu_register(struct sh_tmu_priv *p, char *name, static int sh_tmu_register(struct sh_tmu_priv *p, char *name,
......
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