ntpshmread.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /* ntpshmread.c -- monitor the inner end of an ntpshmwrite.connection
  2. *
  3. * This file is Copyright 2010 by the GPSD project
  4. * SPDX-License-Identifier: BSD-2-clause
  5. *
  6. * Some of this was swiped from the NTPD distribution.
  7. */
  8. #include "gpsd_config.h" /* must be before all includes */
  9. #include <assert.h>
  10. #include <errno.h>
  11. #include <math.h>
  12. #include <stdbool.h>
  13. #include <stdint.h>
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <sys/stat.h>
  17. #include <sys/types.h>
  18. #include <unistd.h>
  19. #include "ntpshm.h"
  20. #include "compiler.h"
  21. /* initialize a SHM segment */
  22. struct shmTime *shm_get(const int unit, const bool create, const bool forall)
  23. {
  24. struct shmTime *p = NULL;
  25. int shmid;
  26. /*
  27. * Big units will give non-ASCII but that's OK
  28. * as long as everybody does it the same way.
  29. */
  30. shmid = shmget((key_t)(NTPD_BASE + unit), sizeof(struct shmTime),
  31. (create ? IPC_CREAT : 0) | (forall ? 0666 : 0600));
  32. if (shmid == -1) { /* error */
  33. return NULL;
  34. }
  35. p = (struct shmTime *)shmat (shmid, 0, 0);
  36. if (p == (struct shmTime *)-1) { /* error */
  37. return NULL;
  38. }
  39. return p;
  40. }
  41. char *ntp_name(const int unit)
  42. /* return the name of a specified segment */
  43. {
  44. static char name[5] = "NTP\0";
  45. name[3] = (char)('0' + unit);
  46. return name;
  47. }
  48. /* try to grab a sample from the specified SHM segment */
  49. enum segstat_t ntp_read(struct shmTime *shm_in, struct shm_stat_t *shm_stat,
  50. const bool consume)
  51. {
  52. volatile struct shmTime shmcopy, *shm = shm_in;
  53. volatile int cnt;
  54. unsigned int cns_new, rns_new;
  55. if (shm == NULL) {
  56. shm_stat->status = NO_SEGMENT;
  57. return NO_SEGMENT;
  58. }
  59. shm_stat->tvc.tv_sec = shm_stat->tvc.tv_nsec = 0;
  60. /* relying on word access to be atomic here */
  61. if (shm->valid == 0) {
  62. shm_stat->status = NOT_READY;
  63. return NOT_READY;
  64. }
  65. cnt = shm->count;
  66. /*
  67. * This is proof against concurrency issues if either (a) the
  68. * memory_barrier() call works on this host, or (b) memset
  69. * compiles to an uninterruptible single-instruction bitblt (this
  70. * will probably cease to be true if the structure exceeds your VM
  71. * page size).
  72. */
  73. memory_barrier();
  74. memcpy((void *)&shmcopy, (void *)shm, sizeof(struct shmTime));
  75. /*
  76. * An update consumer such as ntpd should zero the valid flag at this point.
  77. * A program snooping the updates to collect statistics should not, lest
  78. * it make the data unavailable for consumers.
  79. */
  80. if (consume)
  81. shm->valid = 0;
  82. memory_barrier();
  83. /*
  84. * Clash detection in case neither (a) nor (b) was true.
  85. * Not supported in mode 0, and word access to the count field
  86. * must be atomic for this to work.
  87. */
  88. if (shmcopy.mode > 0 && cnt != shm->count) {
  89. shm_stat->status = CLASH;
  90. return shm_stat->status;
  91. }
  92. shm_stat->status = OK;
  93. switch (shmcopy.mode) {
  94. case 0:
  95. shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec;
  96. shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000;
  97. rns_new = shmcopy.receiveTimeStampNSec;
  98. shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec;
  99. shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000;
  100. cns_new = shmcopy.clockTimeStampNSec;
  101. /* Since the following comparisons are between unsigned
  102. ** variables they are always well defined, and any
  103. ** (signed) underflow will turn into very large unsigned
  104. ** values, well above the 1000 cutoff.
  105. **
  106. ** Note: The usecs *must* be a *truncated*
  107. ** representation of the nsecs. This code will fail for
  108. ** *rounded* usecs, and the logic to deal with
  109. ** wrap-arounds in the presence of rounded values is
  110. ** much more convoluted.
  111. */
  112. if (((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
  113. && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
  114. shm_stat->tvt.tv_nsec = cns_new;
  115. shm_stat->tvr.tv_nsec = rns_new;
  116. }
  117. /* At this point shm_stat->tvr and shm_stat->tvt contain valid ns-level
  118. ** timestamps, possibly generated by extending the old
  119. ** us-level timestamps
  120. */
  121. break;
  122. case 1:
  123. shm_stat->tvr.tv_sec = shmcopy.receiveTimeStampSec;
  124. shm_stat->tvr.tv_nsec = shmcopy.receiveTimeStampUSec * 1000;
  125. rns_new = shmcopy.receiveTimeStampNSec;
  126. shm_stat->tvt.tv_sec = shmcopy.clockTimeStampSec;
  127. shm_stat->tvt.tv_nsec = shmcopy.clockTimeStampUSec * 1000;
  128. cns_new = shmcopy.clockTimeStampNSec;
  129. /* See the case above for an explanation of the
  130. ** following test.
  131. */
  132. if (((cns_new - (unsigned)shm_stat->tvt.tv_nsec) < 1000)
  133. && ((rns_new - (unsigned)shm_stat->tvr.tv_nsec) < 1000)) {
  134. shm_stat->tvt.tv_nsec = cns_new;
  135. shm_stat->tvr.tv_nsec = rns_new;
  136. }
  137. /* At this point shm_stat->tvr and shm_stat->tvt contains valid ns-level
  138. ** timestamps, possibly generated by extending the old
  139. ** us-level timestamps
  140. */
  141. break;
  142. default:
  143. shm_stat->status = BAD_MODE;
  144. break;
  145. }
  146. /*
  147. * leap field is not a leap offset but a leap notification code.
  148. * The values are magic numbers used by NTP and set by GPSD, if at all, in
  149. * the subframe code.
  150. */
  151. shm_stat->leap = shmcopy.leap;
  152. shm_stat->precision = shmcopy.precision;
  153. return shm_stat->status;
  154. }
  155. /* end */
  156. // vim: set expandtab shiftwidth=4