Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 09 May 1996 08:29:51 -0400
From:      "Louis A. Mamakos" <louie@TransSys.COM>
To:        Poul-Henning Kamp <phk@critter.tfs.com>
Cc:        Garrett Wollman <wollman@freebsd.org>, freebsd-bugs@freebsd.org
Subject:   Re: kern/1179 
Message-ID:  <199605091229.IAA09791@whizzo.transsys.com>
In-Reply-To: Your message of "Wed, 08 May 1996 17:06:19 -0000." <2522.831575179@critter.tfs.com> 
References:  <2522.831575179@critter.tfs.com> 

next in thread | previous in thread | raw e-mail | index | archive | help
> > Synopsis: Add socket option to timestamp arriving UDP data queued to socket
> 
> xntpd will probably like this too.  Are there any other implementations 
> of the API we should stay close to for compatibility ?

As far as I know, the only other implementation of the timestamp
socket option is one that I did many years ago in 4.3 BSD Tahoe.  It
is substantially the same as that one; but that implementation never
want anywhere or say the light of day.  Using the control information
goo with the recvmsg() system call seems a very general purpose sort
of interface to return generic blobs of data.

What was disappointing (and what differs from my 4.3 Tahoe
implementation) is that this version only works with UDP sockets.  The
code which captures the timestamp really "belongs" in the socket
abstraction layer.  The problem is that the socket code in
uipc_socket2.c doesn't have access to the socket data structure, only
the struct sockbuf inside them.  Ideally you'd add code to do this in
sbappendrecord() and sbappendaddr() and sbappendcontrol() so that it
just works for all types of datagram oriented sockets.  But doing that
required changing the API to those funtions to pass down a pointer to
the socket struct, and that seems a little be too extreme right now.

I have some code for the very latest xntpd to use this, though I'm not
quite sure if I should try to retrofit it into the version in the
FreeBSD sources, or port the new version.  I did this code explicitly
for xntpd, and the version I have now has a bunch of instrumentation
to diagnose the effectiveness of this approach.  I've had a long
history with NTP, being the co-author of the original UNIX ntpd waaay
back when.

For your curiosity, here's the diffs to the latest xntpd to give you a
sense of how it gets used.  There's a bunch of diagnostic code in
there which measures and records the difference between the timestamp
captured the by socket option, and the timestamp returned by a
gettimeofday() in the SIGIO signal handler which is fired off when the
packet arrives.  

The numbers are rather interesting - they are mostly pretty good, with
the occasional extreme excursions when doing interesting things (like
rewinding a SCSI tape?)  The code captures and logs the peak delta
time offsets, as well as maintaining a histogram.  Here's one from my
133MHz Pentium after running for a while.  It's been mostly idle (no
make world, etc):

   0us| 0 |50us|  42 |60us| 190 |75us| 305 |100us| 107 |200us| 14 |500us| 3 
|1000us| 8 |5000us| 0 |10000us| 1 |50000us| 0 |100000us| 0 |150000us|

This is read like this: there were 0 samples between 0 and 50
microseconds, 42 samples between 50 and 60 microseconds, 190 samples
between 60 and 75 microseconds, etc.  And then there's one waay out at
somewhere between 10 and 50 *milliseconds* (actually measured at 12.4
milliseconds).  I've actually seen more extreme varience, both on this
machine and a 66MHz 486DX2, which has larger base numbers.
      
diff -ru xntp3/include/ntp_unixtime.h xntp3.5c-new/include/ntp_unixtime.h
--- xntp3/include/ntp_unixtime.h	Sun Jun 18 08:16:12 1995
+++ xntp3.5c-new/include/ntp_unixtime.h	Tue Apr 16 00:48:22 1996
@@ -27,6 +27,8 @@
 #define SETTIMEOFDAY(a, b) (winnt_settimeofday(a))
 #endif /* SYS_WINNT */
 
+extern	void	cvt_systime	P((struct timeval *, l_fp *));
+
 /*
  * Time of day conversion constant.  Ntp's time scale starts in 1900,
  * Unix in 1970.

diff -ru xntp3/lib/systime.c xntp3.5c-new/lib/systime.c
--- xntp3/lib/systime.c	Mon Mar 18 15:32:23 1996
+++ xntp3.5c-new/lib/systime.c	Tue Apr 16 00:44:22 1996
@@ -96,6 +96,25 @@
 }
 
 /*
+ * cvt_systime - return the converted system time in timestamp format
+ * from a passed-in timeval struct.
+ */
+void
+cvt_systime(tv, ts)
+	struct timeval *tv;
+	l_fp *ts;
+{
+	TVTOTS(tv, ts);
+	L_ADD(ts, &sys_clock_offset);
+	if (ts->l_uf & TS_ROUNDBIT)
+		L_ADDUF(ts, TS_ROUNDBIT);
+
+	ts->l_ui += JAN_1970;
+	ts->l_uf &= TS_MASK;
+}
+
+
+/*
  * step_systime - do a step adjustment in the system time (at least from
  *		  NTP's point of view.
  */

diff -ru xntp3/xntpd/ntp_io.c xntp3.5c-new/xntpd/ntp_io.c
--- xntp3/xntpd/ntp_io.c	Sat Mar 16 11:39:34 1996
+++ xntp3.5c-new/xntpd/ntp_io.c	Thu May  2 00:57:51 1996
@@ -21,6 +21,11 @@
 #ifdef SYS_UXPV
 #include <sys/sockio.h>
 #endif
+#if defined(USE_TIMESTAMP_OPTION)
+#if defined(SYS_FREEBSD) || defined(SYS_NETBSD) || defined(SYS_BSDI)
+#include <sys/uio.h>
+#endif
+#endif
 
 #include "ntpd.h"
 #include "ntp_select.h"
@@ -732,6 +737,14 @@
 		syslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m");
 	}
 
+#if	defined(USE_TIMESTAMP_OPTION) && defined(SO_TIMESTAMP)
+	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on, sizeof(on))) {
+		syslog(LOG_ERR, "setsockopt SO_TIMESTAMP on fails: %m");
+#ifdef	DEBUG
+		if (debug) printf("setsockopt(SO_TIMESTAMP) fails\n");
+#endif
+	}
+#endif
 	/*
 	 * bind the local address.
 	 */
@@ -1099,6 +1112,76 @@
   return buffer;
 }
 
+#if	defined(USE_TIMESTAMP_OPTION) && defined(SO_TIMESTAMP) && defined(DEBUG)
+
+struct offlimits {
+    unsigned int low,  count;
+} offhist[] = {
+    0,		0,
+    50,		0,
+    60,		0,
+    75,		0,
+    100,	0,	/* 100 us */
+    200,	0,	/* 200 us */
+    500,	0,	/* 500 us */
+    1000,	0,	/* 1 ms */
+    5000,	0,	/* 5 ms */
+    10000,	0,	/* 10 ms */
+    50000,	0,	/* 50 ms */
+    100000,	0,	/* 100 ms */
+    150000,	0,	/* 150 ms */
+    200000,	0,	/* 200 ms */
+    100000000,	0	/* big */
+};
+
+static int tsopt_samples;
+
+tsopt_stats(deltafp)
+	l_fp *deltafp;
+{
+	l_fp work;
+	int i;
+
+	work = *deltafp;
+	if (L_ISNEG(&work)) {
+		L_NEG(&work);
+	}
+
+	if (work.l_ui) {
+		syslog(LOG_NOTICE, "delta offset exceeds 1000ms");
+		return;
+	}
+	for (i = 0; i < 6; i++) {
+		l_fp ftmp;
+
+		L_LSHIFT(&work);	/*  x2 */
+		ftmp = work;
+		L_LSHIFT(&work);	/*  x4 */
+		L_LSHIFT(&work);	/*  x8 */
+		L_ADD(&work, &ftmp);	/*  x8 + x2 = x10 */
+	}
+
+	/* work is now in units of microseconds */
+	for (i = 0; offhist[i].low < 10000000; i++) {
+		if (work.l_ui < offhist[i+1].low) {
+			offhist[i].count++;
+			break;
+		 }
+	}
+	if ((++tsopt_samples % 10) == 0) {
+	    char buf[2048], *cp = buf;
+	    for (i = 0; offhist[i].low < 10000000; i++) {
+		(void) sprintf(cp, "%d| %d |", offhist[i].low,
+			       offhist[i].count);
+		while (*cp)
+		    cp++;
+	    }
+	    syslog(LOG_NOTICE, "tsopt: %s", buf);
+	}
+}
+#endif
+
+
 /*
  * input_handler - receive packets asynchronously
  */
@@ -1115,6 +1198,14 @@
 	l_fp ts;
 	fd_set fds;
 	int first = 1;
+#if	defined(USE_TIMESTAMP_OPTION) && defined(SO_TIMESTAMP)
+	struct msghdr msg;
+	char cmsg[128];
+	struct cmsghdr *cm;
+	struct iovec iov[1];
+	struct timeval tv_pkt;
+	static l_fp max_delta = {0,0};
+#endif
 
 	handler_calls++;
 	ts = *cts;
@@ -1306,11 +1397,25 @@
 				free_recvbufs--;
 	
 				fromlen = sizeof(struct sockaddr_in);
+#if	defined(USE_TIMESTAMP_OPTION) && defined(SO_TIMESTAMP)
+				msg.msg_name = (caddr_t) &rb->recv_srcadr;
+				msg.msg_namelen = fromlen;
+				msg.msg_iov = iov;
+				iov[0].iov_base = (caddr_t) &rb->recv_space;
+				iov[0].iov_len = sizeof(rb->recv_space);
+				msg.msg_iovlen = 1;
+				msg.msg_control = cmsg;
+				msg.msg_controllen = sizeof(cmsg);
+				msg.msg_flags = 0;
+
+				rb->recv_length = recvmsg(fd, &msg, 0);
+#else
 				rb->recv_length = recvfrom(fd,
 				    (char *)&rb->recv_space,
 				    sizeof(rb->recv_space), 0,
 				    (struct sockaddr *)&rb->recv_srcadr,
 				    &fromlen);
+#endif
 				if (rb->recv_length == -1) {
 					syslog(LOG_ERR, "recvfrom: %m");
 					rb->next = freelist;
@@ -1337,6 +1442,39 @@
 				rb->dstadr = &inter_list[i];
 				rb->fd = fd;
 				rb->recv_time = ts;
+#if	defined(USE_TIMESTAMP_OPTION) && defined(SO_TIMESTAMP)
+				if (msg.msg_controllen) {
+				    l_fp deltafp;
+
+				    cm = (struct cmsghdr *) cmsg;
+				    for (cm = CMSG_FIRSTHDR(&msg); cm;
+					 cm = CMSG_NXTHDR(&msg, cm)) {
+					if (cm->cmsg_level = SOL_SOCKET &&
+					    cm->cmsg_type == SCM_TIMESTAMP) {
+
+					    memcpy(&tv_pkt, CMSG_DATA(cm),
+						   sizeof(tv_pkt));
+					    cvt_systime(&tv_pkt,
+							&rb->recv_time);
+					    deltafp = ts;
+					    L_SUB(&deltafp, &rb->recv_time);
+					    tsopt_stats(&deltafp);
+					    if (L_ISGEQ(&deltafp, &max_delta)) {
+						max_delta = deltafp;
+						syslog(LOG_NOTICE, "New max SIGIO/timestamp opt delta %s ms",
+						       lfptoms(&max_delta, 8));
+					    }
+#ifdef	DEBUG
+	if (debug) {
+	    printf("input_handler: fd=%d got timestamp opt tv %u/%u offset %s ms (max off %sms)\n",
+		   fd, tv_pkt.tv_sec, tv_pkt.tv_usec, lfptoms(&deltafp, 8), lfptoms(&max_delta, 8));
+	}
+#endif
+					    break;
+					}
+				    }
+				}
+#endif
 				rb->receiver = receive;
 	
 



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