summaryrefslogtreecommitdiff
path: root/ntpshmread.c
blob: 5854df64035e4790d9da41d0c3610ea314533cd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* 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 <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;

    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;

    shm_stat->leap = shm->leap;
    shm_stat->precision = shm->precision;
    shm_stat->mode = OK;
    return OK;
}

/* end */