[PATCH] rtc class locking bugfixes
authorDavid Brownell <david-b@pacbell.net>
Sat, 25 Nov 2006 19:09:28 +0000 (11:09 -0800)
committerLinus Torvalds <torvalds@woody.osdl.org>
Sat, 25 Nov 2006 21:28:33 +0000 (13:28 -0800)
I got a lockdep warning when running "rtctest" so I though it'd be good
to see what was up.

 - The warning was for rtc->irq_task_lock, gotten from rtc_update_irq()
   by irq handlerss ... but in a handful of other cases, grabbed without
   blocking IRQs.

 - Some callers to rtc_update_irq() were not ensuring IRQs were blocked,
   yet the routine expects that; make sure all callers block IRQs.

It would appear that RTC API tests haven't been part of anyone's kernel
regression test suite recently, at least not with lockdep running.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/rtc/interface.c
drivers/rtc/rtc-at91.c
drivers/rtc/rtc-dev.c
drivers/rtc/rtc-ds1553.c
drivers/rtc/rtc-test.c

index 4783ec68fb3c92127ed631963baf8b8982282b25..6f11f6dfdd9dccc699b5a1a958132d55dec5daa6 100644 (file)
@@ -145,6 +145,13 @@ int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
 }
 EXPORT_SYMBOL_GPL(rtc_set_alarm);
 
+/**
+ * rtc_update_irq - report RTC periodic, alarm, and/or update irqs
+ * @class_dev: the rtc's class device
+ * @num: how many irqs are being reported (usually one)
+ * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
+ * Context: in_interrupt(), irqs blocked
+ */
 void rtc_update_irq(struct class_device *class_dev,
                unsigned long num, unsigned long events)
 {
@@ -201,12 +208,12 @@ int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
        if (task == NULL || task->func == NULL)
                return -EINVAL;
 
-       spin_lock(&rtc->irq_task_lock);
+       spin_lock_irq(&rtc->irq_task_lock);
        if (rtc->irq_task == NULL) {
                rtc->irq_task = task;
                retval = 0;
        }
-       spin_unlock(&rtc->irq_task_lock);
+       spin_unlock_irq(&rtc->irq_task_lock);
 
        return retval;
 }
@@ -216,10 +223,10 @@ void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
 {
        struct rtc_device *rtc = to_rtc_device(class_dev);
 
-       spin_lock(&rtc->irq_task_lock);
+       spin_lock_irq(&rtc->irq_task_lock);
        if (rtc->irq_task == task)
                rtc->irq_task = NULL;
-       spin_unlock(&rtc->irq_task_lock);
+       spin_unlock_irq(&rtc->irq_task_lock);
 }
 EXPORT_SYMBOL_GPL(rtc_irq_unregister);
 
index bd61e99540a330450478a5c45832adda143fdc00..5c8addcaf1fbd8a0a49078155a81be129b040800 100644 (file)
@@ -292,7 +292,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
                                        AT91_RTC_CALEV);
 
        ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
-                               IRQF_SHARED, "at91_rtc", pdev);
+                               IRQF_DISABLED | IRQF_SHARED,
+                               "at91_rtc", pdev);
        if (ret) {
                printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
                                AT91_ID_SYS);
index 3109865e8d7324d8e697ff7f04c5e1d51e20cef3..814b9e1873f55ef8061b4abfc66ab5776da38dc1 100644 (file)
@@ -61,7 +61,9 @@ static void rtc_uie_task(void *data)
        int err;
 
        err = rtc_read_time(&rtc->class_dev, &tm);
-       spin_lock_irq(&rtc->irq_lock);
+
+       local_irq_disable();
+       spin_lock(&rtc->irq_lock);
        if (rtc->stop_uie_polling || err) {
                rtc->uie_task_active = 0;
        } else if (rtc->oldsecs != tm.tm_sec) {
@@ -74,11 +76,11 @@ static void rtc_uie_task(void *data)
        } else if (schedule_work(&rtc->uie_task) == 0) {
                rtc->uie_task_active = 0;
        }
-       spin_unlock_irq(&rtc->irq_lock);
+       spin_unlock(&rtc->irq_lock);
        if (num)
                rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
+       local_irq_enable();
 }
-
 static void rtc_uie_timer(unsigned long data)
 {
        struct rtc_device *rtc = (struct rtc_device *)data;
@@ -238,10 +240,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
 
        /* avoid conflicting IRQ users */
        if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
-               spin_lock(&rtc->irq_task_lock);
+               spin_lock_irq(&rtc->irq_task_lock);
                if (rtc->irq_task)
                        err = -EBUSY;
-               spin_unlock(&rtc->irq_task_lock);
+               spin_unlock_irq(&rtc->irq_task_lock);
 
                if (err < 0)
                        return err;
index 78552e6e76aa252d8065ec93cc95abde426e92dd..001eb1123a657707a581b0915ec919e435a95dd7 100644 (file)
@@ -340,7 +340,8 @@ static int __init ds1553_rtc_probe(struct platform_device *pdev)
 
        if (pdata->irq >= 0) {
                writeb(0, ioaddr + RTC_INTERRUPTS);
-               if (request_irq(pdata->irq, ds1553_rtc_interrupt, IRQF_SHARED,
+               if (request_irq(pdata->irq, ds1553_rtc_interrupt,
+                               IRQF_DISABLED | IRQF_SHARED,
                                pdev->name, pdev) < 0) {
                        dev_warn(&pdev->dev, "interrupt not available.\n");
                        pdata->irq = -1;
index bc4bd24508a2d4beb23458c3b6ee3a86b7e41e26..6ef9c62d5032afa97122331c4754513f9b44b0dc 100644 (file)
@@ -99,6 +99,7 @@ static ssize_t test_irq_store(struct device *dev,
        struct rtc_device *rtc = platform_get_drvdata(plat_dev);
 
        retval = count;
+       local_irq_disable();
        if (strncmp(buf, "tick", 4) == 0)
                rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
        else if (strncmp(buf, "alarm", 5) == 0)
@@ -107,6 +108,7 @@ static ssize_t test_irq_store(struct device *dev,
                rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
        else
                retval = -EINVAL;
+       local_irq_enable();
 
        return retval;
 }