diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-11-07 05:14:57 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-11-07 05:14:57 -0500 |
commit | c3348760aaffd268f7e91b2185999025fdc5607f (patch) | |
tree | dc4fd97943a8547189bb025c71321fb67c5fc3c6 /drivers/char/rtc.c | |
parent | 1d4ae4a119e5ba711f12b05cdf27f794460add4b (diff) |
[PATCH] Fix wrong irq enable via rtc_control()
rtc_control() may be called in the interrupt context in ALSA rtc-timer
driver. The patch fixes the wrong irq enable in rtc.c, and also fixes
the possible race of bit flags.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'drivers/char/rtc.c')
-rw-r--r-- | drivers/char/rtc.c | 65 |
1 files changed, 38 insertions, 27 deletions
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index 63fff7c1244a..a7f099fb7dfe 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c | |||
@@ -149,8 +149,22 @@ static void get_rtc_alm_time (struct rtc_time *alm_tm); | |||
149 | #ifdef RTC_IRQ | 149 | #ifdef RTC_IRQ |
150 | static void rtc_dropped_irq(unsigned long data); | 150 | static void rtc_dropped_irq(unsigned long data); |
151 | 151 | ||
152 | static void set_rtc_irq_bit(unsigned char bit); | 152 | static void set_rtc_irq_bit_locked(unsigned char bit); |
153 | static void mask_rtc_irq_bit(unsigned char bit); | 153 | static void mask_rtc_irq_bit_locked(unsigned char bit); |
154 | |||
155 | static inline void set_rtc_irq_bit(unsigned char bit) | ||
156 | { | ||
157 | spin_lock_irq(&rtc_lock); | ||
158 | set_rtc_irq_bit_locked(bit); | ||
159 | spin_unlock_irq(&rtc_lock); | ||
160 | } | ||
161 | |||
162 | static void mask_rtc_irq_bit(unsigned char bit) | ||
163 | { | ||
164 | spin_lock_irq(&rtc_lock); | ||
165 | mask_rtc_irq_bit_locked(bit); | ||
166 | spin_unlock_irq(&rtc_lock); | ||
167 | } | ||
154 | #endif | 168 | #endif |
155 | 169 | ||
156 | static int rtc_proc_open(struct inode *inode, struct file *file); | 170 | static int rtc_proc_open(struct inode *inode, struct file *file); |
@@ -401,18 +415,19 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | |||
401 | } | 415 | } |
402 | case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ | 416 | case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ |
403 | { | 417 | { |
404 | mask_rtc_irq_bit(RTC_PIE); | 418 | unsigned long flags; /* can be called from isr via rtc_control() */ |
419 | spin_lock_irqsave (&rtc_lock, flags); | ||
420 | mask_rtc_irq_bit_locked(RTC_PIE); | ||
405 | if (rtc_status & RTC_TIMER_ON) { | 421 | if (rtc_status & RTC_TIMER_ON) { |
406 | spin_lock_irq (&rtc_lock); | ||
407 | rtc_status &= ~RTC_TIMER_ON; | 422 | rtc_status &= ~RTC_TIMER_ON; |
408 | del_timer(&rtc_irq_timer); | 423 | del_timer(&rtc_irq_timer); |
409 | spin_unlock_irq (&rtc_lock); | ||
410 | } | 424 | } |
425 | spin_unlock_irqrestore (&rtc_lock, flags); | ||
411 | return 0; | 426 | return 0; |
412 | } | 427 | } |
413 | case RTC_PIE_ON: /* Allow periodic ints */ | 428 | case RTC_PIE_ON: /* Allow periodic ints */ |
414 | { | 429 | { |
415 | 430 | unsigned long flags; /* can be called from isr via rtc_control() */ | |
416 | /* | 431 | /* |
417 | * We don't really want Joe User enabling more | 432 | * We don't really want Joe User enabling more |
418 | * than 64Hz of interrupts on a multi-user machine. | 433 | * than 64Hz of interrupts on a multi-user machine. |
@@ -421,14 +436,14 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | |||
421 | (!capable(CAP_SYS_RESOURCE))) | 436 | (!capable(CAP_SYS_RESOURCE))) |
422 | return -EACCES; | 437 | return -EACCES; |
423 | 438 | ||
439 | spin_lock_irqsave (&rtc_lock, flags); | ||
424 | if (!(rtc_status & RTC_TIMER_ON)) { | 440 | if (!(rtc_status & RTC_TIMER_ON)) { |
425 | spin_lock_irq (&rtc_lock); | ||
426 | rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; | 441 | rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; |
427 | add_timer(&rtc_irq_timer); | 442 | add_timer(&rtc_irq_timer); |
428 | rtc_status |= RTC_TIMER_ON; | 443 | rtc_status |= RTC_TIMER_ON; |
429 | spin_unlock_irq (&rtc_lock); | ||
430 | } | 444 | } |
431 | set_rtc_irq_bit(RTC_PIE); | 445 | set_rtc_irq_bit_locked(RTC_PIE); |
446 | spin_unlock_irqrestore (&rtc_lock, flags); | ||
432 | return 0; | 447 | return 0; |
433 | } | 448 | } |
434 | case RTC_UIE_OFF: /* Mask ints from RTC updates. */ | 449 | case RTC_UIE_OFF: /* Mask ints from RTC updates. */ |
@@ -609,6 +624,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | |||
609 | { | 624 | { |
610 | int tmp = 0; | 625 | int tmp = 0; |
611 | unsigned char val; | 626 | unsigned char val; |
627 | unsigned long flags; /* can be called from isr via rtc_control() */ | ||
612 | 628 | ||
613 | /* | 629 | /* |
614 | * The max we can do is 8192Hz. | 630 | * The max we can do is 8192Hz. |
@@ -631,9 +647,9 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | |||
631 | if (arg != (1<<tmp)) | 647 | if (arg != (1<<tmp)) |
632 | return -EINVAL; | 648 | return -EINVAL; |
633 | 649 | ||
634 | spin_lock_irq(&rtc_lock); | 650 | spin_lock_irqsave(&rtc_lock, flags); |
635 | if (hpet_set_periodic_freq(arg)) { | 651 | if (hpet_set_periodic_freq(arg)) { |
636 | spin_unlock_irq(&rtc_lock); | 652 | spin_unlock_irqrestore(&rtc_lock, flags); |
637 | return 0; | 653 | return 0; |
638 | } | 654 | } |
639 | rtc_freq = arg; | 655 | rtc_freq = arg; |
@@ -641,7 +657,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) | |||
641 | val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0; | 657 | val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0; |
642 | val |= (16 - tmp); | 658 | val |= (16 - tmp); |
643 | CMOS_WRITE(val, RTC_FREQ_SELECT); | 659 | CMOS_WRITE(val, RTC_FREQ_SELECT); |
644 | spin_unlock_irq(&rtc_lock); | 660 | spin_unlock_irqrestore(&rtc_lock, flags); |
645 | return 0; | 661 | return 0; |
646 | } | 662 | } |
647 | #endif | 663 | #endif |
@@ -844,12 +860,15 @@ int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) | |||
844 | #ifndef RTC_IRQ | 860 | #ifndef RTC_IRQ |
845 | return -EIO; | 861 | return -EIO; |
846 | #else | 862 | #else |
847 | spin_lock_irq(&rtc_task_lock); | 863 | unsigned long flags; |
864 | if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) | ||
865 | return -EINVAL; | ||
866 | spin_lock_irqsave(&rtc_task_lock, flags); | ||
848 | if (rtc_callback != task) { | 867 | if (rtc_callback != task) { |
849 | spin_unlock_irq(&rtc_task_lock); | 868 | spin_unlock_irqrestore(&rtc_task_lock, flags); |
850 | return -ENXIO; | 869 | return -ENXIO; |
851 | } | 870 | } |
852 | spin_unlock_irq(&rtc_task_lock); | 871 | spin_unlock_irqrestore(&rtc_task_lock, flags); |
853 | return rtc_do_ioctl(cmd, arg, 1); | 872 | return rtc_do_ioctl(cmd, arg, 1); |
854 | #endif | 873 | #endif |
855 | } | 874 | } |
@@ -1306,40 +1325,32 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm) | |||
1306 | * meddles with the interrupt enable/disable bits. | 1325 | * meddles with the interrupt enable/disable bits. |
1307 | */ | 1326 | */ |
1308 | 1327 | ||
1309 | static void mask_rtc_irq_bit(unsigned char bit) | 1328 | static void mask_rtc_irq_bit_locked(unsigned char bit) |
1310 | { | 1329 | { |
1311 | unsigned char val; | 1330 | unsigned char val; |
1312 | 1331 | ||
1313 | spin_lock_irq(&rtc_lock); | 1332 | if (hpet_mask_rtc_irq_bit(bit)) |
1314 | if (hpet_mask_rtc_irq_bit(bit)) { | ||
1315 | spin_unlock_irq(&rtc_lock); | ||
1316 | return; | 1333 | return; |
1317 | } | ||
1318 | val = CMOS_READ(RTC_CONTROL); | 1334 | val = CMOS_READ(RTC_CONTROL); |
1319 | val &= ~bit; | 1335 | val &= ~bit; |
1320 | CMOS_WRITE(val, RTC_CONTROL); | 1336 | CMOS_WRITE(val, RTC_CONTROL); |
1321 | CMOS_READ(RTC_INTR_FLAGS); | 1337 | CMOS_READ(RTC_INTR_FLAGS); |
1322 | 1338 | ||
1323 | rtc_irq_data = 0; | 1339 | rtc_irq_data = 0; |
1324 | spin_unlock_irq(&rtc_lock); | ||
1325 | } | 1340 | } |
1326 | 1341 | ||
1327 | static void set_rtc_irq_bit(unsigned char bit) | 1342 | static void set_rtc_irq_bit_locked(unsigned char bit) |
1328 | { | 1343 | { |
1329 | unsigned char val; | 1344 | unsigned char val; |
1330 | 1345 | ||
1331 | spin_lock_irq(&rtc_lock); | 1346 | if (hpet_set_rtc_irq_bit(bit)) |
1332 | if (hpet_set_rtc_irq_bit(bit)) { | ||
1333 | spin_unlock_irq(&rtc_lock); | ||
1334 | return; | 1347 | return; |
1335 | } | ||
1336 | val = CMOS_READ(RTC_CONTROL); | 1348 | val = CMOS_READ(RTC_CONTROL); |
1337 | val |= bit; | 1349 | val |= bit; |
1338 | CMOS_WRITE(val, RTC_CONTROL); | 1350 | CMOS_WRITE(val, RTC_CONTROL); |
1339 | CMOS_READ(RTC_INTR_FLAGS); | 1351 | CMOS_READ(RTC_INTR_FLAGS); |
1340 | 1352 | ||
1341 | rtc_irq_data = 0; | 1353 | rtc_irq_data = 0; |
1342 | spin_unlock_irq(&rtc_lock); | ||
1343 | } | 1354 | } |
1344 | #endif | 1355 | #endif |
1345 | 1356 | ||