Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <greearb@candelatech.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: John Stultz <john.stultz@linaro.org>

authored by

Thomas Gleixner and committed by
John Stultz
3c8bb90e 53cc2820

+37 -19
+37 -19
drivers/rtc/interface.c
··· 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 always cancel the timer here first, 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 ··· 674 651 int err = 0; 675 652 unsigned long flags; 676 653 654 + retry: 677 655 spin_lock_irqsave(&rtc->irq_task_lock, flags); 678 656 if (rtc->irq_task != NULL && task == NULL) 679 657 err = -EBUSY; 680 658 if (rtc->irq_task != task) 681 659 err = -EACCES; 682 - if (err) 683 - goto out; 684 - 685 - if (enabled) { 686 - ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 687 - hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); 688 - } else { 689 - hrtimer_cancel(&rtc->pie_timer); 660 + if (!err) { 661 + if (rtc_update_hrtimer(rtc, enabled) < 0) { 662 + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 663 + cpu_relax(); 664 + goto retry; 665 + } 666 + rtc->pie_enabled = enabled; 690 667 } 691 - rtc->pie_enabled = enabled; 692 - out: 693 668 spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 694 - 695 669 return err; 696 670 } 697 671 EXPORT_SYMBOL_GPL(rtc_irq_set_state); ··· 710 690 711 691 if (freq <= 0) 712 692 return -EINVAL; 713 - 693 + retry: 714 694 spin_lock_irqsave(&rtc->irq_task_lock, flags); 715 695 if (rtc->irq_task != NULL && task == NULL) 716 696 err = -EBUSY; 717 697 if (rtc->irq_task != task) 718 698 err = -EACCES; 719 - if (err == 0) { 699 + if (!err) { 720 700 rtc->irq_freq = freq; 721 - if (rtc->pie_enabled) { 722 - ktime_t period; 723 - hrtimer_cancel(&rtc->pie_timer); 724 - period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); 725 - hrtimer_start(&rtc->pie_timer, period, 726 - HRTIMER_MODE_REL); 701 + if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) { 702 + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); 703 + cpu_relax(); 704 + goto retry; 727 705 } 728 706 } 729 707 spin_unlock_irqrestore(&rtc->irq_task_lock, flags);