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

Commit d728b1e6 authored by David Brownell's avatar David Brownell Committed by Linus Torvalds
Browse files

[PATCH] rtc class locking bugfixes



I got a lockdep warning when running "rtctest" so I though it'd be good
to see what was up.

 - The warning was for rtc->irq_task_lock, gotten from rtc_update_irq()
   by irq handlerss ... but in a handful of other cases, grabbed without
   blocking IRQs.

 - Some callers to rtc_update_irq() were not ensuring IRQs were blocked,
   yet the routine expects that; make sure all callers block IRQs.

It would appear that RTC API tests haven't been part of anyone's kernel
regression test suite recently, at least not with lockdep running.

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Acked-by: default avatarAlessandro Zummo <a.zummo@towertech.it>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2601a464
Loading
Loading
Loading
Loading
+11 −4
Original line number Original line Diff line number Diff line
@@ -145,6 +145,13 @@ int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
}
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
EXPORT_SYMBOL_GPL(rtc_set_alarm);


/**
 * rtc_update_irq - report RTC periodic, alarm, and/or update irqs
 * @class_dev: the rtc's class device
 * @num: how many irqs are being reported (usually one)
 * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
 * Context: in_interrupt(), irqs blocked
 */
void rtc_update_irq(struct class_device *class_dev,
void rtc_update_irq(struct class_device *class_dev,
		unsigned long num, unsigned long events)
		unsigned long num, unsigned long events)
{
{
@@ -201,12 +208,12 @@ int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
	if (task == NULL || task->func == NULL)
	if (task == NULL || task->func == NULL)
		return -EINVAL;
		return -EINVAL;


	spin_lock(&rtc->irq_task_lock);
	spin_lock_irq(&rtc->irq_task_lock);
	if (rtc->irq_task == NULL) {
	if (rtc->irq_task == NULL) {
		rtc->irq_task = task;
		rtc->irq_task = task;
		retval = 0;
		retval = 0;
	}
	}
	spin_unlock(&rtc->irq_task_lock);
	spin_unlock_irq(&rtc->irq_task_lock);


	return retval;
	return retval;
}
}
@@ -216,10 +223,10 @@ void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
{
{
	struct rtc_device *rtc = to_rtc_device(class_dev);
	struct rtc_device *rtc = to_rtc_device(class_dev);


	spin_lock(&rtc->irq_task_lock);
	spin_lock_irq(&rtc->irq_task_lock);
	if (rtc->irq_task == task)
	if (rtc->irq_task == task)
		rtc->irq_task = NULL;
		rtc->irq_task = NULL;
	spin_unlock(&rtc->irq_task_lock);
	spin_unlock_irq(&rtc->irq_task_lock);
}
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);
EXPORT_SYMBOL_GPL(rtc_irq_unregister);


+2 −1
Original line number Original line Diff line number Diff line
@@ -292,7 +292,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
					AT91_RTC_CALEV);
					AT91_RTC_CALEV);


	ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
	ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
				IRQF_SHARED, "at91_rtc", pdev);
				IRQF_DISABLED | IRQF_SHARED,
				"at91_rtc", pdev);
	if (ret) {
	if (ret) {
		printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
		printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
				AT91_ID_SYS);
				AT91_ID_SYS);
+7 −5
Original line number Original line Diff line number Diff line
@@ -61,7 +61,9 @@ static void rtc_uie_task(void *data)
	int err;
	int err;


	err = rtc_read_time(&rtc->class_dev, &tm);
	err = rtc_read_time(&rtc->class_dev, &tm);
	spin_lock_irq(&rtc->irq_lock);

	local_irq_disable();
	spin_lock(&rtc->irq_lock);
	if (rtc->stop_uie_polling || err) {
	if (rtc->stop_uie_polling || err) {
		rtc->uie_task_active = 0;
		rtc->uie_task_active = 0;
	} else if (rtc->oldsecs != tm.tm_sec) {
	} else if (rtc->oldsecs != tm.tm_sec) {
@@ -74,11 +76,11 @@ static void rtc_uie_task(void *data)
	} else if (schedule_work(&rtc->uie_task) == 0) {
	} else if (schedule_work(&rtc->uie_task) == 0) {
		rtc->uie_task_active = 0;
		rtc->uie_task_active = 0;
	}
	}
	spin_unlock_irq(&rtc->irq_lock);
	spin_unlock(&rtc->irq_lock);
	if (num)
	if (num)
		rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
		rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
	local_irq_enable();
}
}

static void rtc_uie_timer(unsigned long data)
static void rtc_uie_timer(unsigned long data)
{
{
	struct rtc_device *rtc = (struct rtc_device *)data;
	struct rtc_device *rtc = (struct rtc_device *)data;
@@ -238,10 +240,10 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,


	/* avoid conflicting IRQ users */
	/* avoid conflicting IRQ users */
	if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
	if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
		spin_lock(&rtc->irq_task_lock);
		spin_lock_irq(&rtc->irq_task_lock);
		if (rtc->irq_task)
		if (rtc->irq_task)
			err = -EBUSY;
			err = -EBUSY;
		spin_unlock(&rtc->irq_task_lock);
		spin_unlock_irq(&rtc->irq_task_lock);


		if (err < 0)
		if (err < 0)
			return err;
			return err;
+2 −1
Original line number Original line Diff line number Diff line
@@ -340,7 +340,8 @@ static int __init ds1553_rtc_probe(struct platform_device *pdev)


	if (pdata->irq >= 0) {
	if (pdata->irq >= 0) {
		writeb(0, ioaddr + RTC_INTERRUPTS);
		writeb(0, ioaddr + RTC_INTERRUPTS);
		if (request_irq(pdata->irq, ds1553_rtc_interrupt, IRQF_SHARED,
		if (request_irq(pdata->irq, ds1553_rtc_interrupt,
				IRQF_DISABLED | IRQF_SHARED,
				pdev->name, pdev) < 0) {
				pdev->name, pdev) < 0) {
			dev_warn(&pdev->dev, "interrupt not available.\n");
			dev_warn(&pdev->dev, "interrupt not available.\n");
			pdata->irq = -1;
			pdata->irq = -1;
+2 −0
Original line number Original line Diff line number Diff line
@@ -99,6 +99,7 @@ static ssize_t test_irq_store(struct device *dev,
	struct rtc_device *rtc = platform_get_drvdata(plat_dev);
	struct rtc_device *rtc = platform_get_drvdata(plat_dev);


	retval = count;
	retval = count;
	local_irq_disable();
	if (strncmp(buf, "tick", 4) == 0)
	if (strncmp(buf, "tick", 4) == 0)
		rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
		rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
	else if (strncmp(buf, "alarm", 5) == 0)
	else if (strncmp(buf, "alarm", 5) == 0)
@@ -107,6 +108,7 @@ static ssize_t test_irq_store(struct device *dev,
		rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
		rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
	else
	else
		retval = -EINVAL;
		retval = -EINVAL;
	local_irq_enable();


	return retval;
	return retval;
}
}