--- /dev/null
+From stable-bounces@linux.kernel.org Fri Nov 11 10:47:39 2005
+Date: Fri, 11 Nov 2005 19:47:28 +0100
+From: Takashi Iwai <tiwai@suse.de>
+To: stable@kernel.org
+Cc:
+Subject: [PATCH] Fix soft lockup with ALSA rtc-timer
+
+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 <tiwai@suse.de>
+Signed-off-by: Chris Wright <chrisw@osdl.org>
+---
+ drivers/char/rtc.c | 65 ++++++++++++++++++++++++++++++-----------------------
+ 1 files changed, 38 insertions(+), 27 deletions(-)
+
+Index: linux-2.6.14.y/drivers/char/rtc.c
+===================================================================
+--- linux-2.6.14.y.orig/drivers/char/rtc.c
++++ linux-2.6.14.y/drivers/char/rtc.c
+@@ -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 cmd
+ 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
+ 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
+