Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 17 Oct 1999 16:39:53 -0700 (PDT)
From:      "Jason K. Fritcher" <jkf@wolfnet.org>
To:        freebsd-hackers@freebsd.org
Subject:   PR kern/14034: gettimeofday() returns negative value
Message-ID:  <Pine.BSF.4.10.9910161847210.31697-100000@solaris.wolfnet.org>

next in thread | raw e-mail | index | archive | help

Hello. As a starting point to learn the FreeBSD kernel, and as a starting
point for contributing back to the best free OS, I have decided to start
trying to fix bugs in the bug database. The PR in the subject seemed like a
subsystem that didn't seem too complex, so I made that my starting point.

I been looking through source code, trying to understand it, and I think I
do. What I would like to do is verify that my understanding of the code is
correct.

When gettimeofday() is called from a program, it enters the kernel at the
gettimeofday() function in src/sys/kern/kern_time.c. If a timeval pointer
has been given, it calls microtime() to get the time of day. Below is
microtime().

void     
microtime(struct timeval *tv) 
{
        struct timecounter *tc;

        tc = (struct timecounter *)timecounter;
        tv->tv_sec = tc->tc_offset_sec;
        tv->tv_usec = tc->tc_offset_micro;
        tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
        tv->tv_usec += boottime.tv_usec;
        tv->tv_sec += boottime.tv_sec;
        while (tv->tv_usec >= 1000000) {
                tv->tv_usec -= 1000000;
                tv->tv_sec++;
        }
}


In microtime(), it reads the seconds and microseconds since boot and puts
them into the timeval struct. Next, tco_delta is called. The way I
understand that code is it polls the counter so it knows how much time has
elapsed since timecounter was updated by an interrupt.

Since tco_delta is an inline function, that line gets expanded to this:

        tv->tv_usec += ((u_int64_t)((tc->tc_get_timecount(tc) -
                         tc->tc_offset_count) & tc->tc_counter_mask) *
                         tc->tc_scale_micro) >> 32;

The way I am understanding this code, it reads the current value of the
counter, which is going to be in ticks, then subtracts tc_offset_micro,
which, unless I am mistaken, is in microseconds. This doesn't seem quite
right to me. The remainder is then AND'ed with the counter's mask value to
get a delta. With 64 bit, unsigned math, the delta is multiplied with the
counter's scale value to translate the ticks into microseconds, leaves the
result in the upper dword, and then shifts the result into the lower dword.
The result is then added to the microsecond value in the timeval struct.
Another thing that strikes me as odd about this line is the fact that the
final math is done unsigned, but the result is put into a signed container.

Next the boottime seconds and microseconds are added to the timeval struct,
and finally if the microseconds in the timeval is larger than a second, it
and the seconds value gets adjusted appropriately. gettimeofday then
finishes and control is passed back to the user.

The only concern I have about the code is if the final delta value that is
computed is large enough to set the MSB in the lower dword, when it gets
added into the timeval struct, the previously positive signed value becomes
a negative signed value. It seems to me that this could possibly lead to
what is being seen in the PR.

I hope I am just misunderstanding the code, because this seems like it could
be a real pain.

Thanx.

--
 Jason K. Fritcher
  jkf@wolfnet.org



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.10.9910161847210.31697-100000>