| From 1a91436e21abb6e926921e1fb15bd28fe09035cb Mon Sep 17 00:00:00 2001 |
| From: Alexandre Belloni <alexandre.belloni@bootlin.com> |
| Date: Mon, 21 Oct 2019 01:13:20 +0200 |
| Subject: [PATCH] rtc: disable uie before setting time and enable after |
| |
| commit 7e7c005b4b1f1f169bcc4b2c3a40085ecc663df2 upstream. |
| |
| When setting the time in the future with the uie timer enabled, |
| rtc_timer_do_work will loop for a while because the expiration of the uie |
| timer was way before the current RTC time and a new timer will be enqueued |
| until the current rtc time is reached. |
| |
| If the uie timer is enabled, disable it before setting the time and enable |
| it after expiring current timers (which may actually be an alarm). |
| |
| This is the safest thing to do to ensure the uie timer is still |
| synchronized with the RTC, especially in the UIE emulation case. |
| |
| Reported-by: syzbot+08116743f8ad6f9a6de7@syzkaller.appspotmail.com |
| Fixes: 6610e0893b8b ("RTC: Rework RTC code to use timerqueue for events") |
| Link: https://lore.kernel.org/r/20191020231320.8191-1-alexandre.belloni@bootlin.com |
| Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c |
| index 4124f4dd376b..f50003dd3b6f 100644 |
| --- a/drivers/rtc/interface.c |
| +++ b/drivers/rtc/interface.c |
| @@ -125,7 +125,7 @@ EXPORT_SYMBOL_GPL(rtc_read_time); |
| |
| int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) |
| { |
| - int err; |
| + int err, uie; |
| |
| err = rtc_valid_tm(tm); |
| if (err != 0) |
| @@ -137,6 +137,17 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) |
| |
| rtc_subtract_offset(rtc, tm); |
| |
| +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL |
| + uie = rtc->uie_rtctimer.enabled || rtc->uie_irq_active; |
| +#else |
| + uie = rtc->uie_rtctimer.enabled; |
| +#endif |
| + if (uie) { |
| + err = rtc_update_irq_enable(rtc, 0); |
| + if (err) |
| + return err; |
| + } |
| + |
| err = mutex_lock_interruptible(&rtc->ops_lock); |
| if (err) |
| return err; |
| @@ -153,6 +164,12 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) |
| /* A timer might have just expired */ |
| schedule_work(&rtc->irqwork); |
| |
| + if (uie) { |
| + err = rtc_update_irq_enable(rtc, 1); |
| + if (err) |
| + return err; |
| + } |
| + |
| trace_rtc_set_time(rtc_tm_to_time64(tm), err); |
| return err; |
| } |
| -- |
| 2.7.4 |
| |