clocksource: Fix up a registration/IRQ race in the sh drivers.
authorPaul Mundt <lethal@linux-sh.org>
Thu, 25 Feb 2010 07:37:46 +0000 (16:37 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Thu, 25 Feb 2010 07:37:46 +0000 (16:37 +0900)
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: Rafael Ignacio Zurita <rafaelignacio.zurita@gmail.com>
Cc: stable@kernel.org
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
drivers/clocksource/sh_cmt.c
drivers/clocksource/sh_mtu2.c
drivers/clocksource/sh_tmu.c

index 6b3e0c2f33e2b193838fbecc0dda72aa9f0db5ef..6fe4f7701188c87423fb3fb85987f76cd95a5f41 100644 (file)
@@ -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.dev_id = p;
        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 */
        p->clk = clk_get(&p->pdev->dev, cfg->clk);
        if (IS_ERR(p->clk)) {
                pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
                ret = PTR_ERR(p->clk);
-               goto err2;
+               goto err1;
        }
 
        if (resource_size(res) == 6) {
@@ -627,14 +622,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
                p->clear_bits = ~0xc000;
        }
 
-       return sh_cmt_register(p, cfg->name,
-                              cfg->clockevent_rating,
-                              cfg->clocksource_rating);
- err2:
-       remove_irq(irq, &p->irqaction);
- err1:
+       ret = sh_cmt_register(p, cfg->name,
+                             cfg->clockevent_rating,
+                             cfg->clocksource_rating);
+       if (ret) {
+               pr_err("sh_cmt: registration failed\n");
+               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);
- err0:
+err0:
        return ret;
 }
 
index 973e714d605147d7e2110eaae48a25b780d32bc7..4c8a759e60cdb56c14cf57002a9672f81100822e 100644 (file)
@@ -221,15 +221,15 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
        ced->cpumask = cpumask_of(0);
        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);
        if (ret) {
                pr_err("sh_mtu2: failed to request irq %d\n",
                       p->irqaction.irq);
                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,
index 93c2322feab79e8e119d0b51949ac0b1bc42135d..961f5b5ef6a3584c558c45dbe20a05074fe325b1 100644 (file)
@@ -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_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);
        if (ret) {
                pr_err("sh_tmu: failed to request irq %d\n",
                       p->irqaction.irq);
                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,