diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2015-02-24 22:53:07 -0500 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2015-02-25 00:38:23 -0500 |
commit | b7ee3abbaf7e77dccf3bc2c355e07ea519e4fe9e (patch) | |
tree | ecc669a9fd17bec5c58848a047d4a41edf577723 /ntpshmread.c | |
parent | 908344d4d6e6ff314ea263f8ec227f23e4826cee (diff) | |
download | gpsd-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.c | 141 |
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 */ |