From 53cc2820acbdbcc768675bfaff321f3a8680a317 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 22 Jul 2011 09:12:50 +0000 Subject: rtc: Handle errors correctly in rtc_irq_set_state() In rtc_irq_set_state, the code checks the correctness of the parameters, but then goes on to unconditionally arms/disarms the hrtimer. Thus a random task might arm/disarm rtc timer and surprise the real owner by either generating events or by stopping them. Cc: stable@kernel.org Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- drivers/rtc/interface.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index df68618f6dbb..b6bf57f25cc9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -656,6 +656,8 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; + if (err) + goto out; if (enabled) { ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); @@ -664,6 +666,7 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled hrtimer_cancel(&rtc->pie_timer); } rtc->pie_enabled = enabled; +out: spin_unlock_irqrestore(&rtc->irq_task_lock, flags); return err; -- cgit v1.2.3 From 3c8bb90efb6e3105206e4aaa9127395feeda5492 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 22 Jul 2011 09:12:51 +0000 Subject: rtc: Fix hrtimer deadlock Ben reported a lockup related to rtc. The lockup happens due to: CPU0 CPU1 rtc_irq_set_state() __run_hrtimer() spin_lock_irqsave(&rtc->irq_task_lock) rtc_handle_legacy_irq(); spin_lock(&rtc->irq_task_lock); hrtimer_cancel() while (callback_running); So the running callback never finishes as it's blocked on rtc->irq_task_lock. Use hrtimer_try_to_cancel() instead and drop rtc->irq_task_lock while waiting for the callback. Fix this for both rtc_irq_set_state() and rtc_irq_set_freq(). Cc: stable@kernel.org Reported-by: Ben Greear Signed-off-by: Thomas Gleixner Signed-off-by: John Stultz --- drivers/rtc/interface.c | 56 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index b6bf57f25cc9..a1ba2caa8308 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,24 +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 (err) - goto out; - - 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; -out: spin_unlock_irqrestore(&rtc->irq_task_lock, flags); - return err; } EXPORT_SYMBOL_GPL(rtc_irq_set_state); @@ -690,20 +710,18 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) if (freq <= 0) 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); -- cgit v1.2.3 From 6e7a333eaa522ef73be01caec7a01521490aaf00 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 22 Jul 2011 09:12:51 +0000 Subject: rtc: Limit RTC PIE frequency The RTC pie hrtimer is self rearming. We really need to limit the frequency to something sensible. Thus limit it to the 8192Hz max value from the rtc man documentation Cc: Willy Tarreau Cc: stable@kernel.org Signed-off-by: Thomas Gleixner [jstultz: slightly reworked to use RTC_MAX_FREQ value] Signed-off-by: John Stultz --- drivers/rtc/interface.c | 2 +- include/linux/rtc.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index a1ba2caa8308..44e91e598f8d 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -708,7 +708,7 @@ 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); diff --git a/include/linux/rtc.h b/include/linux/rtc.h index b27ebea25660..93f4d035076b 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 -- cgit v1.2.3 From 2113852b239ed4a93d04135372162252f9342bb6 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 27 May 2011 09:57:25 -0400 Subject: rtc: Add module.h to implicit users in drivers/rtc The module.h was implicitly everywhere, but when we clean that up, the implicit users will compile fail; fix them up in advance. Signed-off-by: Paul Gortmaker --- drivers/rtc/interface.c | 1 + drivers/rtc/rtc-dm355evm.c | 1 + drivers/rtc/rtc-ds1305.c | 1 + drivers/rtc/rtc-ds1511.c | 1 + drivers/rtc/rtc-ds1553.c | 1 + drivers/rtc/rtc-ds1672.c | 1 + drivers/rtc/rtc-ds1742.c | 1 + drivers/rtc/rtc-em3027.c | 1 + drivers/rtc/rtc-isl12022.c | 1 + drivers/rtc/rtc-mv.c | 1 + drivers/rtc/rtc-pcf2123.c | 1 + drivers/rtc/rtc-pcf8563.c | 1 + drivers/rtc/rtc-rs5c348.c | 1 + drivers/rtc/rtc-rs5c372.c | 1 + drivers/rtc/rtc-stk17ta8.c | 1 + drivers/rtc/rtc-tx4939.c | 1 + drivers/rtc/rtc-x1205.c | 1 + 17 files changed, 17 insertions(+) (limited to 'drivers/rtc/interface.c') diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 44e91e598f8d..8e286259a007 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -13,6 +13,7 @@ #include #include +#include #include #include diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c index 58d4e18530da..2322c43af201 100644 --- a/drivers/rtc/rtc-dm355evm.c +++ b/drivers/rtc/rtc-dm355evm.c @@ -14,6 +14,7 @@ #include #include +#include /* diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 57fbcc149ba7..3a33b1fdbe0f 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -17,6 +17,7 @@ #include #include +#include /* diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 568ad30617e7..586c244a05d8 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -23,6 +23,7 @@ #include #include #include +#include #define DRV_VERSION "0.6" diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index fee41b97c9e8..1350029044e6 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DRV_VERSION "0.3" diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 06dfb54f99b6..a319402a5447 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -11,6 +11,7 @@ #include #include +#include #define DRV_VERSION "0.4" diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index d84a448dd754..e3e0f92b60f0 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -21,6 +21,7 @@ #include #include #include +#include #define DRV_VERSION "0.4" diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index d8e1c2578553..8414dea5fb14 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -14,6 +14,7 @@ #include #include #include +#include /* Registers */ #define EM3027_REG_ON_OFF_CTRL 0x00 diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index ddbc797ea6cd..6186833973ee 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -15,6 +15,7 @@ #include #include #include +#include #define DRV_VERSION "0.1" diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index 60627a764514..768e2edb9678 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -14,6 +14,7 @@ #include #include #include +#include #define RTC_TIME_REG_OFFS 0 diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c index 71bab0ef5443..2ee3bbf7e5ea 100644 --- a/drivers/rtc/rtc-pcf2123.c +++ b/drivers/rtc/rtc-pcf2123.c @@ -42,6 +42,7 @@ #include #include #include +#include #define DRV_VERSION "0.6" diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index b42c0c679266..606fdfab34e2 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DRV_VERSION "0.4.3" diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index 368d0e63cf83..971bc8e08da6 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -23,6 +23,7 @@ #include #include #include +#include #define DRV_VERSION "0.2" diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 85c1b848dd72..d29f5432c6e8 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -14,6 +14,7 @@ #include #include #include +#include #define DRV_VERSION "0.6" diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index 3b943673cd3e..ed3e9b599031 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -21,6 +21,7 @@ #include #include #include +#include #define DRV_VERSION "0.1" diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index ec6313d15359..aac0ffed4345 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index b00aad2620d4..8c051d3179db 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -21,6 +21,7 @@ #include #include #include +#include #define DRV_VERSION "1.0.8" -- cgit v1.2.3