svn commit: r327774 - in head: . sys/i386/bios

Bruce Evans brde at optusnet.com.au
Sun Jan 14 09:09:58 UTC 2018


On Sat, 13 Jan 2018, Warner Losh wrote:

> On Sat, Jan 13, 2018 at 6:17 AM, Bruce Evans <brde at optusnet.com.au> wrote:
>
>> On Wed, 10 Jan 2018, Warner Losh wrote:
>>
>> Log:
>>>  inittodr(0) actually sets the time, so there's no need to call
>>>  tc_setclock(). It's redundant. Tweak UPDATING based on code review of
>>>  past releases.
>>
>> No, tc_setclock() is not redundant (except for bugs).  initodr(0) sets
>> ...

>> Untested fixes:
>>
>> X Index: apm.c
>> X ===================================================================
>> X --- apm.c     (revision 327911)
>> X +++ apm.c     (working copy)
>> X @@ -1067,17 +1067,17 @@
>> X       } while (apm_event != PMEV_NOEVENT);
>> X  }
>> X X -static struct timeval suspend_time;
>> X -static struct timeval diff_time;
>> X +static struct timespec diff_time;
>> X +static struct timespec suspend_time;
>> X X  static int
>> X  apm_rtc_suspend(void *arg __unused)
>> X  {
>> X X -   microtime(&diff_time);
>> X -     inittodr(0);
>> X -     microtime(&suspend_time);
>> X -     timevalsub(&diff_time, &suspend_time);
>> X +     nanotime(&diff_time);           /* not a difference yet */
>> X +     inittodr(0);                    /* set tc to raw time seen by RTC
>> */
>> X +     nanotime(&suspend_time);        /* record this raw time */
>> X +     timespecsub(&diff_time, &suspend_time); /* diff between tc and RTC
>> */
>> X       return (0);
>> X  }
>> X X @@ -1084,23 +1084,22 @@
>> X  static int
>> X  apm_rtc_resume(void *arg __unused)
>> X  {
>> X +     struct timespec resume_time, suspend_interval;
>> X       u_int second, minute, hour;
>>
>> This already changes too much, so I didn't fix blind truncation of tv_sec
>> starting in 2038 or other type botches for second/minute/hour.
>
> I suspect this code will no longer be running in 2028, let alone 2038.

20 years is not far away.

(32-bit) u_int for seconds actually works (if time_t is >= 33 bits signed)
until 2106.  If time_t is 32 bits signed and tv_sec is negative, then the
blind assignment to 'seconds' actually gives sign extension bugs.
Excessive fixups and sanity checking probably prevent negative time_t's
going near the RTC although they can still be set in software.

>> X -     struct timeval resume_time, tmp_time;
>> X X -   /* modified for adjkerntz */
>> X -     timer_restore();                /* restore the all timers */
>> X -     inittodr(0);                    /* adjust time to RTC */
>> X -     microtime(&resume_time);
>> X -     getmicrotime(&tmp_time);
>> X -     timevaladd(&tmp_time, &diff_time);
>> X +     timer_restore();
>> X +     inittodr(0);                    /* set tc to new raw RTC time */
>> X +     nanotime(&resume_time);         /* record this raw time */
>> X +     tc_setclock(&diff_time);        /* restore old diff to tc */
>>
>> Fixing tc_setclock() is the easiest part
>
> tc_setclock() sets the absolute time. It's not a phase shift.

Oops.

> How is this then, it fixes the delta vs time setting in your version and
> tweaks variable names to remove banal comments....
>
> static struct timespec suspend_time;            /* Saved RTC time at
> suspend */

This comment is too banal.  The comment is too far to the right, and the
mail client highlights this by mangling the line by splitting it.  The
comment seems to start in column 48.  Columns 32 and 40 are normal.

Part of the banality is echoing 'suspend' and 'time' from the variable name
in the comment.  The variable names aren't very good.  Shorter names like
ts are better in some ways.  'time' in the variable name is no more
descriptive than 't'.  'suspend_time' might be either the timestamp of the
suspension event or the length of the suspension interval.  The clock used
to make the timestamp is delicate.  It isn't just the RTC, but the RTC
converted to a real time by adding a timezone offset and perhaps other
magic.  (Note that in normal use, inittodr() is only used once at boot
time and them the timezone offset is 0.  This is the only use where the
real time set by inittodr() is fully described as the RTC time.

> static struct timespec diff_interval;           /* Delta between systime
> and RTC */

A better comment, but what is the systime? (it is actually the time
for the clock id CLOCK_REALTIME.  I spelled this as tc (timecounter).
inittodr() really does init the full tc and we then specialize to the
real time using nanotime().  nanotime() obviously uses clock id
CLOCK_REALTIME so no commit is needed about what this clock id is,
but it is subtle that CLOCK_REALTIME is the correct (or least worst)
clock id to use.

>
> static int
> apm_rtc_suspend(void *arg __unused)
> {
>
>        nanotime(&diff_interval);

'interval' here is worse than 'time'.  It is not even redundant like it
would be for a difference between timestamps made using the same clock.
Here it is the difference between different clocks measure at a time
which is the same as possible.  This difference is not of an interval.

>        inittodr(0);                    /* systime = RTC */
>        nanotime(&suspend_time);
>        timespecsub(&diff_interval, &suspend_time);
>
>        return (0);
> }
>
> static int
> apm_rtc_resume(void *arg __unused)
> {
>        struct timespec resume_time, new_time, suspend_interval;

new_time is unsorted.

>        u_int second, minute, hour;

Old unsorted declarations (fix later).

>
>        timer_restore();
>        inittodr(0);                    /* Set to updated RTC time */
>        nanotime(&resume_time);
>        new_time = resume_time;

Perhaps name it adj_resume_time.

>        timespecadd(&new_time, &diff_interval);
>        tc_setclock(&new_time);         /* Pre-suspend system time + sleep
> time */

The comment is correct, but we haven't calculated the sleep time yet (and
we later name it suspend_interval), and the code actually calculates
(current_time + adjustment_for_difference_between_clocks_as_calculated_at_
suspend_time)

> ...

This can be further improved using CLOCK_GETTIME() instead of abusing
inittodr().  inittodr() has unwanted side effects including setting the
timecounter.  CLOCK_GETTIME() really does return the RTC time.

Pseudo-code for suspend:

 	nanotime(&now);
 	CLOCK_GETTIME(myrtc, &rtc_now);
 	/* Try omitting utc_offset() adjustment -- see inittodr(). */
 	delta = now;
 	timespecsub(&delta, &rtc_now);

Pseudo-code for resume:

 	CLOCK_GETTIME(myrtc, &rtc_now);
 	now = rtc_now;
 	timespecadd(&now, &delta);
 	tc_setclock(&now);

Omitting utc_offset() gives large deltas near utc_offset() instead of near
0, but makes no difference to the result provided utc_offset() doesn't change
in between.  More locking might be needed to prevent it changing in between.
There is some locking for CLOCK_GETTIME() but this doesn't cover sysctl
variables usied by utc_offset().

CLOCK_GETTIME() is over-engineered and hard to use.  It is hard even
for i386/isa code to find its own RTC.  Note that myrtc should be same
for suspend and resume.  For i386 inittodr(), the RTC used to be just
the isa one, but inittodr() now loops over all available RTCs and there
is no guarantee that it always finds the same one.

Bruce


More information about the svn-src-head mailing list