Message ID | CABFtUbQ7OWChggMPxYfpUeVCdTTtM74YpkqTF3--RT+wWkPoWQ@mail.gmail.com |
---|---|
State | Superseded |
Headers | show |
On 04/06/2016 at 18:58:59 +0200, Gabriele Mazzotta wrote : > 2016-06-04 16:46 GMT+02:00 Alexandre Belloni > <alexandre.belloni@free-electrons.com>: > > On 01/06/2016 at 17:40:14 +0200, Gabriele Mazzotta wrote : > >> If the system wakes up because of a wake alarm, the internal state > >> of the alarm is not updated. As consequence, the state no longer > >> reflects the actual state of the hardware and setting a new alarm > >> is not possible until the expired alarm is cleared. > >> > > > > I'm not completely sure to understand what is happening but could you > > check whether that one is solved by > > 2b2f5ff00f63847d95adad6289bd8b05f5983dd5 in my tree (rtc-next). > > > > I picked 2b2f5ff00f63847d95adad6289bd8b05f5983dd5 and applied it > on top of 4.7-rc1, but that didn't fix the problem. > > Let me explain the problem by showing you how I reproduce it: > > root@localhost:~# cat /proc/driver/rtc | grep alarm_IRQ > alarm_IRQ : no > root@localhost:~# echo +10 > /sys/class/rtc/rtc0/wakealarm > root@localhost:~# echo mem > /sys/power/state # wait for auto-resume > root@localhost:~# echo +10 > /sys/class/rtc/rtc0/wakealarm > bash: echo: write error: Device or resource busy > root@localhost:~# cat /proc/driver/rtc | grep alarm_IRQ > alarm_IRQ : yes > > To set another alarm, I have to first write 0 to wakealarm. After that > I can set what I want. This doesn't happen if the alarm fires while > the system is awake, it happens only if the system is suspended and > the alarm wakes it. > > I actually forgot to say that maybe this problem is not limited > to rtc-cmos and that maybe a general solution could be used. > > I've just looked better into what is causing this and the problem > seems to be caused by the fact that rtc_timer_do_work() is not > executed if the timer expires while the system is suspended. > I doubt this is a driver independent issues as I don't get that behaviour on many other RTCs. I'll have a look.
On 04/06/2016 19:48, Alexandre Belloni wrote: > On 04/06/2016 at 18:58:59 +0200, Gabriele Mazzotta wrote : >> 2016-06-04 16:46 GMT+02:00 Alexandre Belloni >> <alexandre.belloni@free-electrons.com>: >>> On 01/06/2016 at 17:40:14 +0200, Gabriele Mazzotta wrote : >>>> If the system wakes up because of a wake alarm, the internal state >>>> of the alarm is not updated. As consequence, the state no longer >>>> reflects the actual state of the hardware and setting a new alarm >>>> is not possible until the expired alarm is cleared. >>>> >>> >>> I'm not completely sure to understand what is happening but could you >>> check whether that one is solved by >>> 2b2f5ff00f63847d95adad6289bd8b05f5983dd5 in my tree (rtc-next). >>> >> >> I picked 2b2f5ff00f63847d95adad6289bd8b05f5983dd5 and applied it >> on top of 4.7-rc1, but that didn't fix the problem. >> >> Let me explain the problem by showing you how I reproduce it: >> >> root@localhost:~# cat /proc/driver/rtc | grep alarm_IRQ >> alarm_IRQ : no >> root@localhost:~# echo +10 > /sys/class/rtc/rtc0/wakealarm >> root@localhost:~# echo mem > /sys/power/state # wait for auto-resume >> root@localhost:~# echo +10 > /sys/class/rtc/rtc0/wakealarm >> bash: echo: write error: Device or resource busy >> root@localhost:~# cat /proc/driver/rtc | grep alarm_IRQ >> alarm_IRQ : yes >> >> To set another alarm, I have to first write 0 to wakealarm. After that >> I can set what I want. This doesn't happen if the alarm fires while >> the system is awake, it happens only if the system is suspended and >> the alarm wakes it. >> >> I actually forgot to say that maybe this problem is not limited >> to rtc-cmos and that maybe a general solution could be used. >> >> I've just looked better into what is causing this and the problem >> seems to be caused by the fact that rtc_timer_do_work() is not >> executed if the timer expires while the system is suspended. >> > > I doubt this is a driver independent issues as I don't get that > behaviour on many other RTCs. I'll have a look. Hi, were you able to verify that no other driver is affect? Gabriele
Hi, On 25/08/2016 at 16:54:18 +0200, Gabriele Mazzotta wrote : > Hi, > > were you able to verify that no other driver is affect? > I had a closer look at the issue. I think what is happening is that you don't enter the do/while loop in cmos_resume twice. That is supposed to handle then clear the RTC_AIE bit and that is why the alarm still seems enabled. Can you add some tracing there to understand why? It is probably also useful to know the value of cmos->suspend_ctrl in cmos_suspend. My guess is that is_intr(mask) is false and you break out of the loop at the first pass, meaning that the RTC_AIE bit is never cleared from RTC_CONTROL. That would also mean that your RTC is not setting RTC_AF after waking your PC. Am I right? Regards,
On 31/08/2016 01:28, Alexandre Belloni wrote: > Hi, > > On 25/08/2016 at 16:54:18 +0200, Gabriele Mazzotta wrote : >> Hi, >> >> were you able to verify that no other driver is affect? >> > > I had a closer look at the issue. I think what is happening is that you > don't enter the do/while loop in cmos_resume twice. That is supposed to > handle then clear the RTC_AIE bit and that is why the alarm still seems > enabled. > > Can you add some tracing there to understand why? It is probably also > useful to know the value of cmos->suspend_ctrl in cmos_suspend. cmos->suspend_ctrl is 0x2 when no alarm is set, 0x22 otherwise. The only way to clear RTC_AIE is to set an alarm and wait for it to expire while the system is awake. > My guess is that is_intr(mask) is false and you break out of the loop at > the first pass, meaning that the RTC_AIE bit is never cleared from > RTC_CONTROL. That would also mean that your RTC is not setting RTC_AF > after waking your PC. Am I right? You are right, is_intr(mask) is false and now I see where the problem is. I thought cmos_interrupt() was taking care of everything, but I've just noticed that it's executed only when the system is awake. That's because cmos->wake_on is not NULL, so enable_irq_wake() is not called. However, not even rtc_handler() is called, so I guess the BIOS silently wakes the system when an alarm expires while suspended. This means that we can't update RTC_CONTROL from rtc_handler() and that we have to do it from cmos_resume(). There's a problem with this. We don't know whether the system is waking up because of an alarm or because the user resumed it. In both cases RTC_AIE is set. One way to solve this problem is to manually check from cmos_resume() if any alarm expired while suspended. If we find such an alarm, we don't break early out of the loop and let it clear the flags. Is this reasonable? > Regards, >
On 31/08/2016 at 17:05:58 +0200, Gabriele Mazzotta wrote : > On 31/08/2016 01:28, Alexandre Belloni wrote: > > Hi, > > > > On 25/08/2016 at 16:54:18 +0200, Gabriele Mazzotta wrote : > >> Hi, > >> > >> were you able to verify that no other driver is affect? > >> > > > > I had a closer look at the issue. I think what is happening is that you > > don't enter the do/while loop in cmos_resume twice. That is supposed to > > handle then clear the RTC_AIE bit and that is why the alarm still seems > > enabled. > > > > Can you add some tracing there to understand why? It is probably also > > useful to know the value of cmos->suspend_ctrl in cmos_suspend. > > cmos->suspend_ctrl is 0x2 when no alarm is set, 0x22 otherwise. > The only way to clear RTC_AIE is to set an alarm and wait for it to > expire while the system is awake. > > > My guess is that is_intr(mask) is false and you break out of the loop at > > the first pass, meaning that the RTC_AIE bit is never cleared from > > RTC_CONTROL. That would also mean that your RTC is not setting RTC_AF > > after waking your PC. Am I right? > > You are right, is_intr(mask) is false and now I see where the problem is. > > I thought cmos_interrupt() was taking care of everything, but I've just > noticed that it's executed only when the system is awake. That's because > cmos->wake_on is not NULL, so enable_irq_wake() is not called. > > However, not even rtc_handler() is called, so I guess the BIOS silently > wakes the system when an alarm expires while suspended. This means that > we can't update RTC_CONTROL from rtc_handler() and that we have to do it > from cmos_resume(). > > There's a problem with this. We don't know whether the system is waking up > because of an alarm or because the user resumed it. In both cases RTC_AIE > is set. > > One way to solve this problem is to manually check from cmos_resume() if > any alarm expired while suspended. If we find such an alarm, we don't > break early out of the loop and let it clear the flags. > > Is this reasonable? > Yeah, I think the fix is to clear RTC_AIE in cmos_resume when now > alarm (same check as in your original patch) after the do/while loop. Also, you will need to properly handle the "missed" interrupt and call rtc_update_irq To be clear, something like: if (mask & RTC_AIE) { ... if (now > alarm) { rtc_update_irq(cmos->rtc, 1, mask); tmp &= ~RTC_AIE; CMOS_WRITE(tmp, RTC_CONTROL); } } This limits the impact of the patch as (mask & RTC_AIE) will be false in the case of a properly functioning RTC. Don't forget to comment that it is a quirk, possibly mentioning the maker of the PC and the BIOS. The other quirk is a bit more complicated than your second fix. You actually have to read the alarm in cmos_suspend and then compare it with what you have in cmos_resume. If it has changed and is not expired then you have to set it back.
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 74fd974..80d6a12 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -102,6 +102,9 @@ static int rtc_resume(struct device *dev) struct timespec64 sleep_time; int err; + /* A timer might have expired while suspended */ + schedule_work(&rtc->irqwork); + if (timekeeping_rtc_skipresume()) return 0;