Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

David Brownell and committed by
Linus Torvalds
d728b1e6 2601a464

+24 -11
+11 -4
drivers/rtc/interface.c
··· 145 145 } 146 146 EXPORT_SYMBOL_GPL(rtc_set_alarm); 147 147 148 + /** 149 + * rtc_update_irq - report RTC periodic, alarm, and/or update irqs 150 + * @class_dev: the rtc's class device 151 + * @num: how many irqs are being reported (usually one) 152 + * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF 153 + * Context: in_interrupt(), irqs blocked 154 + */ 148 155 void rtc_update_irq(struct class_device *class_dev, 149 156 unsigned long num, unsigned long events) 150 157 { ··· 208 201 if (task == NULL || task->func == NULL) 209 202 return -EINVAL; 210 203 211 - spin_lock(&rtc->irq_task_lock); 204 + spin_lock_irq(&rtc->irq_task_lock); 212 205 if (rtc->irq_task == NULL) { 213 206 rtc->irq_task = task; 214 207 retval = 0; 215 208 } 216 - spin_unlock(&rtc->irq_task_lock); 209 + spin_unlock_irq(&rtc->irq_task_lock); 217 210 218 211 return retval; 219 212 } ··· 223 216 { 224 217 struct rtc_device *rtc = to_rtc_device(class_dev); 225 218 226 - spin_lock(&rtc->irq_task_lock); 219 + spin_lock_irq(&rtc->irq_task_lock); 227 220 if (rtc->irq_task == task) 228 221 rtc->irq_task = NULL; 229 - spin_unlock(&rtc->irq_task_lock); 222 + spin_unlock_irq(&rtc->irq_task_lock); 230 223 } 231 224 EXPORT_SYMBOL_GPL(rtc_irq_unregister); 232 225
+2 -1
drivers/rtc/rtc-at91.c
··· 292 292 AT91_RTC_CALEV); 293 293 294 294 ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, 295 - IRQF_SHARED, "at91_rtc", pdev); 295 + IRQF_DISABLED | IRQF_SHARED, 296 + "at91_rtc", pdev); 296 297 if (ret) { 297 298 printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", 298 299 AT91_ID_SYS);
+7 -5
drivers/rtc/rtc-dev.c
··· 61 61 int err; 62 62 63 63 err = rtc_read_time(&rtc->class_dev, &tm); 64 - spin_lock_irq(&rtc->irq_lock); 64 + 65 + local_irq_disable(); 66 + spin_lock(&rtc->irq_lock); 65 67 if (rtc->stop_uie_polling || err) { 66 68 rtc->uie_task_active = 0; 67 69 } else if (rtc->oldsecs != tm.tm_sec) { ··· 76 74 } else if (schedule_work(&rtc->uie_task) == 0) { 77 75 rtc->uie_task_active = 0; 78 76 } 79 - spin_unlock_irq(&rtc->irq_lock); 77 + spin_unlock(&rtc->irq_lock); 80 78 if (num) 81 79 rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF); 80 + local_irq_enable(); 82 81 } 83 - 84 82 static void rtc_uie_timer(unsigned long data) 85 83 { 86 84 struct rtc_device *rtc = (struct rtc_device *)data; ··· 240 238 241 239 /* avoid conflicting IRQ users */ 242 240 if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) { 243 - spin_lock(&rtc->irq_task_lock); 241 + spin_lock_irq(&rtc->irq_task_lock); 244 242 if (rtc->irq_task) 245 243 err = -EBUSY; 246 - spin_unlock(&rtc->irq_task_lock); 244 + spin_unlock_irq(&rtc->irq_task_lock); 247 245 248 246 if (err < 0) 249 247 return err;
+2 -1
drivers/rtc/rtc-ds1553.c
··· 340 340 341 341 if (pdata->irq >= 0) { 342 342 writeb(0, ioaddr + RTC_INTERRUPTS); 343 - if (request_irq(pdata->irq, ds1553_rtc_interrupt, IRQF_SHARED, 343 + if (request_irq(pdata->irq, ds1553_rtc_interrupt, 344 + IRQF_DISABLED | IRQF_SHARED, 344 345 pdev->name, pdev) < 0) { 345 346 dev_warn(&pdev->dev, "interrupt not available.\n"); 346 347 pdata->irq = -1;
+2
drivers/rtc/rtc-test.c
··· 99 99 struct rtc_device *rtc = platform_get_drvdata(plat_dev); 100 100 101 101 retval = count; 102 + local_irq_disable(); 102 103 if (strncmp(buf, "tick", 4) == 0) 103 104 rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF); 104 105 else if (strncmp(buf, "alarm", 5) == 0) ··· 108 107 rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF); 109 108 else 110 109 retval = -EINVAL; 110 + local_irq_enable(); 111 111 112 112 return retval; 113 113 }