[PATCH] rtc class locking bugfixes
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>
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 4783ec6..6f11f6d 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -145,6 +145,13 @@
}
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 @@
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 @@
{
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);
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91.c
index bd61e99..5c8addc 100644
--- a/drivers/rtc/rtc-at91.c
+++ b/drivers/rtc/rtc-at91.c
@@ -292,7 +292,8 @@
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);
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 3109865..814b9e1 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -61,7 +61,9 @@
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 @@
} 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 @@
/* 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;
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 78552e6..001eb11 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -340,7 +340,8 @@
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;
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index bc4bd245..6ef9c62 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -99,6 +99,7 @@
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 @@
rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
else
retval = -EINVAL;
+ local_irq_enable();
return retval;
}