forked from Minki/linux
[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>
This commit is contained in:
parent
2601a46474
commit
d728b1e69f
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user