/* 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. * * Some of this was swiped from the NTPD distribution. */ #include #include #include #include #include #include #include #include #include #include #include "ntpshm.h" struct shmTime *shm_get(const int unit, const bool create, const 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), (create ? IPC_CREAT : 0) | (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; shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0; clock_gettime(CLOCK_REALTIME, &shm_stat->tvc); if (shm == NULL) { shm_stat->mode = NO_SEGMENT; return NO_SEGMENT; } if (!shm->valid) { shm_stat->mode = NOT_READY; 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) { shm_stat->mode = CLASH; 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: shm_stat->mode = BAD_MODE; return BAD_MODE; break; } shm->valid = 0; /* * leap field is not a leap offset but a leap notification code. * The values are magic numbers used by NTP and set by GPSD, if at all, in * the subframe code. */ shm_stat->leap = shm->leap; shm_stat->precision = shm->precision; shm_stat->mode = OK; return OK; } /* end */