diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-11-07 11:14:57 +0100 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-11-07 11:14:57 +0100 |
commit | c3348760aaffd268f7e91b2185999025fdc5607f (patch) | |
tree | dc4fd97943a8547189bb025c71321fb67c5fc3c6 /drivers/char/rtc.c | |
parent | Merge with rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2... (diff) | |
download | linux-c3348760aaffd268f7e91b2185999025fdc5607f.tar.xz linux-c3348760aaffd268f7e91b2185999025fdc5607f.zip |
[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); #ifdef RTC_IRQ static void rtc_dropped_irq(unsigned long data); -static void set_rtc_irq_bit(unsigned char bit); -static void mask_rtc_irq_bit(unsigned char bit); +static void set_rtc_irq_bit_locked(unsigned char bit); +static void mask_rtc_irq_bit_locked(unsigned char bit); + +static inline void set_rtc_irq_bit(unsigned char bit) +{ + spin_lock_irq(&rtc_lock); + set_rtc_irq_bit_locked(bit); + spin_unlock_irq(&rtc_lock); +} + +static void mask_rtc_irq_bit(unsigned char bit) +{ + spin_lock_irq(&rtc_lock); + mask_rtc_irq_bit_locked(bit); + spin_unlock_irq(&rtc_lock); +} #endif 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) } case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ { - mask_rtc_irq_bit(RTC_PIE); + unsigned long flags; /* can be called from isr via rtc_control() */ + spin_lock_irqsave (&rtc_lock, flags); + mask_rtc_irq_bit_locked(RTC_PIE); if (rtc_status & RTC_TIMER_ON) { - spin_lock_irq (&rtc_lock); rtc_status &= ~RTC_TIMER_ON; del_timer(&rtc_irq_timer); - spin_unlock_irq (&rtc_lock); } + spin_unlock_irqrestore (&rtc_lock, flags); return 0; } case RTC_PIE_ON: /* Allow periodic ints */ { - + unsigned long flags; /* can be called from isr via rtc_control() */ /* * We don't really want Joe User enabling more * 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) (!capable(CAP_SYS_RESOURCE))) return -EACCES; + spin_lock_irqsave (&rtc_lock, flags); if (!(rtc_status & RTC_TIMER_ON)) { - spin_lock_irq (&rtc_lock); rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; add_timer(&rtc_irq_timer); rtc_status |= RTC_TIMER_ON; - spin_unlock_irq (&rtc_lock); } - set_rtc_irq_bit(RTC_PIE); + set_rtc_irq_bit_locked(RTC_PIE); + spin_unlock_irqrestore (&rtc_lock, flags); return 0; } 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) { int tmp = 0; unsigned char val; + unsigned long flags; /* can be called from isr via rtc_control() */ /* * The max we can do is 8192Hz. @@ -631,9 +647,9 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) if (arg != (1<<tmp)) return -EINVAL; - spin_lock_irq(&rtc_lock); + spin_lock_irqsave(&rtc_lock, flags); if (hpet_set_periodic_freq(arg)) { - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); return 0; } rtc_freq = arg; @@ -641,7 +657,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0; val |= (16 - tmp); CMOS_WRITE(val, RTC_FREQ_SELECT); - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); return 0; } #endif @@ -844,12 +860,15 @@ int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) #ifndef RTC_IRQ return -EIO; #else - spin_lock_irq(&rtc_task_lock); + unsigned long flags; + if (cmd != RTC_PIE_ON && cmd != RTC_PIE_OFF && cmd != RTC_IRQP_SET) + return -EINVAL; + spin_lock_irqsave(&rtc_task_lock, flags); if (rtc_callback != task) { - spin_unlock_irq(&rtc_task_lock); + spin_unlock_irqrestore(&rtc_task_lock, flags); return -ENXIO; } - spin_unlock_irq(&rtc_task_lock); + spin_unlock_irqrestore(&rtc_task_lock, flags); return rtc_do_ioctl(cmd, arg, 1); #endif } @@ -1306,40 +1325,32 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm) * meddles with the interrupt enable/disable bits. */ -static void mask_rtc_irq_bit(unsigned char bit) +static void mask_rtc_irq_bit_locked(unsigned char bit) { unsigned char val; - spin_lock_irq(&rtc_lock); - if (hpet_mask_rtc_irq_bit(bit)) { - spin_unlock_irq(&rtc_lock); + if (hpet_mask_rtc_irq_bit(bit)) return; - } val = CMOS_READ(RTC_CONTROL); val &= ~bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; - spin_unlock_irq(&rtc_lock); } -static void set_rtc_irq_bit(unsigned char bit) +static void set_rtc_irq_bit_locked(unsigned char bit) { unsigned char val; - spin_lock_irq(&rtc_lock); - if (hpet_set_rtc_irq_bit(bit)) { - spin_unlock_irq(&rtc_lock); + if (hpet_set_rtc_irq_bit(bit)) return; - } val = CMOS_READ(RTC_CONTROL); val |= bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; - spin_unlock_irq(&rtc_lock); } #endif |