summaryrefslogtreecommitdiff
path: root/ntpshmread.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2015-02-24 22:53:07 -0500
committerEric S. Raymond <esr@thyrsus.com>2015-02-25 00:38:23 -0500
commitb7ee3abbaf7e77dccf3bc2c355e07ea519e4fe9e (patch)
treeecc669a9fd17bec5c58848a047d4a41edf577723 /ntpshmread.c
parent908344d4d6e6ff314ea263f8ec227f23e4826cee (diff)
downloadgpsd-b7ee3abbaf7e77dccf3bc2c355e07ea519e4fe9e.tar.gz
First cut at an interface for the read side of an NTP SHM connection.
Not yet used or tested.
Diffstat (limited to 'ntpshmread.c')
-rw-r--r--ntpshmread.c141
1 files changed, 141 insertions, 0 deletions
diff --git a/ntpshmread.c b/ntpshmread.c
new file mode 100644
index 00000000..e7362e8c
--- /dev/null
+++ b/ntpshmread.c
@@ -0,0 +1,141 @@
+/* ntpshmread.c -- monitor the inner end of an ntpshm connection
+ *
+ * This file is Copyright (c) 2010 by the GPSD project
+ * BSD terms apply: see the file COPYING in the distribution root for details.
+ *
+ */
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "ntpshm.h"
+
+struct shmTime *shm_get(int unit, bool forall)
+/* initialize an initial segment */
+{
+ struct shmTime *p = NULL;
+ int shmid;
+
+ /*
+ * Big units will give non-ascii but that's OK
+ * as long as everybody does it the same way.
+ */
+ shmid = shmget(NTPD_BASE + unit, sizeof(struct shmTime),
+ IPC_CREAT | (forall ? 0666 : 0600));
+ if (shmid == -1) { /* error */
+ return NULL;
+ }
+ p = (struct shmTime *)shmat (shmid, 0, 0);
+ if (p == (struct shmTime *)-1) { /* error */
+ return NULL;
+ }
+ return p;
+}
+
+char *shm_name(const int unit)
+/* return the name of a specified segment */
+{
+ static char name[5] = "NTP\0";
+
+ name[3] = '0' + (char)unit;
+
+ return name;
+}
+
+enum segstat_t shm_query(struct shmTime *shm_in, struct shm_stat_t *shm_stat)
+/* try to grab a sample from the specified SHM segment */
+{
+ /* access order is important for lock-free SHM access; we
+ ** enforce order by treating the whole structure volatile.
+ **
+ ** IMPORTANT NOTE: This does not protect from reordering on CPU
+ ** level, and it does nothing for cache consistency and
+ ** visibility of changes by other cores. We need atomic and/or
+ ** fence instructions for that.
+ */
+ volatile struct shmTime *shm = shm_in;
+
+ unsigned int cns_new, rns_new;
+ int cnt;
+
+ if (shm == NULL)
+ return NO_SEGMENT;
+ if (!shm->valid) {
+ return NOT_READY;
+ }
+
+ switch (shm->mode) {
+ case 0:
+ shm_stat->tvr.tv_sec = shm->receiveTimeStampSec;
+ shm_stat->tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
+ rns_new = shm->receiveTimeStampNSec;
+ shm_stat->tvt.tv_sec = shm->clockTimeStampSec;
+ shm_stat->tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
+ cns_new = shm->clockTimeStampNSec;
+
+ /* Since the following comparisons are between unsigned
+ ** variables they are always well defined, and any
+ ** (signed) underflow will turn into very large unsigned
+ ** values, well above the 1000 cutoff.
+ **
+ ** Note: The usecs *must* be a *truncated*
+ ** representation of the nsecs. This code will fail for
+ ** *rounded* usecs, and the logic to deal with
+ ** wrap-arounds in the presence of rounded values is
+ ** much more convoluted.
+ */
+ if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
+ && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
+ shm_stat->tvt.tv_nsec = cns_new;
+ shm_stat->tvr.tv_nsec = rns_new;
+ }
+ /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level
+ ** timestamps, possibly generated by extending the old
+ ** us-level timestamps
+ */
+ break;
+
+ case 1:
+ cnt = shm->count;
+
+ shm_stat->tvr.tv_sec = shm->receiveTimeStampSec;
+ shm_stat->tvr.tv_nsec = shm->receiveTimeStampUSec * 1000;
+ rns_new = shm->receiveTimeStampNSec;
+ shm_stat->tvt.tv_sec = shm->clockTimeStampSec;
+ shm_stat->tvt.tv_nsec = shm->clockTimeStampUSec * 1000;
+ cns_new = shm->clockTimeStampNSec;
+ if (cnt != shm->count) {
+ return CLASH;
+ }
+
+ /* See the case above for an explanation of the
+ ** following test.
+ */
+ if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
+ && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
+ shm_stat->tvt.tv_nsec = cns_new;
+ shm_stat->tvr.tv_nsec = rns_new;
+ }
+ /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level
+ ** timestamps, possibly generated by extending the old
+ ** us-level timestamps
+ */
+ break;
+
+ default:
+ return BAD_MODE;
+ }
+ shm->valid = 0;
+
+ shm_stat->leap = shm->leap;
+ return OK;
+}
+
+/* end */