154359Sroberto/* 2290001Sglebius * refclock_shm - clock driver for utc via shared memory 354359Sroberto * - under construction - 454359Sroberto * To add new modes: Extend or union the shmTime-struct. Do not 554359Sroberto * extend/shrink size, because otherwise existing implementations 654359Sroberto * will specify wrong size of shared memory-segment 754359Sroberto * PB 18.3.97 854359Sroberto */ 954359Sroberto 1054359Sroberto#ifdef HAVE_CONFIG_H 1154359Sroberto# include <config.h> 1254359Sroberto#endif 1354359Sroberto 14290001Sglebius#include "ntp_types.h" 15290001Sglebius 1654359Sroberto#if defined(REFCLOCK) && defined(CLOCK_SHM) 1754359Sroberto 1854359Sroberto#include "ntpd.h" 19290001Sglebius#undef fileno 2054359Sroberto#include "ntp_io.h" 21290001Sglebius#undef fileno 2254359Sroberto#include "ntp_refclock.h" 23290001Sglebius#undef fileno 24290001Sglebius#include "timespecops.h" 25290001Sglebius#undef fileno 2654359Sroberto#include "ntp_stdlib.h" 27290001Sglebius#include "ntp_assert.h" 2854359Sroberto 29290001Sglebius#undef fileno 3082498Sroberto#include <ctype.h> 31290001Sglebius#undef fileno 3282498Sroberto 3354359Sroberto#ifndef SYS_WINNT 3482498Sroberto# include <sys/ipc.h> 3582498Sroberto# include <sys/shm.h> 3682498Sroberto# include <assert.h> 3782498Sroberto# include <unistd.h> 3882498Sroberto# include <stdio.h> 3954359Sroberto#endif 4054359Sroberto 41290001Sglebius#ifdef HAVE_STDATOMIC_H 42290001Sglebius# include <stdatomic.h> 43290001Sglebius#endif /* HAVE_STDATOMIC_H */ 44290001Sglebius 4554359Sroberto/* 4654359Sroberto * This driver supports a reference clock attached thru shared memory 47290001Sglebius */ 4854359Sroberto 4954359Sroberto/* 5054359Sroberto * SHM interface definitions 5154359Sroberto */ 5254359Sroberto#define PRECISION (-1) /* precision assumed (0.5 s) */ 5354359Sroberto#define REFID "SHM" /* reference ID */ 5454359Sroberto#define DESCRIPTION "SHM/Shared memory interface" 5554359Sroberto 5654359Sroberto#define NSAMPLES 3 /* stages of median filter */ 5754359Sroberto 5854359Sroberto/* 59290001Sglebius * Mode flags 60290001Sglebius */ 61290001Sglebius#define SHM_MODE_PRIVATE 0x0001 62290001Sglebius 63290001Sglebius/* 6454359Sroberto * Function prototypes 6554359Sroberto */ 66290001Sglebiusstatic int shm_start (int unit, struct peer *peer); 67290001Sglebiusstatic void shm_shutdown (int unit, struct peer *peer); 68290001Sglebiusstatic void shm_poll (int unit, struct peer *peer); 69290001Sglebiusstatic void shm_timer (int unit, struct peer *peer); 70290001Sglebiusstatic void shm_clockstats (int unit, struct peer *peer); 71290001Sglebiusstatic void shm_control (int unit, const struct refclockstat * in_st, 72290001Sglebius struct refclockstat * out_st, struct peer *peer); 7354359Sroberto 7454359Sroberto/* 7554359Sroberto * Transfer vector 7654359Sroberto */ 7754359Srobertostruct refclock refclock_shm = { 7854359Sroberto shm_start, /* start up driver */ 7954359Sroberto shm_shutdown, /* shut down driver */ 80290001Sglebius shm_poll, /* transmit poll message */ 81290001Sglebius shm_control, /* control settings */ 82290001Sglebius noentry, /* not used: init */ 83290001Sglebius noentry, /* not used: buginfo */ 84290001Sglebius shm_timer, /* once per second */ 8554359Sroberto}; 86290001Sglebius 8754359Srobertostruct shmTime { 88290001Sglebius int mode; /* 0 - if valid is set: 89290001Sglebius * use values, 9054359Sroberto * clear valid 91290001Sglebius * 1 - if valid is set: 9254359Sroberto * if count before and after read of values is equal, 93290001Sglebius * use values 9454359Sroberto * clear valid 9554359Sroberto */ 96290001Sglebius volatile int count; 97290001Sglebius time_t clockTimeStampSec; 98290001Sglebius int clockTimeStampUSec; 99290001Sglebius time_t receiveTimeStampSec; 100290001Sglebius int receiveTimeStampUSec; 101290001Sglebius int leap; 102290001Sglebius int precision; 103290001Sglebius int nsamples; 104290001Sglebius volatile int valid; 105290001Sglebius unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ 106290001Sglebius unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ 107290001Sglebius int dummy[8]; 10854359Sroberto}; 109132451Sroberto 110290001Sglebiusstruct shmunit { 111290001Sglebius struct shmTime *shm; /* pointer to shared memory segment */ 112290001Sglebius int forall; /* access for all UIDs? */ 113132451Sroberto 114290001Sglebius /* debugging/monitoring counters - reset when printed */ 115290001Sglebius int ticks; /* number of attempts to read data*/ 116290001Sglebius int good; /* number of valid samples */ 117290001Sglebius int notready; /* number of peeks without data ready */ 118290001Sglebius int bad; /* number of invalid samples */ 119290001Sglebius int clash; /* number of access clashes while reading */ 120290001Sglebius 121290001Sglebius time_t max_delta; /* difference limit */ 122290001Sglebius time_t max_delay; /* age/stale limit */ 123290001Sglebius}; 124290001Sglebius 125290001Sglebius 126290001Sglebiusstatic struct shmTime* 127290001SglebiusgetShmTime( 128290001Sglebius int unit, 129290001Sglebius int/*BOOL*/ forall 130290001Sglebius ) 131290001Sglebius{ 132290001Sglebius struct shmTime *p = NULL; 133290001Sglebius 13454359Sroberto#ifndef SYS_WINNT 13554359Sroberto 136290001Sglebius int shmid; 137290001Sglebius 138290001Sglebius /* 0x4e545030 is NTP0. 139290001Sglebius * Big units will give non-ascii but that's OK 140290001Sglebius * as long as everybody does it the same way. 141290001Sglebius */ 142290001Sglebius shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime), 143290001Sglebius IPC_CREAT | (forall ? 0666 : 0600)); 144290001Sglebius if (shmid == -1) { /* error */ 145290001Sglebius msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit); 146290001Sglebius return NULL; 14754359Sroberto } 148290001Sglebius p = (struct shmTime *)shmat (shmid, 0, 0); 149290001Sglebius if (p == (struct shmTime *)-1) { /* error */ 150290001Sglebius msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit); 151290001Sglebius return NULL; 15254359Sroberto } 153290001Sglebius 154290001Sglebius return p; 15554359Sroberto#else 156290001Sglebius 157290001Sglebius static const char * nspref[2] = { "Local", "Global" }; 158290001Sglebius char buf[20]; 159290001Sglebius LPSECURITY_ATTRIBUTES psec = 0; 160290001Sglebius HANDLE shmid = 0; 16154359Sroberto SECURITY_DESCRIPTOR sd; 16254359Sroberto SECURITY_ATTRIBUTES sa; 163290001Sglebius unsigned int numch; 164290001Sglebius 165290001Sglebius numch = snprintf(buf, sizeof(buf), "%s\\NTP%d", 166290001Sglebius nspref[forall != 0], (unit & 0xFF)); 167290001Sglebius if (numch >= sizeof(buf)) { 168290001Sglebius msyslog(LOG_ERR, "SHM name too long (unit %d)", unit); 169290001Sglebius return NULL; 170290001Sglebius } 171290001Sglebius if (forall) { /* world access */ 17254359Sroberto if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { 173290001Sglebius msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit); 174290001Sglebius return NULL; 17554359Sroberto } 176290001Sglebius if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) { 177290001Sglebius msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit); 178290001Sglebius return NULL; 17954359Sroberto } 180290001Sglebius sa.nLength = sizeof(SECURITY_ATTRIBUTES); 181290001Sglebius sa.lpSecurityDescriptor = &sd; 182290001Sglebius sa.bInheritHandle = FALSE; 183290001Sglebius psec = &sa; 18454359Sroberto } 185290001Sglebius shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE, 186290001Sglebius 0, sizeof (struct shmTime), buf); 187290001Sglebius if (shmid == NULL) { /*error*/ 188290001Sglebius char buf[1000]; 18954359Sroberto FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 19054359Sroberto 0, GetLastError (), 0, buf, sizeof (buf), 0); 191290001Sglebius msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf); 192290001Sglebius return NULL; 19354359Sroberto } 194290001Sglebius p = (struct shmTime *)MapViewOfFile(shmid, FILE_MAP_WRITE, 0, 0, 195290001Sglebius sizeof (struct shmTime)); 196290001Sglebius if (p == NULL) { /*error*/ 197290001Sglebius char buf[1000]; 198290001Sglebius FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 199290001Sglebius 0, GetLastError (), 0, buf, sizeof (buf), 0); 200290001Sglebius msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf); 201290001Sglebius return NULL; 20254359Sroberto } 203290001Sglebius 204290001Sglebius return p; 20554359Sroberto#endif 206290001Sglebius 207290001Sglebius /* NOTREACHED */ 208290001Sglebius ENSURE(!"getShmTime(): Not reached."); 20954359Sroberto} 210290001Sglebius 211290001Sglebius 21254359Sroberto/* 21354359Sroberto * shm_start - attach to shared memory 21454359Sroberto */ 21554359Srobertostatic int 21654359Srobertoshm_start( 21754359Sroberto int unit, 21854359Sroberto struct peer *peer 21954359Sroberto ) 22054359Sroberto{ 221290001Sglebius struct refclockproc * const pp = peer->procptr; 222290001Sglebius struct shmunit * const up = emalloc_zero(sizeof(*up)); 223290001Sglebius 22454359Sroberto pp->io.clock_recv = noentry; 225290001Sglebius pp->io.srcclock = peer; 22654359Sroberto pp->io.datalen = 0; 22754359Sroberto pp->io.fd = -1; 22854359Sroberto 229290001Sglebius up->forall = (unit >= 2) && !(peer->ttl & SHM_MODE_PRIVATE); 230290001Sglebius 231290001Sglebius up->shm = getShmTime(unit, up->forall); 232290001Sglebius 23354359Sroberto /* 23454359Sroberto * Initialize miscellaneous peer variables 23554359Sroberto */ 23654359Sroberto memcpy((char *)&pp->refid, REFID, 4); 237290001Sglebius if (up->shm != 0) { 238290001Sglebius pp->unitptr = up; 239290001Sglebius up->shm->precision = PRECISION; 240290001Sglebius peer->precision = up->shm->precision; 241290001Sglebius up->shm->valid = 0; 242290001Sglebius up->shm->nsamples = NSAMPLES; 24354359Sroberto pp->clockdesc = DESCRIPTION; 244290001Sglebius /* items to be changed later in 'shm_control()': */ 245290001Sglebius up->max_delay = 5; 246290001Sglebius up->max_delta = 4*3600; 247290001Sglebius return 1; 248290001Sglebius } else { 249290001Sglebius free(up); 250290001Sglebius pp->unitptr = NULL; 25154359Sroberto return 0; 25254359Sroberto } 25354359Sroberto} 25454359Sroberto 25554359Sroberto 25654359Sroberto/* 257290001Sglebius * shm_control - configure flag1/time2 params 258290001Sglebius * 259290001Sglebius * These are not yet available during 'shm_start', so we have to do any 260290001Sglebius * pre-computations we want to avoid during regular poll/timer callbacks 261290001Sglebius * in this callback. 262290001Sglebius */ 263290001Sglebiusstatic void 264290001Sglebiusshm_control( 265290001Sglebius int unit, 266290001Sglebius const struct refclockstat * in_st, 267290001Sglebius struct refclockstat * out_st, 268290001Sglebius struct peer * peer 269290001Sglebius ) 270290001Sglebius{ 271290001Sglebius struct refclockproc * const pp = peer->procptr; 272290001Sglebius struct shmunit * const up = pp->unitptr; 273290001Sglebius 274290001Sglebius UNUSED_ARG(unit); 275290001Sglebius UNUSED_ARG(in_st); 276290001Sglebius UNUSED_ARG(out_st); 277290001Sglebius if (NULL == up) 278290001Sglebius return; 279290001Sglebius if (pp->sloppyclockflag & CLK_FLAG1) 280290001Sglebius up->max_delta = 0; 281290001Sglebius else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.) 282290001Sglebius up->max_delta = 4*3600; 283290001Sglebius else 284290001Sglebius up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5); 285290001Sglebius} 286290001Sglebius 287290001Sglebius 288290001Sglebius/* 28954359Sroberto * shm_shutdown - shut down the clock 29054359Sroberto */ 29154359Srobertostatic void 29254359Srobertoshm_shutdown( 29354359Sroberto int unit, 29454359Sroberto struct peer *peer 29554359Sroberto ) 29654359Sroberto{ 297290001Sglebius struct refclockproc * const pp = peer->procptr; 298290001Sglebius struct shmunit * const up = pp->unitptr; 29954359Sroberto 300290001Sglebius UNUSED_ARG(unit); 301290001Sglebius if (NULL == up) 302290001Sglebius return; 30354359Sroberto#ifndef SYS_WINNT 304290001Sglebius 305290001Sglebius /* HMS: shmdt() wants char* or const void * */ 306290001Sglebius (void)shmdt((char *)up->shm); 307290001Sglebius 30854359Sroberto#else 309290001Sglebius 310290001Sglebius UnmapViewOfFile(up->shm); 311290001Sglebius 31254359Sroberto#endif 313290001Sglebius free(up); 31454359Sroberto} 31554359Sroberto 31654359Sroberto 31754359Sroberto/* 31854359Sroberto * shm_poll - called by the transmit procedure 31954359Sroberto */ 32054359Srobertostatic void 32154359Srobertoshm_poll( 32254359Sroberto int unit, 32354359Sroberto struct peer *peer 32454359Sroberto ) 32554359Sroberto{ 326290001Sglebius struct refclockproc * const pp = peer->procptr; 327290001Sglebius struct shmunit * const up = pp->unitptr; 328290001Sglebius int major_error; 32954359Sroberto 330290001Sglebius pp->polls++; 331290001Sglebius 332290001Sglebius /* get dominant reason if we have no samples at all */ 333290001Sglebius major_error = max(up->notready, up->bad); 334290001Sglebius major_error = max(major_error, up->clash); 335290001Sglebius 336290001Sglebius /* 337290001Sglebius * Process median filter samples. If none received, see what 338290001Sglebius * happened, tell the core and keep going. 339290001Sglebius */ 340290001Sglebius if (pp->coderecv != pp->codeproc) { 341290001Sglebius /* have some samples, everything OK */ 342290001Sglebius pp->lastref = pp->lastrec; 343290001Sglebius refclock_receive(peer); 344290001Sglebius } else if (NULL == up->shm) { /* is this possible at all? */ 345290001Sglebius /* we're out of business without SHM access */ 34654359Sroberto refclock_report(peer, CEVNT_FAULT); 347290001Sglebius } else if (major_error == up->clash) { 348290001Sglebius /* too many collisions is like a bad signal */ 349290001Sglebius refclock_report(peer, CEVNT_PROP); 350290001Sglebius } else if (major_error == up->bad) { 351290001Sglebius /* too much stale/bad/garbled data */ 352290001Sglebius refclock_report(peer, CEVNT_BADREPLY); 353290001Sglebius } else { 354290001Sglebius /* in any other case assume it's just a timeout */ 355290001Sglebius refclock_report(peer, CEVNT_TIMEOUT); 356290001Sglebius } 357290001Sglebius /* shm_clockstats() clears the tallies, so it must be last... */ 358290001Sglebius shm_clockstats(unit, peer); 359290001Sglebius} 360290001Sglebius 361290001Sglebius 362290001Sglebiusenum segstat_t { 363290001Sglebius OK, NO_SEGMENT, NOT_READY, BAD_MODE, CLASH 364290001Sglebius}; 365290001Sglebius 366290001Sglebiusstruct shm_stat_t { 367290001Sglebius int status; 368290001Sglebius int mode; 369290001Sglebius struct timespec tvc, tvr, tvt; 370290001Sglebius int precision; 371290001Sglebius int leap; 372290001Sglebius}; 373290001Sglebius 374290001Sglebiusstatic inline void memory_barrier(void) 375290001Sglebius{ 376290001Sglebius#ifdef HAVE_ATOMIC_THREAD_FENCE 377290001Sglebius atomic_thread_fence(memory_order_seq_cst); 378290001Sglebius#endif /* HAVE_ATOMIC_THREAD_FENCE */ 379290001Sglebius} 380290001Sglebius 381290001Sglebiusstatic enum segstat_t shm_query(volatile struct shmTime *shm_in, struct shm_stat_t *shm_stat) 382290001Sglebius/* try to grab a sample from the specified SHM segment */ 383290001Sglebius{ 384293896Sglebius struct shmTime shmcopy; 385293896Sglebius volatile struct shmTime *shm = shm_in; 386290001Sglebius volatile int cnt; 387290001Sglebius 388290001Sglebius unsigned int cns_new, rns_new; 389290001Sglebius 390290001Sglebius /* 391290001Sglebius * This is the main routine. It snatches the time from the shm 392290001Sglebius * board and tacks on a local timestamp. 393290001Sglebius */ 394290001Sglebius if (shm == NULL) { 395290001Sglebius shm_stat->status = NO_SEGMENT; 396290001Sglebius return NO_SEGMENT; 397290001Sglebius } 398290001Sglebius 399290001Sglebius /*@-type@*//* splint is confused about struct timespec */ 400290001Sglebius shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0; 401290001Sglebius { 402290001Sglebius time_t now; 403290001Sglebius 404290001Sglebius time(&now); 405290001Sglebius shm_stat->tvc.tv_sec = now; 406290001Sglebius } 407290001Sglebius 408290001Sglebius /* relying on word access to be atomic here */ 409290001Sglebius if (shm->valid == 0) { 410290001Sglebius shm_stat->status = NOT_READY; 411290001Sglebius return NOT_READY; 412290001Sglebius } 413290001Sglebius 414290001Sglebius cnt = shm->count; 415290001Sglebius 416290001Sglebius /* 417290001Sglebius * This is proof against concurrency issues if either 418290001Sglebius * (a) the memory_barrier() call works on this host, or 419290001Sglebius * (b) memset compiles to an uninterruptible single-instruction bitblt. 420290001Sglebius */ 421290001Sglebius memory_barrier(); 422293896Sglebius memcpy(&shmcopy, (void*)(uintptr_t)shm, sizeof(struct shmTime)); 423290001Sglebius shm->valid = 0; 424290001Sglebius memory_barrier(); 425290001Sglebius 426290001Sglebius /* 427290001Sglebius * Clash detection in case neither (a) nor (b) was true. 428290001Sglebius * Not supported in mode 0, and word access to the count field 429290001Sglebius * must be atomic for this to work. 430290001Sglebius */ 431290001Sglebius if (shmcopy.mode > 0 && cnt != shm->count) { 432290001Sglebius shm_stat->status = CLASH; 433290001Sglebius return shm_stat->status; 434290001Sglebius } 435290001Sglebius 436290001Sglebius shm_stat->status = OK; 437290001Sglebius shm_stat->mode = shmcopy.mode; 438290001Sglebius 439290001Sglebius switch (shmcopy.mode) { 440290001Sglebius case 0: 441290001Sglebius shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; 442290001Sglebius shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; 443290001Sglebius rns_new = shmcopy.receiveTimeStampNSec; 444290001Sglebius shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; 445290001Sglebius shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; 446290001Sglebius cns_new = shmcopy.clockTimeStampNSec; 447290001Sglebius 448290001Sglebius /* Since the following comparisons are between unsigned 449290001Sglebius ** variables they are always well defined, and any 450290001Sglebius ** (signed) underflow will turn into very large unsigned 451290001Sglebius ** values, well above the 1000 cutoff. 452290001Sglebius ** 453290001Sglebius ** Note: The usecs *must* be a *truncated* 454290001Sglebius ** representation of the nsecs. This code will fail for 455290001Sglebius ** *rounded* usecs, and the logic to deal with 456290001Sglebius ** wrap-arounds in the presence of rounded values is 457290001Sglebius ** much more convoluted. 458290001Sglebius */ 459290001Sglebius if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) 460290001Sglebius && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { 461290001Sglebius shm_stat->tvt.tv_nsec = cns_new; 462290001Sglebius shm_stat->tvr.tv_nsec = rns_new; 46354359Sroberto } 464290001Sglebius /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level 465290001Sglebius ** timestamps, possibly generated by extending the old 466290001Sglebius ** us-level timestamps 467290001Sglebius */ 468290001Sglebius break; 469182007Sroberto 470290001Sglebius case 1: 471290001Sglebius 472290001Sglebius shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; 473290001Sglebius shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; 474290001Sglebius rns_new = shmcopy.receiveTimeStampNSec; 475290001Sglebius shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; 476290001Sglebius shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; 477290001Sglebius cns_new = shmcopy.clockTimeStampNSec; 478290001Sglebius 479290001Sglebius /* See the case above for an explanation of the 480290001Sglebius ** following test. 481290001Sglebius */ 482290001Sglebius if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) 483290001Sglebius && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { 484290001Sglebius shm_stat->tvt.tv_nsec = cns_new; 485290001Sglebius shm_stat->tvr.tv_nsec = rns_new; 486290001Sglebius } 487290001Sglebius /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level 488290001Sglebius ** timestamps, possibly generated by extending the old 489290001Sglebius ** us-level timestamps 490290001Sglebius */ 491290001Sglebius break; 492290001Sglebius 493290001Sglebius default: 494290001Sglebius shm_stat->status = BAD_MODE; 495290001Sglebius break; 496290001Sglebius } 497290001Sglebius /*@-type@*/ 498290001Sglebius 499290001Sglebius /* 500290001Sglebius * leap field is not a leap offset but a leap notification code. 501290001Sglebius * The values are magic numbers used by NTP and set by GPSD, if at all, in 502290001Sglebius * the subframe code. 503290001Sglebius */ 504290001Sglebius shm_stat->leap = shmcopy.leap; 505290001Sglebius shm_stat->precision = shmcopy.precision; 506290001Sglebius 507290001Sglebius return shm_stat->status; 508290001Sglebius} 509290001Sglebius 510290001Sglebius/* 511290001Sglebius * shm_timer - called once every second. 512290001Sglebius * 513290001Sglebius * This tries to grab a sample from the SHM segment, filtering bad ones 514290001Sglebius */ 515290001Sglebiusstatic void 516290001Sglebiusshm_timer( 517290001Sglebius int unit, 518290001Sglebius struct peer *peer 519290001Sglebius ) 520290001Sglebius{ 521290001Sglebius struct refclockproc * const pp = peer->procptr; 522290001Sglebius struct shmunit * const up = pp->unitptr; 523290001Sglebius 524290001Sglebius volatile struct shmTime *shm; 525290001Sglebius 526290001Sglebius l_fp tsrcv; 527290001Sglebius l_fp tsref; 528290001Sglebius int c; 529290001Sglebius 530290001Sglebius /* for formatting 'a_lastcode': */ 531290001Sglebius struct calendar cd; 532290001Sglebius time_t tt; 533290001Sglebius vint64 ts; 534290001Sglebius 535290001Sglebius enum segstat_t status; 536290001Sglebius struct shm_stat_t shm_stat; 537290001Sglebius 538290001Sglebius up->ticks++; 539290001Sglebius if ((shm = up->shm) == NULL) { 540290001Sglebius /* try to map again - this may succeed if meanwhile some- 541290001Sglebius body has ipcrm'ed the old (unaccessible) shared mem segment */ 542290001Sglebius shm = up->shm = getShmTime(unit, up->forall); 543290001Sglebius if (shm == NULL) { 544290001Sglebius DPRINTF(1, ("%s: no SHM segment\n", 545290001Sglebius refnumtoa(&peer->srcadr))); 54654359Sroberto return; 54754359Sroberto } 54854359Sroberto } 549290001Sglebius 550290001Sglebius /* query the segment, atomically */ 551290001Sglebius status = shm_query(shm, &shm_stat); 552290001Sglebius 553290001Sglebius switch (status) { 554290001Sglebius case OK: 555290001Sglebius DPRINTF(2, ("%s: SHM type %d sample\n", 556290001Sglebius refnumtoa(&peer->srcadr), shm_stat.mode)); 557290001Sglebius break; 558290001Sglebius case NO_SEGMENT: 559290001Sglebius /* should never happen, but is harmless */ 560290001Sglebius return; 561290001Sglebius case NOT_READY: 562290001Sglebius DPRINTF(1, ("%s: SHM not ready\n",refnumtoa(&peer->srcadr))); 563290001Sglebius up->notready++; 564290001Sglebius return; 565290001Sglebius case BAD_MODE: 566290001Sglebius DPRINTF(1, ("%s: SHM type blooper, mode=%d\n", 567290001Sglebius refnumtoa(&peer->srcadr), shm->mode)); 568290001Sglebius up->bad++; 569290001Sglebius msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d", 570290001Sglebius shm->mode); 571290001Sglebius return; 572290001Sglebius case CLASH: 573290001Sglebius DPRINTF(1, ("%s: type 1 access clash\n", 574290001Sglebius refnumtoa(&peer->srcadr))); 575290001Sglebius msyslog (LOG_NOTICE, "SHM: access clash in shared memory"); 576290001Sglebius up->clash++; 577290001Sglebius return; 578290001Sglebius default: 579290001Sglebius DPRINTF(1, ("%s: internal error, unknown SHM fetch status\n", 580290001Sglebius refnumtoa(&peer->srcadr))); 581290001Sglebius msyslog (LOG_NOTICE, "internal error, unknown SHM fetch status"); 582290001Sglebius up->bad++; 583290001Sglebius return; 584290001Sglebius } 585290001Sglebius 586290001Sglebius 587290001Sglebius /* format the last time code in human-readable form into 588290001Sglebius * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible 589290001Sglebius * tv_sec". I can't find a base for this claim, but we can work 590290001Sglebius * around that potential problem. BTW, simply casting a pointer 591290001Sglebius * is a receipe for disaster on some architectures. 592290001Sglebius */ 593290001Sglebius tt = (time_t)shm_stat.tvt.tv_sec; 594290001Sglebius ts = time_to_vint64(&tt); 595290001Sglebius ntpcal_time_to_date(&cd, &ts); 596290001Sglebius 597290001Sglebius /* add ntpq -c cv timecode in ISO 8601 format */ 598290001Sglebius c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 599290001Sglebius "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ", 600290001Sglebius cd.year, cd.month, cd.monthday, 601290001Sglebius cd.hour, cd.minute, cd.second, 602290001Sglebius (long)shm_stat.tvt.tv_nsec); 603294905Sdelphij pp->lencode = (c > 0 && (size_t)c < sizeof(pp->a_lastcode)) ? c : 0; 604290001Sglebius 605290001Sglebius /* check 1: age control of local time stamp */ 606290001Sglebius tt = shm_stat.tvc.tv_sec - shm_stat.tvr.tv_sec; 607290001Sglebius if (tt < 0 || tt > up->max_delay) { 608290001Sglebius DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n", 609290001Sglebius refnumtoa(&peer->srcadr), (long long)tt)); 610290001Sglebius up->bad++; 611290001Sglebius msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds", 612290001Sglebius (long long)tt); 61354359Sroberto return; 61454359Sroberto } 615290001Sglebius 616290001Sglebius /* check 2: delta check */ 617290001Sglebius tt = shm_stat.tvr.tv_sec - shm_stat.tvt.tv_sec - (shm_stat.tvr.tv_nsec < shm_stat.tvt.tv_nsec); 618290001Sglebius if (tt < 0) 619290001Sglebius tt = -tt; 620290001Sglebius if (up->max_delta > 0 && tt > up->max_delta) { 621290001Sglebius DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n", 622290001Sglebius refnumtoa(&peer->srcadr), (long long)tt)); 623290001Sglebius up->bad++; 624290001Sglebius msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n", 625290001Sglebius (long long)tt); 62654359Sroberto return; 62754359Sroberto } 628290001Sglebius 629290001Sglebius /* if we really made it to this point... we're winners! */ 630290001Sglebius DPRINTF(2, ("%s: SHM feeding data\n", 631290001Sglebius refnumtoa(&peer->srcadr))); 632290001Sglebius tsrcv = tspec_stamp_to_lfp(shm_stat.tvr); 633290001Sglebius tsref = tspec_stamp_to_lfp(shm_stat.tvt); 634290001Sglebius pp->leap = shm_stat.leap; 635290001Sglebius peer->precision = shm_stat.precision; 636290001Sglebius refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1); 637290001Sglebius up->good++; 63854359Sroberto} 63954359Sroberto 640290001Sglebius/* 641290001Sglebius * shm_clockstats - dump and reset counters 642290001Sglebius */ 643290001Sglebiusstatic void shm_clockstats( 644290001Sglebius int unit, 645290001Sglebius struct peer *peer 646290001Sglebius ) 647290001Sglebius{ 648290001Sglebius struct refclockproc * const pp = peer->procptr; 649290001Sglebius struct shmunit * const up = pp->unitptr; 650290001Sglebius 651290001Sglebius UNUSED_ARG(unit); 652290001Sglebius if (pp->sloppyclockflag & CLK_FLAG4) { 653290001Sglebius mprintf_clock_stats( 654290001Sglebius &peer->srcadr, "%3d %3d %3d %3d %3d", 655290001Sglebius up->ticks, up->good, up->notready, 656290001Sglebius up->bad, up->clash); 657290001Sglebius } 658290001Sglebius up->ticks = up->good = up->notready = up->bad = up->clash = 0; 659290001Sglebius} 660290001Sglebius 66154359Sroberto#else 662290001SglebiusNONEMPTY_TRANSLATION_UNIT 66354359Sroberto#endif /* REFCLOCK */ 664