aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2011-07-26 19:08:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-26 19:49:42 -0400
commitb830ac1d9a2262093bb0f3f6a2fd2a1c8278daf5 (patch)
tree580f61a7a8a621792e8ac48d2ec4a9721165eecd /drivers/rtc
parent431e2bcc371016824f419baa745f82388258f3ee (diff)
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(). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reported-by: Ben Greear <greearb@candelatech.com> Cc: John Stultz <john.stultz@linaro.org> Cc: Ingo Molnar <mingo@elte.hu> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/interface.c56
1 files changed, 37 insertions, 19 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 601b053bc0e5..3195dbd3ec34 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)
636} 636}
637EXPORT_SYMBOL_GPL(rtc_irq_unregister); 637EXPORT_SYMBOL_GPL(rtc_irq_unregister);
638 638
639static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
640{
641 /*
642 * We unconditionally cancel the timer here, because otherwise
643 * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
644 * when we manage to start the timer before the callback
645 * returns HRTIMER_RESTART.
646 *
647 * We cannot use hrtimer_cancel() here as a running callback
648 * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
649 * would spin forever.
650 */
651 if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
652 return -1;
653
654 if (enabled) {
655 ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
656
657 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
658 }
659 return 0;
660}
661
639/** 662/**
640 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs 663 * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
641 * @rtc: the rtc device 664 * @rtc: the rtc device
@@ -651,24 +674,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled
651 int err = 0; 674 int err = 0;
652 unsigned long flags; 675 unsigned long flags;
653 676
677retry:
654 spin_lock_irqsave(&rtc->irq_task_lock, flags); 678 spin_lock_irqsave(&rtc->irq_task_lock, flags);
655 if (rtc->irq_task != NULL && task == NULL) 679 if (rtc->irq_task != NULL && task == NULL)
656 err = -EBUSY; 680 err = -EBUSY;
657 if (rtc->irq_task != task) 681 if (rtc->irq_task != task)
658 err = -EACCES; 682 err = -EACCES;
659 if (err) 683 if (!err) {
660 goto out; 684 if (rtc_update_hrtimer(rtc, enabled) < 0) {
661 685 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
662 if (enabled) { 686 cpu_relax();
663 ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 687 goto retry;
664 hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); 688 }
665 } else { 689 rtc->pie_enabled = enabled;
666 hrtimer_cancel(&rtc->pie_timer);
667 } 690 }
668 rtc->pie_enabled = enabled;
669out:
670 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 691 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
671
672 return err; 692 return err;
673} 693}
674EXPORT_SYMBOL_GPL(rtc_irq_set_state); 694EXPORT_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)
690 710
691 if (freq <= 0 || freq > 5000) 711 if (freq <= 0 || freq > 5000)
692 return -EINVAL; 712 return -EINVAL;
693 713retry:
694 spin_lock_irqsave(&rtc->irq_task_lock, flags); 714 spin_lock_irqsave(&rtc->irq_task_lock, flags);
695 if (rtc->irq_task != NULL && task == NULL) 715 if (rtc->irq_task != NULL && task == NULL)
696 err = -EBUSY; 716 err = -EBUSY;
697 if (rtc->irq_task != task) 717 if (rtc->irq_task != task)
698 err = -EACCES; 718 err = -EACCES;
699 if (err == 0) { 719 if (!err) {
700 rtc->irq_freq = freq; 720 rtc->irq_freq = freq;
701 if (rtc->pie_enabled) { 721 if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
702 ktime_t period; 722 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
703 hrtimer_cancel(&rtc->pie_timer); 723 cpu_relax();
704 period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 724 goto retry;
705 hrtimer_start(&rtc->pie_timer, period,
706 HRTIMER_MODE_REL);
707 } 725 }
708 } 726 }
709 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 727 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);