1 1.10 christos /* $NetBSD: refclock_shm.c,v 1.10 2020/05/25 20:47:26 christos Exp $ */ 2 1.1 kardel 3 1.1 kardel /* 4 1.2 christos * refclock_shm - clock driver for utc via shared memory 5 1.1 kardel * - under construction - 6 1.1 kardel * To add new modes: Extend or union the shmTime-struct. Do not 7 1.1 kardel * extend/shrink size, because otherwise existing implementations 8 1.1 kardel * will specify wrong size of shared memory-segment 9 1.1 kardel * PB 18.3.97 10 1.1 kardel */ 11 1.1 kardel 12 1.1 kardel #ifdef HAVE_CONFIG_H 13 1.1 kardel # include <config.h> 14 1.1 kardel #endif 15 1.1 kardel 16 1.2 christos #include "ntp_types.h" 17 1.2 christos 18 1.1 kardel #if defined(REFCLOCK) && defined(CLOCK_SHM) 19 1.1 kardel 20 1.1 kardel #include "ntpd.h" 21 1.2 christos #undef fileno 22 1.1 kardel #include "ntp_io.h" 23 1.2 christos #undef fileno 24 1.1 kardel #include "ntp_refclock.h" 25 1.2 christos #undef fileno 26 1.2 christos #include "timespecops.h" 27 1.2 christos #undef fileno 28 1.1 kardel #include "ntp_stdlib.h" 29 1.6 christos #include "ntp_assert.h" 30 1.1 kardel 31 1.2 christos #undef fileno 32 1.1 kardel #include <ctype.h> 33 1.2 christos #undef fileno 34 1.1 kardel 35 1.1 kardel #ifndef SYS_WINNT 36 1.1 kardel # include <sys/ipc.h> 37 1.1 kardel # include <sys/shm.h> 38 1.1 kardel # include <assert.h> 39 1.1 kardel # include <unistd.h> 40 1.1 kardel # include <stdio.h> 41 1.1 kardel #endif 42 1.1 kardel 43 1.6 christos #ifdef HAVE_STDATOMIC_H 44 1.6 christos # include <stdatomic.h> 45 1.6 christos #endif /* HAVE_STDATOMIC_H */ 46 1.6 christos 47 1.1 kardel /* 48 1.1 kardel * This driver supports a reference clock attached thru shared memory 49 1.2 christos */ 50 1.1 kardel 51 1.1 kardel /* 52 1.1 kardel * SHM interface definitions 53 1.1 kardel */ 54 1.1 kardel #define PRECISION (-1) /* precision assumed (0.5 s) */ 55 1.1 kardel #define REFID "SHM" /* reference ID */ 56 1.1 kardel #define DESCRIPTION "SHM/Shared memory interface" 57 1.1 kardel 58 1.1 kardel #define NSAMPLES 3 /* stages of median filter */ 59 1.1 kardel 60 1.1 kardel /* 61 1.4 christos * Mode flags 62 1.4 christos */ 63 1.4 christos #define SHM_MODE_PRIVATE 0x0001 64 1.4 christos 65 1.4 christos /* 66 1.1 kardel * Function prototypes 67 1.1 kardel */ 68 1.1 kardel static int shm_start (int unit, struct peer *peer); 69 1.1 kardel static void shm_shutdown (int unit, struct peer *peer); 70 1.1 kardel static void shm_poll (int unit, struct peer *peer); 71 1.1 kardel static void shm_timer (int unit, struct peer *peer); 72 1.3 christos static void shm_clockstats (int unit, struct peer *peer); 73 1.3 christos static void shm_control (int unit, const struct refclockstat * in_st, 74 1.3 christos struct refclockstat * out_st, struct peer *peer); 75 1.1 kardel 76 1.1 kardel /* 77 1.1 kardel * Transfer vector 78 1.1 kardel */ 79 1.1 kardel struct refclock refclock_shm = { 80 1.1 kardel shm_start, /* start up driver */ 81 1.1 kardel shm_shutdown, /* shut down driver */ 82 1.1 kardel shm_poll, /* transmit poll message */ 83 1.3 christos shm_control, /* control settings */ 84 1.1 kardel noentry, /* not used: init */ 85 1.1 kardel noentry, /* not used: buginfo */ 86 1.1 kardel shm_timer, /* once per second */ 87 1.1 kardel }; 88 1.1 kardel 89 1.1 kardel struct shmTime { 90 1.2 christos int mode; /* 0 - if valid is set: 91 1.2 christos * use values, 92 1.1 kardel * clear valid 93 1.2 christos * 1 - if valid is set: 94 1.1 kardel * if count before and after read of values is equal, 95 1.2 christos * use values 96 1.1 kardel * clear valid 97 1.1 kardel */ 98 1.2 christos volatile int count; 99 1.2 christos time_t clockTimeStampSec; 100 1.2 christos int clockTimeStampUSec; 101 1.2 christos time_t receiveTimeStampSec; 102 1.2 christos int receiveTimeStampUSec; 103 1.2 christos int leap; 104 1.2 christos int precision; 105 1.2 christos int nsamples; 106 1.2 christos volatile int valid; 107 1.2 christos unsigned clockTimeStampNSec; /* Unsigned ns timestamps */ 108 1.2 christos unsigned receiveTimeStampNSec; /* Unsigned ns timestamps */ 109 1.2 christos int dummy[8]; 110 1.1 kardel }; 111 1.1 kardel 112 1.1 kardel struct shmunit { 113 1.1 kardel struct shmTime *shm; /* pointer to shared memory segment */ 114 1.4 christos int forall; /* access for all UIDs? */ 115 1.1 kardel 116 1.1 kardel /* debugging/monitoring counters - reset when printed */ 117 1.1 kardel int ticks; /* number of attempts to read data*/ 118 1.1 kardel int good; /* number of valid samples */ 119 1.1 kardel int notready; /* number of peeks without data ready */ 120 1.1 kardel int bad; /* number of invalid samples */ 121 1.1 kardel int clash; /* number of access clashes while reading */ 122 1.3 christos 123 1.3 christos time_t max_delta; /* difference limit */ 124 1.3 christos time_t max_delay; /* age/stale limit */ 125 1.1 kardel }; 126 1.1 kardel 127 1.6 christos 128 1.4 christos static struct shmTime* 129 1.4 christos getShmTime( 130 1.4 christos int unit, 131 1.4 christos int/*BOOL*/ forall 132 1.4 christos ) 133 1.4 christos { 134 1.4 christos struct shmTime *p = NULL; 135 1.1 kardel 136 1.4 christos #ifndef SYS_WINNT 137 1.1 kardel 138 1.4 christos int shmid; 139 1.1 kardel 140 1.1 kardel /* 0x4e545030 is NTP0. 141 1.1 kardel * Big units will give non-ascii but that's OK 142 1.2 christos * as long as everybody does it the same way. 143 1.1 kardel */ 144 1.4 christos shmid=shmget(0x4e545030 + unit, sizeof (struct shmTime), 145 1.4 christos IPC_CREAT | (forall ? 0666 : 0600)); 146 1.2 christos if (shmid == -1) { /* error */ 147 1.2 christos msyslog(LOG_ERR, "SHM shmget (unit %d): %m", unit); 148 1.4 christos return NULL; 149 1.1 kardel } 150 1.4 christos p = (struct shmTime *)shmat (shmid, 0, 0); 151 1.4 christos if (p == (struct shmTime *)-1) { /* error */ 152 1.4 christos msyslog(LOG_ERR, "SHM shmat (unit %d): %m", unit); 153 1.4 christos return NULL; 154 1.1 kardel } 155 1.6 christos 156 1.4 christos return p; 157 1.1 kardel #else 158 1.4 christos 159 1.4 christos static const char * nspref[2] = { "Local", "Global" }; 160 1.4 christos char buf[20]; 161 1.2 christos LPSECURITY_ATTRIBUTES psec = 0; 162 1.2 christos HANDLE shmid = 0; 163 1.1 kardel SECURITY_DESCRIPTOR sd; 164 1.1 kardel SECURITY_ATTRIBUTES sa; 165 1.4 christos unsigned int numch; 166 1.2 christos 167 1.4 christos numch = snprintf(buf, sizeof(buf), "%s\\NTP%d", 168 1.4 christos nspref[forall != 0], (unit & 0xFF)); 169 1.4 christos if (numch >= sizeof(buf)) { 170 1.4 christos msyslog(LOG_ERR, "SHM name too long (unit %d)", unit); 171 1.4 christos return NULL; 172 1.4 christos } 173 1.4 christos if (forall) { /* world access */ 174 1.1 kardel if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { 175 1.2 christos msyslog(LOG_ERR,"SHM InitializeSecurityDescriptor (unit %d): %m", unit); 176 1.4 christos return NULL; 177 1.1 kardel } 178 1.4 christos if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)) { 179 1.2 christos msyslog(LOG_ERR, "SHM SetSecurityDescriptorDacl (unit %d): %m", unit); 180 1.4 christos return NULL; 181 1.1 kardel } 182 1.4 christos sa.nLength = sizeof(SECURITY_ATTRIBUTES); 183 1.2 christos sa.lpSecurityDescriptor = &sd; 184 1.4 christos sa.bInheritHandle = FALSE; 185 1.2 christos psec = &sa; 186 1.1 kardel } 187 1.2 christos shmid = CreateFileMapping ((HANDLE)0xffffffff, psec, PAGE_READWRITE, 188 1.4 christos 0, sizeof (struct shmTime), buf); 189 1.4 christos if (shmid == NULL) { /*error*/ 190 1.4 christos char buf[1000]; 191 1.1 kardel FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 192 1.1 kardel 0, GetLastError (), 0, buf, sizeof (buf), 0); 193 1.2 christos msyslog(LOG_ERR, "SHM CreateFileMapping (unit %d): %s", unit, buf); 194 1.4 christos return NULL; 195 1.4 christos } 196 1.4 christos p = (struct shmTime *)MapViewOfFile(shmid, FILE_MAP_WRITE, 0, 0, 197 1.4 christos sizeof (struct shmTime)); 198 1.4 christos if (p == NULL) { /*error*/ 199 1.4 christos char buf[1000]; 200 1.4 christos FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 201 1.4 christos 0, GetLastError (), 0, buf, sizeof (buf), 0); 202 1.4 christos msyslog(LOG_ERR,"SHM MapViewOfFile (unit %d): %s", unit, buf); 203 1.4 christos return NULL; 204 1.1 kardel } 205 1.4 christos 206 1.5 christos return p; 207 1.1 kardel #endif 208 1.6 christos 209 1.6 christos /* NOTREACHED */ 210 1.6 christos ENSURE(!"getShmTime(): Not reached."); 211 1.1 kardel } 212 1.6 christos 213 1.6 christos 214 1.1 kardel /* 215 1.1 kardel * shm_start - attach to shared memory 216 1.1 kardel */ 217 1.1 kardel static int 218 1.1 kardel shm_start( 219 1.1 kardel int unit, 220 1.1 kardel struct peer *peer 221 1.1 kardel ) 222 1.1 kardel { 223 1.4 christos struct refclockproc * const pp = peer->procptr; 224 1.4 christos struct shmunit * const up = emalloc_zero(sizeof(*up)); 225 1.1 kardel 226 1.1 kardel pp->io.clock_recv = noentry; 227 1.2 christos pp->io.srcclock = peer; 228 1.1 kardel pp->io.datalen = 0; 229 1.1 kardel pp->io.fd = -1; 230 1.1 kardel 231 1.4 christos up->forall = (unit >= 2) && !(peer->ttl & SHM_MODE_PRIVATE); 232 1.1 kardel 233 1.4 christos up->shm = getShmTime(unit, up->forall); 234 1.1 kardel 235 1.1 kardel /* 236 1.1 kardel * Initialize miscellaneous peer variables 237 1.1 kardel */ 238 1.1 kardel memcpy((char *)&pp->refid, REFID, 4); 239 1.1 kardel if (up->shm != 0) { 240 1.2 christos pp->unitptr = up; 241 1.1 kardel up->shm->precision = PRECISION; 242 1.1 kardel peer->precision = up->shm->precision; 243 1.2 christos up->shm->valid = 0; 244 1.2 christos up->shm->nsamples = NSAMPLES; 245 1.1 kardel pp->clockdesc = DESCRIPTION; 246 1.3 christos /* items to be changed later in 'shm_control()': */ 247 1.3 christos up->max_delay = 5; 248 1.3 christos up->max_delta = 4*3600; 249 1.2 christos return 1; 250 1.2 christos } else { 251 1.2 christos free(up); 252 1.3 christos pp->unitptr = NULL; 253 1.1 kardel return 0; 254 1.1 kardel } 255 1.1 kardel } 256 1.1 kardel 257 1.1 kardel 258 1.1 kardel /* 259 1.3 christos * shm_control - configure flag1/time2 params 260 1.3 christos * 261 1.3 christos * These are not yet available during 'shm_start', so we have to do any 262 1.3 christos * pre-computations we want to avoid during regular poll/timer callbacks 263 1.3 christos * in this callback. 264 1.3 christos */ 265 1.3 christos static void 266 1.3 christos shm_control( 267 1.3 christos int unit, 268 1.3 christos const struct refclockstat * in_st, 269 1.3 christos struct refclockstat * out_st, 270 1.3 christos struct peer * peer 271 1.3 christos ) 272 1.3 christos { 273 1.4 christos struct refclockproc * const pp = peer->procptr; 274 1.4 christos struct shmunit * const up = pp->unitptr; 275 1.3 christos 276 1.4 christos UNUSED_ARG(unit); 277 1.4 christos UNUSED_ARG(in_st); 278 1.4 christos UNUSED_ARG(out_st); 279 1.3 christos if (NULL == up) 280 1.3 christos return; 281 1.3 christos if (pp->sloppyclockflag & CLK_FLAG1) 282 1.3 christos up->max_delta = 0; 283 1.3 christos else if (pp->fudgetime2 < 1. || pp->fudgetime2 > 86400.) 284 1.3 christos up->max_delta = 4*3600; 285 1.3 christos else 286 1.3 christos up->max_delta = (time_t)floor(pp->fudgetime2 + 0.5); 287 1.3 christos } 288 1.3 christos 289 1.3 christos 290 1.3 christos /* 291 1.1 kardel * shm_shutdown - shut down the clock 292 1.1 kardel */ 293 1.1 kardel static void 294 1.1 kardel shm_shutdown( 295 1.1 kardel int unit, 296 1.1 kardel struct peer *peer 297 1.1 kardel ) 298 1.1 kardel { 299 1.4 christos struct refclockproc * const pp = peer->procptr; 300 1.4 christos struct shmunit * const up = pp->unitptr; 301 1.2 christos 302 1.4 christos UNUSED_ARG(unit); 303 1.2 christos if (NULL == up) 304 1.2 christos return; 305 1.1 kardel #ifndef SYS_WINNT 306 1.4 christos 307 1.2 christos /* HMS: shmdt() wants char* or const void * */ 308 1.4 christos (void)shmdt((char *)up->shm); 309 1.4 christos 310 1.1 kardel #else 311 1.4 christos 312 1.4 christos UnmapViewOfFile(up->shm); 313 1.4 christos 314 1.1 kardel #endif 315 1.2 christos free(up); 316 1.1 kardel } 317 1.1 kardel 318 1.1 kardel 319 1.1 kardel /* 320 1.1 kardel * shm_poll - called by the transmit procedure 321 1.1 kardel */ 322 1.1 kardel static void 323 1.1 kardel shm_poll( 324 1.1 kardel int unit, 325 1.1 kardel struct peer *peer 326 1.1 kardel ) 327 1.1 kardel { 328 1.4 christos struct refclockproc * const pp = peer->procptr; 329 1.4 christos struct shmunit * const up = pp->unitptr; 330 1.3 christos int major_error; 331 1.1 kardel 332 1.3 christos pp->polls++; 333 1.2 christos 334 1.3 christos /* get dominant reason if we have no samples at all */ 335 1.3 christos major_error = max(up->notready, up->bad); 336 1.3 christos major_error = max(major_error, up->clash); 337 1.1 kardel 338 1.1 kardel /* 339 1.3 christos * Process median filter samples. If none received, see what 340 1.3 christos * happened, tell the core and keep going. 341 1.1 kardel */ 342 1.3 christos if (pp->coderecv != pp->codeproc) { 343 1.3 christos /* have some samples, everything OK */ 344 1.3 christos pp->lastref = pp->lastrec; 345 1.9 christos refclock_report(peer, CEVNT_NOMINAL); 346 1.3 christos refclock_receive(peer); 347 1.3 christos } else if (NULL == up->shm) { /* is this possible at all? */ 348 1.3 christos /* we're out of business without SHM access */ 349 1.3 christos refclock_report(peer, CEVNT_FAULT); 350 1.3 christos } else if (major_error == up->clash) { 351 1.3 christos /* too many collisions is like a bad signal */ 352 1.3 christos refclock_report(peer, CEVNT_PROP); 353 1.3 christos } else if (major_error == up->bad) { 354 1.3 christos /* too much stale/bad/garbled data */ 355 1.3 christos refclock_report(peer, CEVNT_BADREPLY); 356 1.3 christos } else { 357 1.3 christos /* in any other case assume it's just a timeout */ 358 1.1 kardel refclock_report(peer, CEVNT_TIMEOUT); 359 1.1 kardel } 360 1.3 christos /* shm_clockstats() clears the tallies, so it must be last... */ 361 1.1 kardel shm_clockstats(unit, peer); 362 1.1 kardel } 363 1.1 kardel 364 1.6 christos 365 1.6 christos enum segstat_t { 366 1.6 christos OK, NO_SEGMENT, NOT_READY, BAD_MODE, CLASH 367 1.6 christos }; 368 1.6 christos 369 1.6 christos struct shm_stat_t { 370 1.6 christos int status; 371 1.6 christos int mode; 372 1.6 christos struct timespec tvc, tvr, tvt; 373 1.6 christos int precision; 374 1.6 christos int leap; 375 1.6 christos }; 376 1.6 christos 377 1.6 christos static inline void memory_barrier(void) 378 1.6 christos { 379 1.6 christos #ifdef HAVE_ATOMIC_THREAD_FENCE 380 1.6 christos atomic_thread_fence(memory_order_seq_cst); 381 1.6 christos #endif /* HAVE_ATOMIC_THREAD_FENCE */ 382 1.6 christos } 383 1.6 christos 384 1.6 christos static enum segstat_t shm_query(volatile struct shmTime *shm_in, struct shm_stat_t *shm_stat) 385 1.6 christos /* try to grab a sample from the specified SHM segment */ 386 1.6 christos { 387 1.7 christos struct shmTime shmcopy; 388 1.7 christos volatile struct shmTime *shm = shm_in; 389 1.6 christos volatile int cnt; 390 1.6 christos 391 1.6 christos unsigned int cns_new, rns_new; 392 1.6 christos 393 1.6 christos /* 394 1.6 christos * This is the main routine. It snatches the time from the shm 395 1.6 christos * board and tacks on a local timestamp. 396 1.6 christos */ 397 1.6 christos if (shm == NULL) { 398 1.6 christos shm_stat->status = NO_SEGMENT; 399 1.6 christos return NO_SEGMENT; 400 1.6 christos } 401 1.6 christos 402 1.6 christos /*@-type@*//* splint is confused about struct timespec */ 403 1.6 christos shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0; 404 1.6 christos { 405 1.6 christos time_t now; 406 1.6 christos 407 1.6 christos time(&now); 408 1.6 christos shm_stat->tvc.tv_sec = now; 409 1.6 christos } 410 1.6 christos 411 1.6 christos /* relying on word access to be atomic here */ 412 1.6 christos if (shm->valid == 0) { 413 1.6 christos shm_stat->status = NOT_READY; 414 1.6 christos return NOT_READY; 415 1.6 christos } 416 1.6 christos 417 1.6 christos cnt = shm->count; 418 1.6 christos 419 1.6 christos /* 420 1.6 christos * This is proof against concurrency issues if either 421 1.6 christos * (a) the memory_barrier() call works on this host, or 422 1.6 christos * (b) memset compiles to an uninterruptible single-instruction bitblt. 423 1.6 christos */ 424 1.6 christos memory_barrier(); 425 1.7 christos memcpy(&shmcopy, (void*)(uintptr_t)shm, sizeof(struct shmTime)); 426 1.6 christos shm->valid = 0; 427 1.6 christos memory_barrier(); 428 1.6 christos 429 1.6 christos /* 430 1.6 christos * Clash detection in case neither (a) nor (b) was true. 431 1.6 christos * Not supported in mode 0, and word access to the count field 432 1.6 christos * must be atomic for this to work. 433 1.6 christos */ 434 1.6 christos if (shmcopy.mode > 0 && cnt != shm->count) { 435 1.6 christos shm_stat->status = CLASH; 436 1.6 christos return shm_stat->status; 437 1.6 christos } 438 1.6 christos 439 1.6 christos shm_stat->status = OK; 440 1.6 christos shm_stat->mode = shmcopy.mode; 441 1.6 christos 442 1.6 christos switch (shmcopy.mode) { 443 1.6 christos case 0: 444 1.6 christos shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; 445 1.6 christos shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; 446 1.6 christos rns_new = shmcopy.receiveTimeStampNSec; 447 1.6 christos shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; 448 1.6 christos shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; 449 1.6 christos cns_new = shmcopy.clockTimeStampNSec; 450 1.6 christos 451 1.6 christos /* Since the following comparisons are between unsigned 452 1.6 christos ** variables they are always well defined, and any 453 1.6 christos ** (signed) underflow will turn into very large unsigned 454 1.6 christos ** values, well above the 1000 cutoff. 455 1.6 christos ** 456 1.6 christos ** Note: The usecs *must* be a *truncated* 457 1.6 christos ** representation of the nsecs. This code will fail for 458 1.6 christos ** *rounded* usecs, and the logic to deal with 459 1.6 christos ** wrap-arounds in the presence of rounded values is 460 1.6 christos ** much more convoluted. 461 1.6 christos */ 462 1.6 christos if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) 463 1.6 christos && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { 464 1.6 christos shm_stat->tvt.tv_nsec = cns_new; 465 1.6 christos shm_stat->tvr.tv_nsec = rns_new; 466 1.6 christos } 467 1.6 christos /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level 468 1.6 christos ** timestamps, possibly generated by extending the old 469 1.6 christos ** us-level timestamps 470 1.6 christos */ 471 1.6 christos break; 472 1.6 christos 473 1.6 christos case 1: 474 1.6 christos 475 1.6 christos shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec; 476 1.6 christos shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000; 477 1.6 christos rns_new = shmcopy.receiveTimeStampNSec; 478 1.6 christos shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec; 479 1.6 christos shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000; 480 1.6 christos cns_new = shmcopy.clockTimeStampNSec; 481 1.6 christos 482 1.6 christos /* See the case above for an explanation of the 483 1.6 christos ** following test. 484 1.6 christos */ 485 1.6 christos if ( ((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000) 486 1.6 christos && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) { 487 1.6 christos shm_stat->tvt.tv_nsec = cns_new; 488 1.6 christos shm_stat->tvr.tv_nsec = rns_new; 489 1.6 christos } 490 1.6 christos /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level 491 1.6 christos ** timestamps, possibly generated by extending the old 492 1.6 christos ** us-level timestamps 493 1.6 christos */ 494 1.6 christos break; 495 1.6 christos 496 1.6 christos default: 497 1.6 christos shm_stat->status = BAD_MODE; 498 1.6 christos break; 499 1.6 christos } 500 1.6 christos /*@-type@*/ 501 1.6 christos 502 1.6 christos /* 503 1.6 christos * leap field is not a leap offset but a leap notification code. 504 1.6 christos * The values are magic numbers used by NTP and set by GPSD, if at all, in 505 1.6 christos * the subframe code. 506 1.6 christos */ 507 1.6 christos shm_stat->leap = shmcopy.leap; 508 1.6 christos shm_stat->precision = shmcopy.precision; 509 1.6 christos 510 1.6 christos return shm_stat->status; 511 1.6 christos } 512 1.6 christos 513 1.1 kardel /* 514 1.6 christos * shm_timer - called once every second. 515 1.4 christos * 516 1.6 christos * This tries to grab a sample from the SHM segment, filtering bad ones 517 1.1 kardel */ 518 1.3 christos static void 519 1.4 christos shm_timer( 520 1.1 kardel int unit, 521 1.1 kardel struct peer *peer 522 1.1 kardel ) 523 1.1 kardel { 524 1.4 christos struct refclockproc * const pp = peer->procptr; 525 1.4 christos struct shmunit * const up = pp->unitptr; 526 1.3 christos 527 1.3 christos volatile struct shmTime *shm; 528 1.3 christos 529 1.3 christos l_fp tsrcv; 530 1.3 christos l_fp tsref; 531 1.6 christos int c; 532 1.3 christos 533 1.3 christos /* for formatting 'a_lastcode': */ 534 1.3 christos struct calendar cd; 535 1.6 christos time_t tt; 536 1.3 christos vint64 ts; 537 1.1 kardel 538 1.6 christos enum segstat_t status; 539 1.6 christos struct shm_stat_t shm_stat; 540 1.6 christos 541 1.1 kardel up->ticks++; 542 1.4 christos if ((shm = up->shm) == NULL) { 543 1.1 kardel /* try to map again - this may succeed if meanwhile some- 544 1.1 kardel body has ipcrm'ed the old (unaccessible) shared mem segment */ 545 1.4 christos shm = up->shm = getShmTime(unit, up->forall); 546 1.4 christos if (shm == NULL) { 547 1.4 christos DPRINTF(1, ("%s: no SHM segment\n", 548 1.4 christos refnumtoa(&peer->srcadr))); 549 1.4 christos return; 550 1.4 christos } 551 1.3 christos } 552 1.2 christos 553 1.6 christos /* query the segment, atomically */ 554 1.6 christos status = shm_query(shm, &shm_stat); 555 1.3 christos 556 1.6 christos switch (status) { 557 1.6 christos case OK: 558 1.6 christos DPRINTF(2, ("%s: SHM type %d sample\n", 559 1.6 christos refnumtoa(&peer->srcadr), shm_stat.mode)); 560 1.6 christos break; 561 1.6 christos case NO_SEGMENT: 562 1.6 christos /* should never happen, but is harmless */ 563 1.6 christos return; 564 1.6 christos case NOT_READY: 565 1.6 christos DPRINTF(1, ("%s: SHM not ready\n",refnumtoa(&peer->srcadr))); 566 1.6 christos up->notready++; 567 1.6 christos return; 568 1.6 christos case BAD_MODE: 569 1.6 christos DPRINTF(1, ("%s: SHM type blooper, mode=%d\n", 570 1.6 christos refnumtoa(&peer->srcadr), shm->mode)); 571 1.6 christos up->bad++; 572 1.6 christos msyslog (LOG_ERR, "SHM: bad mode found in shared memory: %d", 573 1.6 christos shm->mode); 574 1.6 christos return; 575 1.6 christos case CLASH: 576 1.6 christos DPRINTF(1, ("%s: type 1 access clash\n", 577 1.6 christos refnumtoa(&peer->srcadr))); 578 1.6 christos msyslog (LOG_NOTICE, "SHM: access clash in shared memory"); 579 1.6 christos up->clash++; 580 1.6 christos return; 581 1.3 christos default: 582 1.6 christos DPRINTF(1, ("%s: internal error, unknown SHM fetch status\n", 583 1.6 christos refnumtoa(&peer->srcadr))); 584 1.6 christos msyslog (LOG_NOTICE, "internal error, unknown SHM fetch status"); 585 1.6 christos up->bad++; 586 1.6 christos return; 587 1.3 christos } 588 1.6 christos 589 1.3 christos 590 1.3 christos /* format the last time code in human-readable form into 591 1.3 christos * 'pp->a_lastcode'. Someone claimed: "NetBSD has incompatible 592 1.3 christos * tv_sec". I can't find a base for this claim, but we can work 593 1.3 christos * around that potential problem. BTW, simply casting a pointer 594 1.3 christos * is a receipe for disaster on some architectures. 595 1.3 christos */ 596 1.6 christos tt = (time_t)shm_stat.tvt.tv_sec; 597 1.3 christos ts = time_to_vint64(&tt); 598 1.3 christos ntpcal_time_to_date(&cd, &ts); 599 1.3 christos 600 1.3 christos /* add ntpq -c cv timecode in ISO 8601 format */ 601 1.3 christos c = snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 602 1.3 christos "%04u-%02u-%02uT%02u:%02u:%02u.%09ldZ", 603 1.3 christos cd.year, cd.month, cd.monthday, 604 1.3 christos cd.hour, cd.minute, cd.second, 605 1.6 christos (long)shm_stat.tvt.tv_nsec); 606 1.8 christos pp->lencode = (c > 0 && (size_t)c < sizeof(pp->a_lastcode)) ? c : 0; 607 1.3 christos 608 1.3 christos /* check 1: age control of local time stamp */ 609 1.6 christos tt = shm_stat.tvc.tv_sec - shm_stat.tvr.tv_sec; 610 1.3 christos if (tt < 0 || tt > up->max_delay) { 611 1.3 christos DPRINTF(1, ("%s:SHM stale/bad receive time, delay=%llds\n", 612 1.3 christos refnumtoa(&peer->srcadr), (long long)tt)); 613 1.3 christos up->bad++; 614 1.3 christos msyslog (LOG_ERR, "SHM: stale/bad receive time, delay=%llds", 615 1.3 christos (long long)tt); 616 1.3 christos return; 617 1.1 kardel } 618 1.3 christos 619 1.3 christos /* check 2: delta check */ 620 1.6 christos tt = shm_stat.tvr.tv_sec - shm_stat.tvt.tv_sec - (shm_stat.tvr.tv_nsec < shm_stat.tvt.tv_nsec); 621 1.3 christos if (tt < 0) 622 1.3 christos tt = -tt; 623 1.3 christos if (up->max_delta > 0 && tt > up->max_delta) { 624 1.3 christos DPRINTF(1, ("%s: SHM diff limit exceeded, delta=%llds\n", 625 1.3 christos refnumtoa(&peer->srcadr), (long long)tt)); 626 1.1 kardel up->bad++; 627 1.3 christos msyslog (LOG_ERR, "SHM: difference limit exceeded, delta=%llds\n", 628 1.3 christos (long long)tt); 629 1.3 christos return; 630 1.1 kardel } 631 1.3 christos 632 1.3 christos /* if we really made it to this point... we're winners! */ 633 1.3 christos DPRINTF(2, ("%s: SHM feeding data\n", 634 1.3 christos refnumtoa(&peer->srcadr))); 635 1.6 christos tsrcv = tspec_stamp_to_lfp(shm_stat.tvr); 636 1.6 christos tsref = tspec_stamp_to_lfp(shm_stat.tvt); 637 1.6 christos pp->leap = shm_stat.leap; 638 1.6 christos peer->precision = shm_stat.precision; 639 1.3 christos refclock_process_offset(pp, tsref, tsrcv, pp->fudgetime1); 640 1.1 kardel up->good++; 641 1.1 kardel } 642 1.1 kardel 643 1.1 kardel /* 644 1.1 kardel * shm_clockstats - dump and reset counters 645 1.1 kardel */ 646 1.3 christos static void shm_clockstats( 647 1.1 kardel int unit, 648 1.1 kardel struct peer *peer 649 1.1 kardel ) 650 1.1 kardel { 651 1.4 christos struct refclockproc * const pp = peer->procptr; 652 1.4 christos struct shmunit * const up = pp->unitptr; 653 1.1 kardel 654 1.4 christos UNUSED_ARG(unit); 655 1.3 christos if (pp->sloppyclockflag & CLK_FLAG4) { 656 1.4 christos mprintf_clock_stats( 657 1.4 christos &peer->srcadr, "%3d %3d %3d %3d %3d", 658 1.4 christos up->ticks, up->good, up->notready, 659 1.4 christos up->bad, up->clash); 660 1.3 christos } 661 1.2 christos up->ticks = up->good = up->notready = up->bad = up->clash = 0; 662 1.1 kardel } 663 1.1 kardel 664 1.1 kardel #else 665 1.2 christos NONEMPTY_TRANSLATION_UNIT 666 1.1 kardel #endif /* REFCLOCK */ 667