Fix soft lockup with ALSA rtc-timer
PATCH 3/23 Fix soft lockup with ALSA rtc-timer
-stable review patch. If anyone has any objections, please let us know.
------------------
Fixed the soft lockup of ALSA rtc-timer due to the wrong irq handling in rtc_control(). The call of rtc_control() can be atomic.
Signed-off-by Takashi Iwai <[email protected]>
Signed-off-by Chris Wright <[email protected]>
Signed-off-by Greg Kroah-Hartman <[email protected]>
--- drivers/char/rtc.c | 65 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 27 deletions(-)
drivers/char/rtc.c +38 -27
@@ -149,8 +149,22 @@ static void get_rtc_alm_time (struct rtc#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 }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 (!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 {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 cmdif (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 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, unsign#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_* 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
--