On Thu, Jun 07, 2018 at 11:27:34AM +0000, sunil+b...@nimmagadda.net wrote: > >Synopsis: apmd(8) poll timer off by 10x > >Category: system > >Environment: > System : OpenBSD 6.3 > Details : OpenBSD 6.3-current (GENERIC.MP) #54: Wed May 30 23:03:50 > MDT 2018 > > dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP > > Architecture: OpenBSD.amd64 > Machine : amd64 > >Description: > With apmd_flags="-Az10", expected apmd(8) to suspend when > battery is at 10%, however, it didn't check in time and > laptop ran of out power. > >How-To-Repeat: > Disconnect A/C adapter and run with -z percent greater > than current estimated battery life reported by apm(8); > poll every minute, for example... > # rcctl stop apmd > # apmd -A -z90 -t60 > should suspend in a minute, however, it suspends after 10 > minutes. > >Fix: > The following diff... > > 1. Provides a dedicated timer that fires every 10 seconds > instead of relying on EVFILT_READ freqency. > > 2. Increments a counter and checks against timeout value, > if it exceeds, invokes auto-action. > > 3. Wraps a few long lines that exceed 80 cols upon code > shuffle.
I don't think we need to introduce an additional timer, or do so much code shufflin' here, to fix your issue. The problem seems to be that apmtimeout is incremented once per iteration but must meet or exceed ts.tv_sec to trigger a status check, so the period for battery status checks is at least n^2 seconds. I'm pretty sure this was unintentional, though it makes it unlikely that apmd will catch a low battery percentage and suspend the machine before the battery is totally exhausted. Especially since, by default, n = 600. Here's a minimal diff that checks if we timed out on return from kevent. There's additional cleanup that this change implies, but I've left it out for the moment. Of note is that an event for either of the descriptors resets the timeout, regardless of how long it's been since we checked the battery status: this is effectively the current behavior. If people want, we can add logic to decrement the maximum timeout accordingly on each iteration and reset it when kevent truly times out. This sounds closer to what the manpage advertises for the -t option. Caveat: I'm unfamiliar with apmd(8) and I don't have time just this second scour the change log to figure out why the behavior is what it is now. Someone more familiar with the code will need to corroborate what I've said and the attached diff. That said, feel free to try this diff in the meantime. Does this work for you? Anyone more familiar with apmd(8) wanna chime in here? -- Scott Cheloha Index: usr.sbin/apmd/apmd.c =================================================================== RCS file: /cvs/src/usr.sbin/apmd/apmd.c,v retrieving revision 1.81 diff -u -p -r1.81 apmd.c --- usr.sbin/apmd/apmd.c 15 Oct 2017 15:14:49 -0000 1.81 +++ usr.sbin/apmd/apmd.c 7 Jun 2018 20:26:11 -0000 @@ -488,7 +488,7 @@ main(int argc, char *argv[]) if ((rv = kevent(kq, NULL, 0, ev, 1, &sts)) < 0) break; - if (apmtimeout >= ts.tv_sec) { + if (rv == 0) { apmtimeout = 0; /* wakeup for timeout: take status */