[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>
This commit is contained in:
Takashi Iwai 2005-11-07 11:14:57 +01:00 committed by Jaroslav Kysela
parent 1d4ae4a119
commit c3348760aa

View file

@ -149,8 +149,22 @@ static void get_rtc_alm_time (struct rtc_time *alm_tm);
#ifdef RTC_IRQ #ifdef RTC_IRQ
static void rtc_dropped_irq(unsigned long data); static void rtc_dropped_irq(unsigned long data);
static void set_rtc_irq_bit(unsigned char bit); static void set_rtc_irq_bit_locked(unsigned char bit);
static void mask_rtc_irq_bit(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 #endif
static int rtc_proc_open(struct inode *inode, struct file *file); 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 */ 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) { if (rtc_status & RTC_TIMER_ON) {
spin_lock_irq (&rtc_lock);
rtc_status &= ~RTC_TIMER_ON; rtc_status &= ~RTC_TIMER_ON;
del_timer(&rtc_irq_timer); del_timer(&rtc_irq_timer);
spin_unlock_irq (&rtc_lock);
} }
spin_unlock_irqrestore (&rtc_lock, flags);
return 0; return 0;
} }
case RTC_PIE_ON: /* Allow periodic ints */ 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 * We don't really want Joe User enabling more
* than 64Hz of interrupts on a multi-user machine. * 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))) (!capable(CAP_SYS_RESOURCE)))
return -EACCES; return -EACCES;
spin_lock_irqsave (&rtc_lock, flags);
if (!(rtc_status & RTC_TIMER_ON)) { if (!(rtc_status & RTC_TIMER_ON)) {
spin_lock_irq (&rtc_lock);
rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100; rtc_irq_timer.expires = jiffies + HZ/rtc_freq + 2*HZ/100;
add_timer(&rtc_irq_timer); add_timer(&rtc_irq_timer);
rtc_status |= RTC_TIMER_ON; 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; return 0;
} }
case RTC_UIE_OFF: /* Mask ints from RTC updates. */ 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; int tmp = 0;
unsigned char val; unsigned char val;
unsigned long flags; /* can be called from isr via rtc_control() */
/* /*
* The max we can do is 8192Hz. * 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)) if (arg != (1<<tmp))
return -EINVAL; return -EINVAL;
spin_lock_irq(&rtc_lock); spin_lock_irqsave(&rtc_lock, flags);
if (hpet_set_periodic_freq(arg)) { if (hpet_set_periodic_freq(arg)) {
spin_unlock_irq(&rtc_lock); spin_unlock_irqrestore(&rtc_lock, flags);
return 0; return 0;
} }
rtc_freq = arg; 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 = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
val |= (16 - tmp); val |= (16 - tmp);
CMOS_WRITE(val, RTC_FREQ_SELECT); CMOS_WRITE(val, RTC_FREQ_SELECT);
spin_unlock_irq(&rtc_lock); spin_unlock_irqrestore(&rtc_lock, flags);
return 0; return 0;
} }
#endif #endif
@ -844,12 +860,15 @@ int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
#ifndef RTC_IRQ #ifndef RTC_IRQ
return -EIO; return -EIO;
#else #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) { if (rtc_callback != task) {
spin_unlock_irq(&rtc_task_lock); spin_unlock_irqrestore(&rtc_task_lock, flags);
return -ENXIO; return -ENXIO;
} }
spin_unlock_irq(&rtc_task_lock); spin_unlock_irqrestore(&rtc_task_lock, flags);
return rtc_do_ioctl(cmd, arg, 1); return rtc_do_ioctl(cmd, arg, 1);
#endif #endif
} }
@ -1306,40 +1325,32 @@ static void get_rtc_alm_time(struct rtc_time *alm_tm)
* meddles with the interrupt enable/disable bits. * 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; unsigned char val;
spin_lock_irq(&rtc_lock); if (hpet_mask_rtc_irq_bit(bit))
if (hpet_mask_rtc_irq_bit(bit)) {
spin_unlock_irq(&rtc_lock);
return; return;
}
val = CMOS_READ(RTC_CONTROL); val = CMOS_READ(RTC_CONTROL);
val &= ~bit; val &= ~bit;
CMOS_WRITE(val, RTC_CONTROL); CMOS_WRITE(val, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS); CMOS_READ(RTC_INTR_FLAGS);
rtc_irq_data = 0; 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; unsigned char val;
spin_lock_irq(&rtc_lock); if (hpet_set_rtc_irq_bit(bit))
if (hpet_set_rtc_irq_bit(bit)) {
spin_unlock_irq(&rtc_lock);
return; return;
}
val = CMOS_READ(RTC_CONTROL); val = CMOS_READ(RTC_CONTROL);
val |= bit; val |= bit;
CMOS_WRITE(val, RTC_CONTROL); CMOS_WRITE(val, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS); CMOS_READ(RTC_INTR_FLAGS);
rtc_irq_data = 0; rtc_irq_data = 0;
spin_unlock_irq(&rtc_lock);
} }
#endif #endif