Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3348760 authored by Takashi Iwai's avatar Takashi Iwai Committed by Jaroslav Kysela
Browse files

[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: default avatarTakashi Iwai <tiwai@suse.de>
parent 1d4ae4a1
Loading
Loading
Loading
Loading
+38 −27
Original line number Diff line number Diff line
@@ -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