Commit b830ac1d9a2262093bb0f3f6a2fd2a1c8278daf5
Committed by
Linus Torvalds
1 parent
431e2bcc37
Exists in
master
and in
4 other branches
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>
Showing 1 changed file with 37 additions and 19 deletions Side-by-side Diff
drivers/rtc/interface.c
... | ... | @@ -636,6 +636,29 @@ |
636 | 636 | } |
637 | 637 | EXPORT_SYMBOL_GPL(rtc_irq_unregister); |
638 | 638 | |
639 | +static 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 | 663 | * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs |
641 | 664 | * @rtc: the rtc device |
642 | 665 | |
643 | 666 | |
644 | 667 | |
... | ... | @@ -651,24 +674,21 @@ |
651 | 674 | int err = 0; |
652 | 675 | unsigned long flags; |
653 | 676 | |
677 | +retry: | |
654 | 678 | spin_lock_irqsave(&rtc->irq_task_lock, flags); |
655 | 679 | if (rtc->irq_task != NULL && task == NULL) |
656 | 680 | err = -EBUSY; |
657 | 681 | if (rtc->irq_task != task) |
658 | 682 | err = -EACCES; |
659 | - if (err) | |
660 | - goto out; | |
661 | - | |
662 | - if (enabled) { | |
663 | - ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); | |
664 | - hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); | |
665 | - } else { | |
666 | - hrtimer_cancel(&rtc->pie_timer); | |
683 | + if (!err) { | |
684 | + if (rtc_update_hrtimer(rtc, enabled) < 0) { | |
685 | + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | |
686 | + cpu_relax(); | |
687 | + goto retry; | |
688 | + } | |
689 | + rtc->pie_enabled = enabled; | |
667 | 690 | } |
668 | - rtc->pie_enabled = enabled; | |
669 | -out: | |
670 | 691 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); |
671 | - | |
672 | 692 | return err; |
673 | 693 | } |
674 | 694 | EXPORT_SYMBOL_GPL(rtc_irq_set_state); |
675 | 695 | |
676 | 696 | |
... | ... | @@ -690,20 +710,18 @@ |
690 | 710 | |
691 | 711 | if (freq <= 0 || freq > 5000) |
692 | 712 | return -EINVAL; |
693 | - | |
713 | +retry: | |
694 | 714 | spin_lock_irqsave(&rtc->irq_task_lock, flags); |
695 | 715 | if (rtc->irq_task != NULL && task == NULL) |
696 | 716 | err = -EBUSY; |
697 | 717 | if (rtc->irq_task != task) |
698 | 718 | err = -EACCES; |
699 | - if (err == 0) { | |
719 | + if (!err) { | |
700 | 720 | rtc->irq_freq = freq; |
701 | - if (rtc->pie_enabled) { | |
702 | - ktime_t period; | |
703 | - hrtimer_cancel(&rtc->pie_timer); | |
704 | - period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); | |
705 | - hrtimer_start(&rtc->pie_timer, period, | |
706 | - HRTIMER_MODE_REL); | |
721 | + if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) { | |
722 | + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); | |
723 | + cpu_relax(); | |
724 | + goto retry; | |
707 | 725 | } |
708 | 726 | } |
709 | 727 | spin_unlock_irqrestore(&rtc->irq_task_lock, flags); |