1 /*-
2 * Copyright (c) 2011-2012 George V. Neville-Neil,
3 * Steven Kreuzer,
4 * Martin Burnicki,
5 * Jan Breuer,
6 * Gael Mace,
7 * Alexandre Van Kempen,
8 * Inaqui Delgado,
9 * Rick Ratzel,
10 * National Instruments.
11 * Copyright (c) 2009-2010 George V. Neville-Neil,
12 * Steven Kreuzer,
13 * Martin Burnicki,
14 * Jan Breuer,
15 * Gael Mace,
16 * Alexandre Van Kempen
17 *
18 * Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
19 *
20 * All Rights Reserved
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions are
24 * met:
25 * 1. Redistributions of source code must retain the above copyright notice,
26 * this list of conditions and the following disclaimer.
27 * 2. Redistributions in binary form must reproduce the above copyright
28 * notice, this list of conditions and the following disclaimer in the
29 * documentation and/or other materials provided with the distribution.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
32 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
38 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
40 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
41 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 /**
45 * @file timer.c
46 * @date Wed Jun 23 09:41:26 2010
47 *
48 * @brief The timers which run the state machine.
49 *
50 * Timers in the PTP daemon are run off of the signal system.
51 */
52
53 #include "../ptpd.h"
54
55 #define US_TIMER_INTERVAL (62500)
56 volatile unsigned int elapsed;
57
58 /*
59 * original code calls sigalarm every fixed 1ms. This highly pollutes the debug_log, and causes more interrupted instructions
60 * This was later modified to have a fixed granularity of 1s.
61 *
62 * Currently this has a configured granularity, and timerStart() guarantees that clocks expire ASAP when the granularity is too small.
63 * Timers must now be explicitelly canceled with timerStop (instead of timerStart(0.0))
64 */
65
66 void
67 catch_alarm(int sig)
68 {
69 elapsed++;
70 /* be sure to NOT call DBG in asynchronous handlers! */
71 }
72
73 void
74 initTimer(void)
75 {
76 struct itimerval itimer;
77
78 DBG("initTimer\n");
79
80 signal(SIGALRM, SIG_IGN);
81
82 elapsed = 0;
83 itimer.it_value.tv_sec = itimer.it_interval.tv_sec = 0;
84 itimer.it_value.tv_usec = itimer.it_interval.tv_usec =
85 US_TIMER_INTERVAL;
86
87 #ifdef __sun
88 sigset(SIGALRM, catch_alarm);
89 #else
90 signal(SIGALRM, catch_alarm);
91 #endif
92 setitimer(ITIMER_REAL, &itimer, 0);
93 }
94
95 void
96 timerUpdate(IntervalTimer * itimer)
97 {
98
99 int i, delta;
100
101 /*
102 * latch how many ticks we got since we were last called
103 * remember that catch_alarm() is totally asynchronous to this timerUpdate()
104 */
105 delta = elapsed;
106 elapsed = 0;
107
108 if (delta <= 0)
109 return;
110
111 /*
112 * if time actually passed, then decrease every timer left
113 * the one(s) that went to zero or negative are:
114 * a) rearmed at the original time (ignoring the time that may have passed ahead)
115 * b) have their expiration latched until timerExpired() is called
116 */
117 for (i = 0; i < TIMER_ARRAY_SIZE; ++i) {
118 if ((itimer[i].interval) > 0 && ((itimer[i].left) -= delta)
119 <= 0) {
120 itimer[i].left = itimer[i].interval;
121 itimer[i].expire = TRUE;
122 DBG2("TimerUpdate: Timer %u has now expired. (Re-armed again with interval %d, left %d)\n", i, itimer[i].interval, itimer[i].left );
123 }
124 }
125
126 }
127
128 void
129 timerStop(UInteger16 index, IntervalTimer * itimer)
130 {
131 if (index >= TIMER_ARRAY_SIZE)
132 return;
133
134 itimer[index].interval = 0;
135 DBG2("timerStop: Stopping timer %d. (New interval: %d; New left: %d)\n", index, itimer[index].left , itimer[index].interval);
136 }
137
138 void
139 timerStart(UInteger16 index, float interval, IntervalTimer * itimer)
140 {
141 if (index >= TIMER_ARRAY_SIZE)
142 return;
143
144 itimer[index].expire = FALSE;
145
146
147 /*
148 * US_TIMER_INTERVAL defines the minimum interval between sigalarms.
149 * timerStart has a float parameter for the interval, which is casted to integer.
150 * very small amounts are forced to expire ASAP by setting the interval to 1
151 */
152 itimer[index].left = (int)((interval * 1E6) / US_TIMER_INTERVAL);
153 if(itimer[index].left == 0){
154 /*
155 * the interval is too small, raise it to 1 to make sure it expires ASAP
156 * Timer cancelation is done explicitelly with stopTimer()
157 */
158 itimer[index].left = 1;
159
160 static int operator_warned_interval_too_small = 0;
161 if(!operator_warned_interval_too_small){
162 operator_warned_interval_too_small = 1;
163 /*
164 * using random uniform timers it is pratically guarantted that we hit the possible minimum timer.
165 * This is because of the current timer model based on periodic sigalarm, irrespective if the next
166 * event is close or far away in time.
167 *
168 * A solution would be to recode this whole module with a calendar queue, while keeping the same API:
169 * E.g.: http://www.isi.edu/nsnam/ns/doc/node35.html
170 *
171 * Having events that expire immediatly (ie, delayreq invocations using random timers) can lead to
172 * messages appearing in unexpected ordering, so the protocol implementation must check more conditions
173 * and not assume a certain ususal ordering
174 */
175 DBG("Timer would be issued immediatly. Please raise dep/timer.c:US_TIMER_INTERVAL to hold %.2fs\n",
176 interval
177 );
178
179 }
180 }
181 itimer[index].interval = itimer[index].left;
182
183 DBG2("timerStart: Set timer %d to %f. New interval: %d; new left: %d\n", index, interval, itimer[index].left , itimer[index].interval);
184 }
185
186
187
188 /*
189 * This function arms the timer with a uniform range, as requested by page 105 of the standard (for sending delayReqs.)
190 * actual time will be U(0, interval * 2.0);
191 *
192 * PTPv1 algorithm was:
193 * ptpClock->R = getRand(&ptpClock->random_seed) % (PTP_DELAY_REQ_INTERVAL - 2) + 2;
194 * R is the number of Syncs to be received, before sending a new request
195 *
196 */
197 void timerStart_random(UInteger16 index, float interval, IntervalTimer * itimer)
198 {
199 float new_value;
200
201 new_value = getRand() * interval * 2.0;
202 DBG2(" timerStart_random: requested %.2f, got %.2f\n", interval, new_value);
203
204 timerStart(index, new_value, itimer);
205 }
206
207
208
209 Boolean
210 timerExpired(UInteger16 index, IntervalTimer * itimer)
211 {
212 timerUpdate(itimer);
213
214 if (index >= TIMER_ARRAY_SIZE)
215 return FALSE;
216
217 if (!itimer[index].expire)
218 return FALSE;
219
220 itimer[index].expire = FALSE;
221
222
223 DBG2("timerExpired: Timer %d expired, taking actions. current interval: %d; current left: %d\n", index, itimer[index].left , itimer[index].interval);
224
225 return TRUE;
226 }
227
228 Boolean
229 timerStopped(UInteger16 index, IntervalTimer * itimer)
230 {
231 timerUpdate(itimer);
232
233 if (index >= TIMER_ARRAY_SIZE)
234 return FALSE;
235
236 if (itimer[index].interval == 0) {
237 return TRUE;
238 DBG2("timerStopped: Timer %d is stopped\n", index);
239 }
240
241 return FALSE;
242
243 }
244
245 Boolean
246 timerRunning(UInteger16 index, IntervalTimer * itimer)
247 {
248 timerUpdate(itimer);
249
250 if (index >= TIMER_ARRAY_SIZE)
251 return FALSE;
252
253 if ((itimer[index].interval != 0) &&
254 (itimer[index].expire == FALSE)) {
255 return TRUE;
256 DBG2("timerRunning: Timer %d is running\n", index);
257 }
258
259 return FALSE;
260
261 }
262