Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FreeBSD ntp_gettime and STA_NANO inconsistant?

4 views
Skip to first unread message

Jeff W. Boote

unread,
Aug 21, 2002, 3:08:18 PM8/21/02
to
I am attempting to use the ntp_gettime system call on FreeBSD 4.5.

It appears to me that it always returns the fractional part of the time struct
in (ns) - and doesn't depend upon the STA_NANO bit in timex.status. (Comments in
the timex.h header file, and the code in ntp/util/ntptime.c lead me to believe
that the fractional part should be scaled based on STA_NANO.)

I have looked at the kern_ntptime.c and verified that it doesn't look at the
current state to determine how to scale the return value.

Which is the correct behavior?

Personally, I would much rather see the time structure always returned in (ns)
like FreeBSD does if the kernel supports STA_NANO, that way I don't have some
other process changing the state of that flag under me.*

But if it always returns (ns) if STA_NANO is supported, then the question
becomes: How do I reasonably determine if the kernel supports STA_NANO? Try
setting it, and then set it back? Then I'm changing it under other processes,
and my application needs to run with more permissions.

thanks,
jeff

* I'm working on a one-way network delay measurement application, and I really
don't want to have to keep polling ntp_adjtime to find out how to interpret the
return value of ntp_gettime. Additionally, that wouldn't be an atomic operation
so I couldn't be sure of it.

David L. Mills

unread,
Aug 21, 2002, 2:30:04 PM8/21/02
to
Jeff,

Please see the timex.h header file. It answers all your questions, but
be advised that file is system dependent.

Dave

Jeff W. Boote

unread,
Aug 22, 2002, 12:55:37 PM8/22/02
to
Dave,

"David L. Mills" wrote:
>
> Jeff,
>
> Please see the timex.h header file. It answers all your questions, but
> be advised that file is system dependent.
>
> Dave
>

I referenced the timex.h header file in my original question. I will describe
the problem again actually quoting the relevant code fragments. I demonstrate
the inconsistency ONLY using the ntptime command line utility near the end of
the post. (Source code is shown using FreeBSD src path names.)

According to timex.h the time member is scaled based upon STA_NANO. Notice the
"Note:" in /usr/src/sys/sys/timex.h:
>
>/*
> * NTP user interface (ntp_gettime()) - used to read kernel clock values
> *
> * Note: The time member is in microseconds if STA_NANO is zero and
> * nanoseconds if not.
> */
>struct ntptimeval {
> struct timespec time; /* current time (ns) (ro) */
> long maxerror; /* maximum error (us) (ro) */
> long esterror; /* estimated error (us) (ro) */
> long tai; /* TAI offset */
> int time_state; /* time status */
>};

Now - there are two ways I can interpret that "Note", one that it is referring
to the macro STA_NANO being defined, or that if I called ntp_adjtime the
timex.status status code would have the STA_NANO bit set.

Lets take the case where I interpret it based upon the status bit, since that is
more consistent with the description and comments for the ntp_adjtime system
call. If ntp_gettime should be scaling the based upon STA_NANO, then there
should be some logic in the system call to scale that return value.

get_ntptime system call within /usr/src/sys/kern/kern_ntptime.c:
> static int
> ntp_sysctl(SYSCTL_HANDLER_ARGS)
> {
> struct ntptimeval ntv; /* temporary structure */
> struct timespec atv; /* nanosecond time */
>
> nanotime(&atv);
> ntv.time.tv_sec = atv.tv_sec;
> ntv.time.tv_nsec = atv.tv_nsec;
...
The time portion is not touched after this. Now to show that nanotime does not
scale it...

kern_clock.c:
> void
> nanotime(struct timespec *ts)
> {
> unsigned count;
> u_int64_t delta;
> struct timecounter *tc;
>
> tc = timecounter;
> ts->tv_sec = tc->tc_offset_sec;
> count = tco_delta(tc);
> delta = tc->tc_offset_nano;
> delta += ((u_int64_t)count * tc->tc_scale_nano_f);
> delta >>= 32;
> delta += ((u_int64_t)count * tc->tc_scale_nano_i);
> delta += boottime.tv_usec * 1000;
> ts->tv_sec += boottime.tv_sec;
> while (delta >= 1000000000) {
> delta -= 1000000000;
> ts->tv_sec++;
> }
> ts->tv_nsec = delta;
> }

As you can see, the system call is going to be returning values over 1000000 for
ts->tv_nsec even if STA_NANO is currently 0, and they sure look like (ns) values
to me.

Now, the other possible interpretion for the "Note" comment in timex.h was to
check if the STA_NANO macro was defined or not. If I assume ntp_gettime() is
going to return (ns) if STA_NANO is defined, I do in fact get consistent values
from ntp_gettime() on FreeBSD. However, if this is the way the system call is
supposed to behave, then there is a bug in the ntptime command line utility:

ntptime.c[274-]:
> /*
> * Fetch timekeeping data and display.
> */
> status = ntp_gettime(&ntv);
> if (status < 0)
> perror("ntp_gettime() call fails");
> else {
> printf("ntp_gettime() returns code %d (%s)\n",
> status, timex_state(status));
> time_frac = ntv.time.tv_frac_sec;
> #ifdef STA_NANO
> if (flash & STA_NANO) {
> ntv.time.tv_frac_sec /= 1000;
> ts_mask = 0xfffffffc; /* 1/2^30 */
> ts_roundbit = 0x00000002;
> fdigits = 9;
> }
> #endif
> tv.tv_sec = ntv.time.tv_sec;
> tv.tv_usec = ntv.time.tv_frac_sec;
> TVTOTS(&tv, &ts);
> ts.l_ui += JAN_1970;
> ts.l_uf += ts_roundbit;
> ts.l_uf &= ts_mask;

You will notice that tv_frac_sec is only scaled from ns to ms if the STA_NANO
bit is set. Now - this bug is easy to see by simply looking at the printout of
the time from ntptime if STA_NANO mode is not set:

>$ ntptime
>ntp_gettime() returns code 0 (OK)
> time c10f8b7e.af1b2000 Thu, Aug 22 2002 10:10:38.684, (.931819496),
> maximum error 846162 us, estimated error 141801 us, TAI offset 0
>ntp_adjtime() returns code 0 (OK)
> modes 0x0 (),
> offset 500000.000 us, frequency -289.081 ppm, interval 1 s,
> maximum error 846162 us, estimated error 141801 us,
> status 0x1 (PLL),
> time constant 4, precision 0.000 us, tolerance 496 ppm,

Notice that the time reported from "prettydate" of the timestamp shows (.684) as
the fractional part. However the actual fractional digits reported from
ntp_gettime were (.931819496).

If I set STA_NANO, and then call ntptime again:

>$ ntptime
>ntp_gettime() returns code 0 (OK)
> time c10f8bc4.723896f8 Thu, Aug 22 2002 10:11:48.446, (.446176110),
> maximum error 880662 us, estimated error 141801 us, TAI offset 0
>ntp_adjtime() returns code 0 (OK)
> modes 0x0 (),
> offset 500000.000 us, frequency -289.081 ppm, interval 1 s,
> maximum error 880662 us, estimated error 141801 us,
> status 0x2001 (PLL,NANO),
> time constant 4, precision 0.001 us, tolerance 496 ppm,

Now the fractional portion reported from prettydate and ntp_gettime agree.

It is obvious that there is an inconsistency here.

My question was which way is it supposed to be? That way I can report the bug as
either a bug for the FreeBSD system call, or for the ntptime function. (And that
way I can also try and figure out a strategy for dealing with this
inconsistency.)

jeff

David L. Mills

unread,
Aug 22, 2002, 5:23:36 PM8/22/02
to Jeff W. Boote
Jeff,

The ntptimeval structure definition has been changed from the template I
provided. See the nanokernal distribution for the original file. For
those older machines that do not have timespec, a timeval structure is
used instead. Ordinarily, STA_NANO is defined in those timex.h files for
systems that do support timespec and not defined for systems that
support only timeval. The motive to remove the conditional the note
refers to is unclear.

In the master copy ktime.c in the nanokernel distribution the time is
scaled as a function of the STA_NANO bit as long as that bit is defined.
The conditional that does that was removed by somebody else.

My nanotime() routine was modified by P.H. Kamp to use his favorite
timecounter scheme. His scheme may be no better or worse than mine, but
I see my intricately crafted multiprocessor support code has been
removed. If you find the routine returns greater than one second, you
should message the FreeBSD kernelmongers. However, note that the
nanotime() routine as suggested by its very name is expected to return
time strictly in nanoseconds.

Dave

Jeff W. Boote

unread,
Aug 22, 2002, 11:42:23 PM8/22/02
to

"David L. Mills" wrote:
> If you find the routine returns greater than one second, you
> should message the FreeBSD kernelmongers.

Thanks, I'll do that.

Just out of curiosity, what would you think of a solution that forced the
STA_NANO flag always on for FreeBSD? It is currently only used for the
ntp_adjtime() call in FreeBSD, and I would argue that it is not strictly
necessary since it does not actually change anything within the FreeBSD clock.
Or am I missing something?

I thought I might have a better chance getting this fixed, if I actually
provided a patch.

jeff

David L. Mills

unread,
Aug 23, 2002, 7:31:11 PM8/23/02
to Jeff W. Boote
Jeff,

I don't see any compelling reason for altering the distribution that
left here and intended for generic cross platform application. If
STA_NANO is present in timex.h, as intended in FreeBSD, Linux, Solaris
and Alpha, but not in SUnOS, the STA_NANO mode bit does as you would
expect. If the bit is off, you get a timeval; with it on you get a
timespec. This is intended so that the behavior when both structures are
available is consistent across compliant platforms. This feature has
been explicitly clipped out of FreeBSD and I have no clue as to the
motive for that.

Dave

John Hay

unread,
Aug 24, 2002, 7:38:00 AM8/24/02
to
David L. Mills <mi...@udel.edu> wrote:

: I don't see any compelling reason for altering the distribution that


: left here and intended for generic cross platform application. If
: STA_NANO is present in timex.h, as intended in FreeBSD, Linux, Solaris
: and Alpha, but not in SUnOS, the STA_NANO mode bit does as you would
: expect. If the bit is off, you get a timeval; with it on you get a
: timespec. This is intended so that the behavior when both structures are
: available is consistent across compliant platforms. This feature has
: been explicitly clipped out of FreeBSD and I have no clue as to the
: motive for that.

Maybe we did interpret things in your nanokernel distribution wrong?
In the latest one I have (dated 2001-04-02), in kern.h there is this
piece:

/*
* If NTP_NANO is defined, the package is configured for a nanosecond
* clock, where the kernel clock (timespec time) runs in seconds and
* nanoseconds. If undefined, the package is configured for a
* microsecond clock, where the kernel clock (timeval time) runs in
* seconds and microseconds. In either case, the actual system time
* delivered to the user is in seconds and nanoseconds in either a
* timeval or a timespec structure.
*/
#define NTP_NANO /* kernel nanosecond clock */

And then in in ktime.c in the function ntp_gettime() it looks like
this:

nano_time(&atv);
#ifdef NTP_NANO


ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_nsec = atv.tv_nsec;

#else
if (!(time_status & STA_NANO))
atv.tv_nsec /= 1000;
ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_usec = atv.tv_nsec;
#endif /* NTP_NANO */
ntv.maxerror = time_maxerror;
ntv.esterror = time_esterror;
ntv.tai = time_tai;

So I guess we thought we will go for the NTP_NANO defined option and then
the #else can never happen? If we misunderstood things and you can clarify
things for me, I'll go and change it.

John

David L. Mills

unread,
Aug 24, 2002, 5:06:48 PM8/24/02
to John Hay
John,

The problem is to make sure everything works when either timeval
(microseconds) is defined, as in SunOS and probably other older systems,
or when both timeval and timespec (nanoseconds) are defined, as in
Solaris, Tru64, FreeBSD and Linux. When NTP_NANO is defined,
ntp_getime() returns a timespec; otherwise, it returns a timeval by
default. But, some of those older systems have been modified to support
timespec when STA_NANO is set in the call. To make this transparent to
the daemon, ntpd sets STA_NANO on the first call to ntp_adjtime(). If
STA_NANO is set in the return, it assumes timespec; otherwise it assumes
timeval.

My fervent hope was that everybody would keep the files as given and
avoid "optimizations" which complicate the maintenance, just as in this
case. Some of the code may be stubs in this case, but it does minimize
confusion.

A complicating factor is the only customer of record for ntp_atjtime()
is ntpd/xntpd. The ntpd converts floating double to whatever the kernel
needs, but xntpd expects microseconds.

Dave

John Hay

unread,
Aug 25, 2002, 5:11:19 AM8/25/02
to
Dave,

Ok, something that I still don't understand. When should NTP_NANO be
defined for a kernel then? Never? Only under certain conditions? Or
was it only to test the nanokernel distribution? The comments where
NTP_NANO is defined, made it sound as if that is the code path that
should be followed for systems that use nanoseconds inside the kernel,
which FreeBSD does. Another question, should NTP_NANO be visible from
user-level or is it just something to select the code path inside the
kernel?

I think we mostly stay pretty close to the intent of your nanokernel
distribution, but it isn't totally possible to use it as-is. For
instance time inside the FreeBSD kernel is done different than the
traditional unix kernels and we have to make your code work with that.

John

David L. Mills <mi...@udel.edu> wrote:

: John,

David L. Mills

unread,
Aug 25, 2002, 12:14:31 PM8/25/02
to
John,

It's really simple. First, be sure you are looking at the nanokernel
code, not the FreeBSD port. If the operating system supports timespec
(nanoseconds), define NTP_NANO; otherwise, do not define that treasure.
This is true both for the kernel build and NTP build. The same timex.h
should be used for both. The only cases where this makes a difference is
with old SunOS, Alpha, Ultrix and HP systems which had my microsecond
kernels but did not have timespec.

Dave

Jeff W. Boote

unread,
Aug 25, 2002, 7:12:56 PM8/25/02
to
Dave,

So, as far as I understand from the nanokernel distribution, the STA_NANO flag
is intended to allow systems that have timespec and your microsecond kernel to
return nanoseconds from the ntp_gettime() system call. If this is the case, why
would it be bad for a system that had timespec and a nanosecond kernel to always
report the STA_NANO flag set? It ensures portability of user code by letting
them use that flag to determine that the timestamp is in nanoseconds.

I do see a problem with the STA_NANO flag as it is currently implemented. Since
the status is not returned from the ntp_gettime call, another process can call
ntp_adjtime and change that flag anytime after your process sets STA_NANO and
before you actually fetch timestamps with ntp_gettime. You don't truly know how
to interpret the timestamp. Now, I realize this probably isn't a big deal in
practice because there isn't usually a lot of processes messing with the clock
interface, but it is a large part of why I would prefer to see the flag
hardcoded on for nanosecond kernels.

John - you asked if NTP_NANO needed to be visible from user-level. As someone
who is attempting to use it that way, I would answer that it is vitally
important that user processes be able to tell what scale the timestamp from
ntp_gettime is in. I don't know that NTP_NANO needs to be the way - but the
ntp_gettime() system call is not very useful if you can't determine reliably
what scale it is going to respond in. (And portably across different OS's.)

jeff

David L. Mills

unread,
Aug 25, 2002, 11:11:30 PM8/25/02
to
Jeff,

The devil is in the details. Older NTPv3 requires timeval
(microseconds), regardless of operating system. Actually, a couple of
timespecs sneaked in the NTPv3 code, but only to calibrate the precision
in those systems where precision is calibratible. NTPv3 didn't know
about STA_NANO, of course, so never set the bit and never got a STA_NANO
response, even if the kernel could do the wicked nanodeed. Thus price
backwards compatibility.

I did consider the problem you raise, but the problem goes further than
that. There's quite a lot of stuff ntp_adjtime() can do that affects
ntp-gettime(), like fiddle frequency and phase. I thought to include a
return code that tells ntp-gettime() that something changed since the
last call and maybe it should be checked out. After all, there is
nothing preventing some other process from changing things without
ntp_gettime() syspecting. A clean and proper solution would require a
lock and a trusted process that remembers things

. Life is just too ding complicated

Dave

David L. Mills

unread,
Aug 25, 2002, 11:10:58 PM8/25/02
to Jeff W. Boote
Jeff,

The devil is in the details. Older NTPv3 requires timeval
(microseconds), regardless of operating system. Actually, a couple of
timespecs sneaked in the NTPv3 code, but only to calibrate the precision
in those systems where precision is calibratible. NTPv3 didn't know
about STA_NANO, of course, so never set the bit and never got a STA_NANO
response, even if the kernel could do the wicked nanodeed. Thus price
backwards compatibility.

I did consider the problem you raise, but the problem goes further than
that. There's quite a lot of stuff ntp_adjtime() can do that affects
ntp-gettime(), like fiddle frequency and phase. I thought to include a
return code that tells ntp-gettime() that something changed since the
last call and maybe it should be checked out. After all, there is
nothing preventing some other process from changing things without
ntp_gettime() syspecting. A clean and proper solution would require a

lock and a trusted process that remembers things. Life is just too ding
complicated

Dave

Jeff W. Boote

unread,
Aug 27, 2002, 11:36:11 PM8/27/02
to
Dave,

I was looking at this problem closer due to some interaction with John Hay. I'm
sure you are sick of this problem by now, and I apologize for continuing this
thread. However, it appears to me that things are actually inconsistent between
your nanokernel distribution and the user code in ntp-4.1.1a. The inconsistency
happens if NTP_NANO is defined for the nanokernel. In code in question is:

nanokernel/ktime.c (ntp_gettime function):

nano_time(&atv);
#ifdef NTP_NANO
ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_nsec = atv.tv_nsec;
#else
if (!(time_status & STA_NANO))
atv.tv_nsec /= 1000;
ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_usec = atv.tv_nsec;
#endif /* NTP_NANO */

As you can see, if NTP_NANO is defined, the time is returned in nanoseconds. It
does not matter what state the STA_NANO flag is in.

Now, if you take a look at ntp-4.1.1a/util/ntptime.c:


status = ntp_gettime(&ntv);
if (status < 0)
perror("ntp_gettime() call fails");
else {
printf("ntp_gettime() returns code %d (%s)\n",
status, timex_state(status));
time_frac = ntv.time.tv_frac_sec;
#ifdef STA_NANO
if (flash & STA_NANO) {
ntv.time.tv_frac_sec /= 1000;
ts_mask = 0xfffffffc; /* 1/2^30 */
ts_roundbit = 0x00000002;
fdigits = 9;
}
#endif
tv.tv_sec = ntv.time.tv_sec;
tv.tv_usec = ntv.time.tv_frac_sec;
TVTOTS(&tv, &ts);

It only treats the value as a nanosecond value if STA_NANO is defined. There is
no reason it will be defined unless it is set from a prior call to ntp_adjtime.

Now, I'm sure the reason this has never been an issue before is that no other
application of record is using these system calls, and the ntpd code actually
sets the STA_NANO flag when it is run. Since no one would reasonably run the
ntptime application unless they were also running the daemon - it kind of
becomes a non issue.

Perhaps I am just completely missing something, but I don't see a way for a user
application like ntptime to determine if the nanokernel code was compiled with
NTP_NANO defined. And without that, the only way I see a user code getting what
it expects is for it to explicitly try setting STA_NANO. (Which requires root
permissions - and I don't really want to mess with the clock in my application.
I just need fast, accurate time stamps with error estimates. ntp_gettime seemed
natural.)

The fallback position I have taken in my code is simply to fail if the macro
STA_NANO is defined, and the bit is not set in status. I report that the user
must set the flag using ntptime. But, I am less than satisfied with this
solution. Perhaps I should get over it.

By the way - NTPv3 won't work with the nanokernel code if NTP_NANO is defined
either for this same reason. So, backwards compatibility is already lost, isn't
it?

"David L. Mills" wrote:
> Jeff,
>
> The devil is in the details. Older NTPv3 requires timeval
> (microseconds), regardless of operating system. Actually, a couple of
> timespecs sneaked in the NTPv3 code, but only to calibrate the precision
> in those systems where precision is calibratible. NTPv3 didn't know
> about STA_NANO, of course, so never set the bit and never got a STA_NANO
> response, even if the kernel could do the wicked nanodeed. Thus price
> backwards compatibility.
>
> I did consider the problem you raise, but the problem goes further than
> that. There's quite a lot of stuff ntp_adjtime() can do that affects
> ntp-gettime(), like fiddle frequency and phase. I thought to include a
> return code that tells ntp-gettime() that something changed since the
> last call and maybe it should be checked out. After all, there is
> nothing preventing some other process from changing things without
> ntp_gettime() syspecting. A clean and proper solution would require a
> lock and a trusted process that remembers things

Yes - that is much worse then I was originally thinking...

jeff

John Hay

unread,
Aug 28, 2002, 2:09:32 AM8/28/02
to
Hi Jeff,

: I was looking at this problem closer due to some interaction with John Hay. I'm


: sure you are sick of this problem by now, and I apologize for continuing this
: thread. However, it appears to me that things are actually inconsistent between
: your nanokernel distribution and the user code in ntp-4.1.1a. The inconsistency
: happens if NTP_NANO is defined for the nanokernel. In code in question is:

: nanokernel/ktime.c (ntp_gettime function):

: nano_time(&atv);
: #ifdef NTP_NANO
: ntv.time.tv_sec = atv.tv_sec;
: ntv.time.tv_nsec = atv.tv_nsec;
: #else
: if (!(time_status & STA_NANO))
: atv.tv_nsec /= 1000;
: ntv.time.tv_sec = atv.tv_sec;
: ntv.time.tv_usec = atv.tv_nsec;
: #endif /* NTP_NANO */

: As you can see, if NTP_NANO is defined, the time is returned in nanoseconds. It
: does not matter what state the STA_NANO flag is in.

: Perhaps I am just completely missing something, but I don't see a way for a user


: application like ntptime to determine if the nanokernel code was compiled with
: NTP_NANO defined. And without that, the only way I see a user code getting what
: it expects is for it to explicitly try setting STA_NANO. (Which requires root
: permissions - and I don't really want to mess with the clock in my application.
: I just need fast, accurate time stamps with error estimates. ntp_gettime seemed
: natural.)

I thought about it some more. The way a user-level program should approach
this is not too complicated. You use autoconf magic to detect if struct
ntptimeval use timespec or timeval for the time element. If it use
timespec, the value that ntp_gettime() will return is always in
nanoseconds. If ntp_gettime() use timeval, then you need to check if
STA_NANO is set to determine if the returned value is micro or nano
seconds. That should work on all systems that have implemented the
current (20010402) nanokernel code.

If you don't like that, you will have to lobby Dave to change ntp_gettime()
in the nanokernel distribution to something like this. Beware, this has
not been compiled or tested.

nano_time(&atv);


if (!(time_status & STA_NANO))
atv.tv_nsec /= 1000;
ntv.time.tv_sec = atv.tv_sec;

#ifdef NTP_NANO
ntv.time.tv_nsec = atv.tv_nsec;
#else


ntv.time.tv_usec = atv.tv_nsec;
#endif /* NTP_NANO */

ntv.maxerror = time_maxerror;
ntv.esterror = time_esterror;

John
--
John Hay -- John...@icomtek.csir.co.za / jh...@FreeBSD.org

Jeff W. Boote

unread,
Aug 28, 2002, 11:04:33 AM8/28/02
to

John Hay wrote:
> I thought about it some more. The way a user-level program should approach
> this is not too complicated. You use autoconf magic to detect if struct
> ntptimeval use timespec or timeval for the time element. If it use
> timespec, the value that ntp_gettime() will return is always in
> nanoseconds. If ntp_gettime() use timeval, then you need to check if
> STA_NANO is set to determine if the returned value is micro or nano
> seconds. That should work on all systems that have implemented the
> current (20010402) nanokernel code.

Will that really work? There are no implementations that define ntptimeval with
timespec and use the STA_NANO flag? I was under the, probably mistaken,
impression that there were microkernel clocks that used timespec for
ntp_gettime.

If simply checking for timespec works, then the NTP_NANO macro should probably
be defined in timex.h as well - but I'm fine with a build-time test to check the
structure type.

Dave, if this is all correct, it still means that changes would be required to
make ntptime consistent.

jeff

John Hay

unread,
Aug 28, 2002, 11:41:36 AM8/28/02
to
Jeff W. Boote <bo...@internet2.edu> wrote:

: John Hay wrote:
:> I thought about it some more. The way a user-level program should approach
:> this is not too complicated. You use autoconf magic to detect if struct
:> ntptimeval use timespec or timeval for the time element. If it use
:> timespec, the value that ntp_gettime() will return is always in
:> nanoseconds. If ntp_gettime() use timeval, then you need to check if
:> STA_NANO is set to determine if the returned value is micro or nano
:> seconds. That should work on all systems that have implemented the
:> current (20010402) nanokernel code.

: Will that really work? There are no implementations that define ntptimeval with
: timespec and use the STA_NANO flag? I was under the, probably mistaken,
: impression that there were microkernel clocks that used timespec for
: ntp_gettime.

: If simply checking for timespec works, then the NTP_NANO macro should probably
: be defined in timex.h as well - but I'm fine with a build-time test to check the
: structure type.

Well, I don't have real proof, but I think that a system that don't
consistantly define or not define NTP_NANO or un-ifdef their files is
broken.

: Dave, if this is all correct, it still means that changes would be required to
: make ntptime consistent.

Well if Dave is happy with this, I'll go and fix ntptime. It shouldn't be
too difficult. There is already a configure check that defines
HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC that can be used.

Hal Murray

unread,
Aug 28, 2002, 12:15:57 PM8/28/02
to
>If simply checking for timespec works, then the NTP_NANO macro should probably
>be defined in timex.h as well - but I'm fine with a build-time test to check the
>structure type.

I haven't double checked the code, but it seems reasonable to expect
code compiled on a system with or without NTP_NANO to work correctly
when run on a system with or without NTP_NANO.

What I thought the code did was:

If compiled with NTP_NANO, the client would set STP_NANO in the
arguments, and the kernel would return STP_NANO if the answer
was in nanos or turn it off if the answer was in micros.
If the client wanted nanos and STP_NANO wasn't set on the
return flags, the client could convert to micros.

If compiled without NTP_NANO, the client would not set STP_NANO
and the kernel (even if it knew about NTP_NANO) would return the
answer in micros and make sure the NTP_NANO flag was off.

Am I on the right track?

--
The suespammers.org mail server is located in California. So are all my
other mailboxes. Please do not send unsolicited bulk e-mail or unsolicited
commercial e-mail to my suespammers.org address or any of my other addresses.
These are my opinions, not necessarily my employer's. I hate spam.

David L. Mills

unread,
Aug 28, 2002, 9:14:34 PM8/28/02
to Jeff W. Boote
Jeff,

You're gonna love this. There are several combinations of kernel
geneology and NTP versionology, and some filth remains in the code to
cater for the all the insane combinations. Everything has to work with
both xntpd and ntpd and whether microkernel, nanokernel or no kernel at
all.

It is the intent when NTP_NANO is defined that the kernel interface is
in nanoseconds and when it is not defined the interface is in
microseconds. The ntptime() declares a timeval for historic reasons, but
uses it either as a timeval or a timespec, depending on whether the
kernel returns hot STA_NANO. This is broken autoconfiguration and should
be fixed.

So, in SunOS kernels without PPS and without timespec, NTP_NANO is not
defined and the clock is kept in microseconds regardless of whether
running old xntpd or new ntpd. This is compatible with the original
microkernel. NTP_NANO must be defined in SunOS kernels with PPS, but the
timespec definition must be kludged when needed. SunOS with PPS and
xntpd would probably be a lose. The NTPv4 routines get to figure out
on-fly which is the case, and there is the rub. Read on; it gets worse.

Now, note that the nano_time() routine reads the hardware in nanoseconds
and the micro_time() routine simply rounds and divides by 1000.
Actually, the ntp_gettime() conditional should have called the proper
routine in situ, with the tiny advantage of rounding. The micro_time()
routine is called directly by some legacy kernel routines, since lots of
kernel routines don't know about nanoseconds. Note the other places
where adjustments are scaled in nanoseconds or microseconds.

Now comes xntpd and ntpd and hence the confusion. There are three cases,
nothing, microtime and nanotime. In all cases the NTP distribution must
be compiled on the same configuration the kernel is compiled on.
STA_NANO appears only in the nanotime timex.h header, but not in the
microtime timex.h header. If it is defined and set, the clock runs in
nanoseconds; otherwise in microseconds. The ntptime routine was coded
before the nanokernel, so reckons in microseconds. Since the program
itself scales in microseconds, in that case it converts nanoseconds to
microseconds and sails on.

The code is not pretty and should be changed, but it is correct.

Dave

David L. Mills

unread,
Aug 28, 2002, 9:14:49 PM8/28/02
to
Jeff,

Dave

David L. Mills

unread,
Aug 28, 2002, 9:21:27 PM8/28/02
to
John,

No, the microtime kernel itself has no timespec. There are probably
cases (late Ultrix) where the system did have timespec, but the
microkernel didn't use it.

Dave

David L. Mills

unread,
Aug 28, 2002, 9:25:57 PM8/28/02
to
Hal,

Your words are much more economic than mine and equally correct.

Dave

John Hay

unread,
Aug 29, 2002, 12:06:48 PM8/29/02
to
David L. Mills <mi...@udel.edu> wrote:

: No, the microtime kernel itself has no timespec. There are probably


: cases (late Ultrix) where the system did have timespec, but the
: microkernel didn't use it.

So shouldn't the code for get_ntptime in the nanokernel distribution
then rather looks like this? Ie. If NTP_NANO is defined, use STA_NANO
to determine if nano or micro seconds should be returned. If NTP_NANO
is not defined, just return microseconds.

#############
s = splclock();


nano_time(&atv);
#ifdef NTP_NANO
ntv.time.tv_sec = atv.tv_sec;

if (!(time_status & STA_NANO))
atv.tv_nsec /= 1000;

ntv.time.tv_nsec = atv.tv_nsec;
#else


atv.tv_nsec /= 1000;
ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_usec = atv.tv_nsec;
#endif /* NTP_NANO */

ntv.maxerror = time_maxerror;
ntv.esterror = time_esterror;

#############

or as a diff:

--- nanokernel/nanokernel.20010402/ktime.c Mon Apr 2 19:32:25 2001
+++ /tmp/ktime.c Thu Aug 29 18:01:29 2002
@@ -168,10 +168,11 @@


nano_time(&atv);
#ifdef NTP_NANO
ntv.time.tv_sec = atv.tv_sec;

- ntv.time.tv_nsec = atv.tv_nsec;
-#else


if (!(time_status & STA_NANO))
atv.tv_nsec /= 1000;

+ ntv.time.tv_nsec = atv.tv_nsec;
+#else
+ atv.tv_nsec /= 1000;


ntv.time.tv_sec = atv.tv_sec;
ntv.time.tv_usec = atv.tv_nsec;
#endif /* NTP_NANO */

John

David L. Mills

unread,
Aug 29, 2002, 1:02:38 PM8/29/02
to John Hay
John,

Noooo. Once a timespec, always a timespec. In modern times ntp_gettime()
should always and ever return nanoseconds only. The only intent of the
STA_NANO bit is to detect legacy kernels. Someday the need for that
filth may go away. If I left misleading comments in the code, consider
it a rectocranial inversion.

Dave

David L. Mills

unread,
Aug 29, 2002, 1:02:51 PM8/29/02
to
John,

Noooo. Once a timespec, always a timespec. In modern times ntp_gettime()
should always and ever return nanoseconds only. The only intent of the
STA_NANO bit is to detect legacy kernels. Someday the need for that
filth may go away. If I left misleading comments in the code, consider
it a rectocranial inversion.

Dave

Jeff W. Boote

unread,
Aug 29, 2002, 2:07:05 PM8/29/02
to

"David L. Mills" wrote:
>
> Jeff,
>

> You're gonna love this.

Ugh.

> It is the intent when NTP_NANO is defined that the kernel interface is
> in nanoseconds and when it is not defined the interface is in
> microseconds. The ntptime() declares a timeval for historic reasons, but
> uses it either as a timeval or a timespec, depending on whether the
> kernel returns hot STA_NANO. This is broken autoconfiguration and should
> be fixed.

There's the rub. How do you fix it?

The only way I see for user code to work currently is if it does the same thing
the ntpd code does. Try to set the STA_NANO flag, and check for it on the
return. This of course means the process needs to run with root permissions -
something I do not like at all.

For the case of an application that does not have root permissions, the best it
can do is to check the current value of STA_NANO. If it is set, then it knows it
is going to get nanos, if it is not set - it currently has no way to determine
what it is going to get.

> So, in SunOS kernels without PPS and without timespec, NTP_NANO is not
> defined and the clock is kept in microseconds regardless of whether
> running old xntpd or new ntpd. This is compatible with the original
> microkernel. NTP_NANO must be defined in SunOS kernels with PPS, but the
> timespec definition must be kludged when needed. SunOS with PPS and
> xntpd would probably be a lose. The NTPv4 routines get to figure out
> on-fly which is the case, and there is the rub. Read on; it gets worse.
>
> Now, note that the nano_time() routine reads the hardware in nanoseconds
> and the micro_time() routine simply rounds and divides by 1000.
> Actually, the ntp_gettime() conditional should have called the proper
> routine in situ, with the tiny advantage of rounding. The micro_time()
> routine is called directly by some legacy kernel routines, since lots of
> kernel routines don't know about nanoseconds. Note the other places
> where adjustments are scaled in nanoseconds or microseconds.
>
> Now comes xntpd and ntpd and hence the confusion. There are three cases,
> nothing, microtime and nanotime. In all cases the NTP distribution must
> be compiled on the same configuration the kernel is compiled on.

Ok - I believe I have already shown that xntpd cannot work for the nanotime case
(nanokernel with NTP_NANO defined). This is because ntp_gettime always returns
time in nanoseconds in that case, and xntpd will always interpret that time as
microseconds.

(Also - this statement precludes Hal's comment in the previous message that user
code should be binary compatible between systems. If user code is compiled on a
system without STA_NANO in the timex.h header file, it will not work correctly
on a system where the kernel code is compiled with NTP_NANO.)

However - backwards compatibility in the case should be defined as user codes
with "newer" definitions of the kernel interface should still work on older
operating systems with the older kernel interface.

I don't believe it is useful to try and make sure that user codes compiled with
the "older" definition of the interface work on operating systems with the
"newer" interface.

> STA_NANO appears only in the nanotime timex.h header, but not in the
> microtime timex.h header. If it is defined and set, the clock runs in
> nanoseconds; otherwise in microseconds. The ntptime routine was coded
> before the nanokernel, so reckons in microseconds. Since the program
> itself scales in microseconds, in that case it converts nanoseconds to
> microseconds and sails on.

You know, I really wish there was simply an ntp_getnanotime() system call - it
would be a lot easier/ and more predictable to catch a nonexistent system call
to preserve backwards compatibility...

jeff

Jeff W. Boote

unread,
Aug 29, 2002, 2:23:18 PM8/29/02
to

"David L. Mills" wrote:
>
> John,
>
> Noooo. Once a timespec, always a timespec. In modern times ntp_gettime()
> should always and ever return nanoseconds only. The only intent of the
> STA_NANO bit is to detect legacy kernels. Someday the need for that
> filth may go away. If I left misleading comments in the code, consider
> it a rectocranial inversion.

Ok if I simply go by the test for a timespec vs. a timeval in the ntptimeval
structure as you seem to be suggesting. Then code I compile on a system that has
timespec will fail if I move that binary to a system that does not define
NTP_NANO, and does not use timespec.

Right? (Unless of course I run as root, attempt to set the STA_NANO flag and
see that it is not set in the return.)

jeff

David L. Mills

unread,
Aug 30, 2002, 12:48:43 PM8/30/02
to
Jeff,

Naughty boy. At mother's knee you learned very early never to use a
compiled binary on another machine unless you knew with certainty it had
the identical configuration as the compiler machine. Supporting legacy
filth is a Very Big Deal around here and I sure don't want to make it
more painful than absolutely necessary.

May I respectfully suggest the media content of continuing discussion on
this issue is probably minimal to the list and maybe we should off the
subject, off the list or both.

Dave

David L. Mills

unread,
Aug 30, 2002, 1:10:44 PM8/30/02
to Jeff W. Boote
Jeff,

I've <<always>> assumed xntpd and ntpd are compiled and used on machines
with identical configuration, including the presence or absence of
NTP_NANO. The xntpd compiles and runs under Solaris, HP-UX, SunOS,
Ultrix and Alpha microkernels just fine, since the kernel is in
microseconds and NTP_NANO is not defined. Using xntpd with the
nanokernel is silly, since the algorithms and discipline are completely
inadequate for nanosecond precision. In other words, if you insist on
using xntpd with the nanokernel, configure the build to disable kernel.

Now I understand your concern about testing STA_NANO and root
permission. As you know from previous discussion, I consider monkeying
with the clock other than by root very questionable. Maybe some way
could be found to work around your problem that is (a) completely
backwards compatible and (b) does not require changes in already
deployed nanokernel code. I would assume your changes be confined to
current NTPv4. The patch light is green.

Dave

David L. Mills

unread,
Aug 30, 2002, 1:10:57 PM8/30/02
to
Jeff,

I've <<always>> assumed xntpd and ntpd are compiled and used on machines
with identical configuration, including the presence or absence of
NTP_NANO. The xntpd compiles and runs under Solaris, HP-UX, SunOS,
Ultrix and Alpha microkernels just fine, since the kernel is in
microseconds and NTP_NANO is not defined. Using xntpd with the
nanokernel is silly, since the algorithms and discipline are completely
inadequate for nanosecond precision. In other words, if you insist on
using xntpd with the nanokernel, configure the build to disable kernel.

Now I understand your concern about testing STA_NANO and root
permission. As you know from previous discussion, I consider monkeying
with the clock other than by root very questionable. Maybe some way
could be found to work around your problem that is (a) completely
backwards compatible and (b) does not require changes in already
deployed nanokernel code. I would assume your changes be confined to
current NTPv4. The patch light is green.

Dave

0 new messages