Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 23 Jul 2004 02:25:16 +1000 (EST)
From:      Bruce Evans <bde@zeta.org.au>
To:        John Birrell <jb@cimlogic.com.au>
Cc:        current@freebsd.org
Subject:   Re: nanosleep returning early
Message-ID:  <20040723014517.B2451@epsplex.bde.org>
In-Reply-To: <20040722225952.S1704@epsplex.bde.org>
References:  <20040721081310.GJ22160@freebsd3.cimlogic.com.au> <20040721215940.GK22160@freebsd3.cimlogic.com.au> <20040722225952.S1704@epsplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 23 Jul 2004, Bruce Evans wrote:

> On Thu, 22 Jul 2004, John Birrell wrote:
>
> > On Wed, Jul 21, 2004 at 11:01:20PM +1000, Bruce Evans wrote:
> > > ...
> > > The most obvious bug is that nanosleep() uses the low-accuracy interface
> > > getnanouptime().  I can't see why the the problem is more obvious with
> > > large HZ or why it affects short sleeps.  From kern_time.c 1.170:
> > > ...
> > > % 	getnanouptime(&ts);
> > >
> > > This may lag the actual (up)time by 1/HZ seconds.
>
> [                       (actually tc_tick/HZ seconds)

> > So, does increasing HZ expose the lower accuracy of getnanouptime() and is
> > that what I'm seeing?
>
> I still don't know the reason.  Unfortunately, I deleted your original
> mail so I can't run the test program in it easily.

Now I think I know the reason.  The interval between clock interrupts
is supposed to be 1/HZ seconds = `tick' microseconds, but it cannot
be set nearly that precisely, and the imprecision of inversely
proportional to HZ.  The i8254 counter has a default nominal frequency
of 1193182 Hz.  Suppose that this is perfectly accurate.  Then to
implement clock interrupts at HZ hz, we want to program the i8254's
maximum count to 1193182/HZ in infinite precision, but counts must be
integers so we must round.  The loss of precision is quite large for
HZ = 1000: 1193182 / 1000.0 = 1193.182; rounding this (to nearest)
gives 1193 and an error of 182 in 1193182 = 152 ppm.  Also, the extra
tick added by tvtohz() is only 1000 uS long, so it only has a chance
of about 152/1000 to compensate for the rounding error.  Finally, the
explicit check that the interval has elapsed cannot compensate for
errors larger than tc_tick/HZ because getnanouptime() is fuzzy.

Rounding 1193.182 to nearest happens to round down; thus clock ticks
are shorter than `tick' microseconds, tvtohz()'s value is too small,
and nanosleep() may return too early.  The loop limits the error to
about 1 tick in this case.  The i8254 frequency may be calibrated or
set using sysctl to a more (or less) accurate value.  Then the rounding
may go the other way so that tvtohz()'s value is too large and nanoleep()
may return too late.  The loop cannot limit the error in this case.
The absolute error may be large for long sleeps.  E.g., 152 ppm over
1 day is 13 seconds.

tvtohz()'s value  may also be too large because the i8254 frequency
is not known accurately.  It's nominal value is wrong by 10-100 Hz
on my systems.  I minimize errors from this by calibrating all
timecounters using a common clock.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040723014517.B2451>