[GIT pull] timer fixes for 3.1

From: Thomas Gleixner
Date: Wed Aug 17 2011 - 11:47:55 EST


Linus,

Please pull the latest timers-fixes-for-linus git tree from:

git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip.git timers-fixes-for-linus

Thanks,

tglx

------------------>
Thomas Gleixner (3):
rtc: Handle errors correctly in rtc_irq_set_state()
rtc: Fix hrtimer deadlock
rtc: Limit RTC PIE frequency


drivers/rtc/interface.c | 55 ++++++++++++++++++++++++++++++++--------------
include/linux/rtc.h | 3 ++
2 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index df68618..44e91e5 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -636,6 +636,29 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);

+static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
+{
+ /*
+ * We always cancel the timer here first, because otherwise
+ * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
+ * when we manage to start the timer before the callback
+ * returns HRTIMER_RESTART.
+ *
+ * We cannot use hrtimer_cancel() here as a running callback
+ * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
+ * would spin forever.
+ */
+ if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
+ return -1;
+
+ if (enabled) {
+ ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
+
+ hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
+ }
+ return 0;
+}
+
/**
* rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
* @rtc: the rtc device
@@ -651,21 +674,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
int err = 0;
unsigned long flags;

+retry:
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != NULL && task == NULL)
err = -EBUSY;
if (rtc->irq_task != task)
err = -EACCES;
-
- if (enabled) {
- ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
- hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
- } else {
- hrtimer_cancel(&rtc->pie_timer);
+ if (!err) {
+ if (rtc_update_hrtimer(rtc, enabled) < 0) {
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ cpu_relax();
+ goto retry;
+ }
+ rtc->pie_enabled = enabled;
}
- rtc->pie_enabled = enabled;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
-
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);
@@ -685,22 +708,20 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
int err = 0;
unsigned long flags;

- if (freq <= 0)
+ if (freq <= 0 || freq > RTC_MAX_FREQ)
return -EINVAL;
-
+retry:
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != NULL && task == NULL)
err = -EBUSY;
if (rtc->irq_task != task)
err = -EACCES;
- if (err == 0) {
+ if (!err) {
rtc->irq_freq = freq;
- if (rtc->pie_enabled) {
- ktime_t period;
- hrtimer_cancel(&rtc->pie_timer);
- period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
- hrtimer_start(&rtc->pie_timer, period,
- HRTIMER_MODE_REL);
+ if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ cpu_relax();
+ goto retry;
}
}
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index b27ebea..93f4d03 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -97,6 +97,9 @@ struct rtc_pll_info {
#define RTC_AF 0x20 /* Alarm interrupt */
#define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */

+
+#define RTC_MAX_FREQ 8192
+
#ifdef __KERNEL__

#include <linux/types.h>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/