1 /*
2 * The olsr.org Optimized Link-State Routing daemon (olsrd)
3 *
4 * (c) by the OLSR project
5 *
6 * See our Git repository to find out who worked on this file
7 * and thus is a copyright holder on it.
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in
19 * the documentation and/or other materials provided with the
20 * distribution.
21 * * Neither the name of olsr.org, olsrd nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * Visit http://www.olsr.org for more information.
39 *
40 * If you find this software useful feel free to make a donation
41 * to the project. For more information see the website or contact
42 * the copyright holders.
43 *
44 */
45
46 #include "scheduler.h"
47 #include "log.h"
48 #include "link_set.h"
49 #include "olsr.h"
50 #include "olsr_cookie.h"
51 #include "net_os.h"
52 #include "mpr_selector_set.h"
53 #include "olsr_random.h"
54 #include "common/avl.h"
55
56 #include <sys/times.h>
57
58 #include <unistd.h>
59 #include <assert.h>
60 #include <time.h>
61
62 #ifdef __MACH__
63 #include "mach/clock_gettime.h"
64 #endif
65
66 #ifdef _WIN32
67 #define close(x) closesocket(x)
68 #endif /* _WIN32 */
69
70 /* Timer data, global. Externed in scheduler.h */
71 uint32_t now_times; /* relative time compared to startup (in milliseconds) */
72 struct timespec first_tv; /* timevalue during startup */
73 struct timespec last_tv; /* timevalue used for last olsr_times() calculation */
74
75 /* Hashed root of all timers */
76 static struct list_node timer_wheel[TIMER_WHEEL_SLOTS];
77 static uint32_t timer_last_run; /* remember the last timeslot walk */
78
79 /* Memory cookie for the block based memory manager */
80 static struct olsr_cookie_info *timer_mem_cookie = NULL;
81
82 /* Head of all OLSR used sockets */
83 static struct list_node socket_head = { &socket_head, &socket_head };
84
85 /* Prototypes */
86 static void walk_timers(uint32_t *);
87 static void walk_timers_cleanup(void);
88 static void poll_sockets(void);
89 static uint32_t calc_jitter(unsigned int rel_time, uint8_t jitter_pct, unsigned int random_val);
90 static void olsr_cleanup_timer(struct timer_entry *timer);
91
92 struct avl_tree timer_cleanup_tree;
93
94 struct timer_cleanup_entry {
95 struct avl_node avl;
96 struct timer_entry * timer;
97 };
98
99 /* static INLINE struct timer_cleanup_entry * node2timercleanup(struct avl_node *ptr) */
100 AVLNODE2STRUCT(node2timercleanup, struct timer_cleanup_entry, avl);
101
102 /**
103 * Loop over all timer cleanup entries and put the iterated entry in timer
104 */
105 #define OLSR_FOR_ALL_TIMERS_CLEANUP(timer) \
106 { \
107 struct avl_node *timer_avl_node, *timer_avl_node_next; \
108 for (timer_avl_node = avl_walk_first(&timer_cleanup_tree); \
109 timer_avl_node; timer_avl_node = timer_avl_node_next) { \
110 timer_avl_node_next = avl_walk_next(timer_avl_node); \
111 timer = node2timercleanup(timer_avl_node);
112 #define OLSR_FOR_ALL_TIMER_CLEANUP_END(timer) }}
113
avl_comp_timer(const void * entry1,const void * entry2)114 static int avl_comp_timer(const void *entry1, const void *entry2) {
115 if (entry1 < entry2) {
116 return -1;
117 }
118
119 if (entry1 > entry2) {
120 return 1;
121 }
122
123 return 0;
124 }
125
126 /*
127 * A wrapper around times(2). Note, that this function has some
128 * portability problems, so do not rely on absolute values returned.
129 * Under Linux, uclibc and libc directly call the sys_times() located
130 * in kernel/sys.c and will only return an error if the tms_buf is
131 * not writeable.
132 */
133 uint32_t
olsr_times(void)134 olsr_times(void)
135 {
136 struct timespec tv;
137
138 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
139 olsr_exit("OS clock is not working, have to shut down OLSR", EXIT_FAILURE);
140 }
141
142 /* test if time jumped backward or more than 60 seconds forward */
143 if ((tv.tv_sec < last_tv.tv_sec) //
144 || ((tv.tv_sec == last_tv.tv_sec) //
145 && tv.tv_nsec < last_tv.tv_nsec) //
146 || ((tv.tv_sec - last_tv.tv_sec) > 60)) {
147 uint64_t nsec;
148
149 OLSR_PRINTF(1, "Time jump (%ld.%09ld to %ld.%09ld)\n", //
150 (long int) last_tv.tv_sec,//
151 (long int) last_tv.tv_nsec,//
152 (long int) tv.tv_sec,//
153 (long int) tv.tv_nsec);
154
155 nsec = (last_tv.tv_sec - first_tv.tv_sec) * 1000000000 + (last_tv.tv_nsec - first_tv.tv_nsec);
156 nsec += 1000000; /* advance time by one millisecond */
157
158 first_tv = tv;
159 first_tv.tv_sec -= (nsec / 1000000000);
160 first_tv.tv_nsec -= (nsec % 1000000000);
161
162 if (first_tv.tv_nsec < 0) {
163 first_tv.tv_sec--;
164 first_tv.tv_nsec += 1000000000;
165 }
166 last_tv = tv;
167 return (nsec / 1000000);
168 }
169
170 last_tv = tv;
171 return (uint32_t)((tv.tv_sec - first_tv.tv_sec) * 1000 + (tv.tv_nsec - first_tv.tv_nsec) / 1000000);
172 }
173
174 /**
175 * Returns a timestamp s seconds in the future
176 */
177 uint32_t
olsr_getTimestamp(uint32_t s)178 olsr_getTimestamp(uint32_t s)
179 {
180 return now_times + s;
181 }
182
183 /**
184 * Returns the number of milliseconds until the timestamp will happen
185 */
186
187 int32_t
olsr_getTimeDue(uint32_t s)188 olsr_getTimeDue(uint32_t s)
189 {
190 uint32_t diff;
191 if (s > now_times) {
192 diff = s - now_times;
193
194 /* overflow ? */
195 if (diff > (1u << 31)) {
196 return -(int32_t) (0xffffffff - diff);
197 }
198 return (int32_t) (diff);
199 }
200
201 diff = now_times - s;
202 /* overflow ? */
203 if (diff > (1u << 31)) {
204 return (int32_t) (0xffffffff - diff);
205 }
206 return -(int32_t) (diff);
207 }
208
209 bool
olsr_isTimedOut(uint32_t s)210 olsr_isTimedOut(uint32_t s)
211 {
212 if (s > now_times) {
213 return s - now_times > (1u << 31);
214 }
215
216 return now_times - s <= (1u << 31);
217 }
218
219 /**
220 * Add a socket and handler to the socketset
221 * beeing used in the main select(2) loop
222 * in listen_loop
223 *
224 *@param fd the socket
225 *@param pf_pr the processing function
226 *@param pf_imm the (immediate) processing function
227 *@param data the data pointer for the processing function
228 *@param flags the flags for the processing function
229 */
230 void
add_olsr_socket(int fd,socket_handler_func pf_pr,socket_handler_func pf_imm,void * data,unsigned int flags)231 add_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, void *data, unsigned int flags)
232 {
233 struct olsr_socket_entry *new_entry;
234
235 if (fd < 0 || (pf_pr == NULL && pf_imm == NULL)) {
236 OLSR_PRINTF(1, "Bogus socket entry - not registering...");
237 return;
238 }
239 OLSR_PRINTF(3, "Adding OLSR socket entry %d\n", fd);
240
241 new_entry = olsr_malloc(sizeof(*new_entry), "Socket entry");
242
243 new_entry->fd = fd;
244 new_entry->process_immediate = pf_imm;
245 new_entry->process_pollrate = pf_pr;
246 new_entry->data = data;
247 new_entry->flags = flags;
248
249 /* Queue */
250 list_node_init(&new_entry->socket_node);
251 list_add_before(&socket_head, &new_entry->socket_node);
252 }
253
254 /**
255 * Remove a socket and handler to the socketset
256 * beeing used in the main select(2) loop
257 * in listen_loop
258 *
259 *@param fd the socket
260 *@param pf_pr the processing function
261 *@param pf_imm the (immediate) processing function
262 */
263 int
remove_olsr_socket(int fd,socket_handler_func pf_pr,socket_handler_func pf_imm)264 remove_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm)
265 {
266 struct olsr_socket_entry *entry;
267
268 if (fd < 0 || (pf_pr == NULL && pf_imm == NULL)) {
269 OLSR_PRINTF(1, "Bogus socket entry - not processing...");
270 return 0;
271 }
272 OLSR_PRINTF(3, "Removing OLSR socket entry %d\n", fd);
273
274 OLSR_FOR_ALL_SOCKETS(entry) {
275 if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
276 /* just mark this node as "deleted", it will be cleared later at the end of handle_fds() */
277 entry->process_immediate = NULL;
278 entry->process_pollrate = NULL;
279 entry->flags = 0;
280 return 1;
281 }
282 }
283 OLSR_FOR_ALL_SOCKETS_END(entry);
284 return 0;
285 }
286
287 void
enable_olsr_socket(int fd,socket_handler_func pf_pr,socket_handler_func pf_imm,unsigned int flags)288 enable_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, unsigned int flags)
289 {
290 struct olsr_socket_entry *entry;
291
292 OLSR_FOR_ALL_SOCKETS(entry) {
293 if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
294 entry->flags |= flags;
295 }
296 }
297 OLSR_FOR_ALL_SOCKETS_END(entry);
298 }
299
300 void
disable_olsr_socket(int fd,socket_handler_func pf_pr,socket_handler_func pf_imm,unsigned int flags)301 disable_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, unsigned int flags)
302 {
303 struct olsr_socket_entry *entry;
304
305 OLSR_FOR_ALL_SOCKETS(entry) {
306 if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
307 entry->flags &= ~flags;
308 }
309 }
310 OLSR_FOR_ALL_SOCKETS_END(entry);
311 }
312
313 /**
314 * Close and free all sockets.
315 */
316 void
olsr_flush_sockets(void)317 olsr_flush_sockets(void)
318 {
319 struct olsr_socket_entry *entry;
320
321 OLSR_FOR_ALL_SOCKETS(entry) {
322 close(entry->fd);
323 list_remove(&entry->socket_node);
324 free(entry);
325 } OLSR_FOR_ALL_SOCKETS_END(entry);
326 }
327
328 static void
poll_sockets(void)329 poll_sockets(void)
330 {
331 int n;
332 struct olsr_socket_entry *entry;
333 fd_set ibits, obits;
334 struct timeval tvp = { 0, 0 };
335 int hfd = 0, fdsets = 0;
336
337 /* If there are no registered sockets we
338 * do not call select(2)
339 */
340 if (list_is_empty(&socket_head)) {
341 return;
342 }
343
344 FD_ZERO(&ibits);
345 FD_ZERO(&obits);
346
347 /* Adding file-descriptors to FD set */
348 OLSR_FOR_ALL_SOCKETS(entry) {
349 if (entry->process_pollrate == NULL) {
350 continue;
351 }
352 if ((entry->flags & SP_PR_READ) != 0) {
353 fdsets |= SP_PR_READ;
354 FD_SET((unsigned int)entry->fd, &ibits); /* And we cast here since we get a warning on Win32 */
355 }
356 if ((entry->flags & SP_PR_WRITE) != 0) {
357 fdsets |= SP_PR_WRITE;
358 FD_SET((unsigned int)entry->fd, &obits); /* And we cast here since we get a warning on Win32 */
359 }
360 if ((entry->flags & (SP_PR_READ | SP_PR_WRITE)) != 0 && entry->fd >= hfd) {
361 hfd = entry->fd + 1;
362 }
363 }
364 OLSR_FOR_ALL_SOCKETS_END(entry);
365
366 /* Running select on the FD set */
367 do {
368 n = olsr_select(hfd, fdsets & SP_PR_READ ? &ibits : NULL, fdsets & SP_PR_WRITE ? &obits : NULL, NULL, &tvp);
369 } while (n == -1 && errno == EINTR);
370
371 if (n == 0) {
372 return;
373 }
374 if (n == -1) { /* Did something go wrong? */
375 OLSR_PRINTF(1, "select error: %s", strerror(errno));
376 return;
377 }
378
379 /* Update time since this is much used by the parsing functions */
380 now_times = olsr_times();
381 OLSR_FOR_ALL_SOCKETS(entry) {
382 int flags;
383 if (entry->process_pollrate == NULL) {
384 continue;
385 }
386 flags = 0;
387 if (FD_ISSET(entry->fd, &ibits)) {
388 flags |= SP_PR_READ;
389 }
390 if (FD_ISSET(entry->fd, &obits)) {
391 flags |= SP_PR_WRITE;
392 }
393 if (flags != 0) {
394 entry->process_pollrate(entry->fd, entry->data, flags);
395 }
396 }
397 OLSR_FOR_ALL_SOCKETS_END(entry);
398 }
399
400 static void
handle_fds(uint32_t next_interval)401 handle_fds(uint32_t next_interval)
402 {
403 struct olsr_socket_entry *entry;
404 struct timeval tvp;
405 int32_t remaining;
406
407 /* calculate the first timeout */
408 now_times = olsr_times();
409
410 remaining = TIME_DUE(next_interval);
411 if (remaining <= 0) {
412 /* we are already over the interval */
413 if (list_is_empty(&socket_head)) {
414 /* If there are no registered sockets we do not call select(2) */
415 return;
416 }
417 tvp.tv_sec = 0;
418 tvp.tv_usec = 0;
419 } else {
420 /* we need an absolute time - milliseconds */
421 tvp.tv_sec = remaining / MSEC_PER_SEC;
422 tvp.tv_usec = (remaining % MSEC_PER_SEC) * USEC_PER_MSEC;
423 }
424
425 /* do at least one select */
426 for (;;) {
427 fd_set ibits, obits;
428 int n, hfd = 0, fdsets = 0;
429 FD_ZERO(&ibits);
430 FD_ZERO(&obits);
431
432 /* Adding file-descriptors to FD set */
433 OLSR_FOR_ALL_SOCKETS(entry) {
434 if (entry->process_immediate == NULL) {
435 continue;
436 }
437 if ((entry->flags & SP_IMM_READ) != 0) {
438 fdsets |= SP_IMM_READ;
439 FD_SET((unsigned int)entry->fd, &ibits); /* And we cast here since we get a warning on Win32 */
440 }
441 if ((entry->flags & SP_IMM_WRITE) != 0) {
442 fdsets |= SP_IMM_WRITE;
443 FD_SET((unsigned int)entry->fd, &obits); /* And we cast here since we get a warning on Win32 */
444 }
445 if ((entry->flags & (SP_IMM_READ | SP_IMM_WRITE)) != 0 && entry->fd >= hfd) {
446 hfd = entry->fd + 1;
447 }
448 }
449 OLSR_FOR_ALL_SOCKETS_END(entry);
450
451 if (hfd == 0 && (long)remaining <= 0) {
452 /* we are over the interval and we have no fd's. Skip the select() etc. */
453 break;
454 }
455
456 do {
457 n = olsr_select(hfd, fdsets & SP_IMM_READ ? &ibits : NULL, fdsets & SP_IMM_WRITE ? &obits : NULL, NULL, &tvp);
458 } while (n == -1 && errno == EINTR);
459
460 if (n == 0) { /* timeout! */
461 break;
462 }
463 if (n == -1) { /* Did something go wrong? */
464 OLSR_PRINTF(1, "select error: %s", strerror(errno));
465 break;
466 }
467
468 /* Update time since this is much used by the parsing functions */
469 now_times = olsr_times();
470 OLSR_FOR_ALL_SOCKETS(entry) {
471 int flags;
472 if (entry->process_immediate == NULL) {
473 continue;
474 }
475 flags = 0;
476 if (FD_ISSET(entry->fd, &ibits)) {
477 flags |= SP_IMM_READ;
478 }
479 if (FD_ISSET(entry->fd, &obits)) {
480 flags |= SP_IMM_WRITE;
481 }
482 if (flags != 0) {
483 entry->process_immediate(entry->fd, entry->data, flags);
484 }
485 }
486 OLSR_FOR_ALL_SOCKETS_END(entry);
487
488 /* calculate the next timeout */
489 remaining = TIME_DUE(next_interval);
490 if (remaining <= 0) {
491 /* we are already over the interval */
492 break;
493 }
494 /* we need an absolute time - milliseconds */
495 tvp.tv_sec = remaining / MSEC_PER_SEC;
496 tvp.tv_usec = (remaining % MSEC_PER_SEC) * USEC_PER_MSEC;
497 }
498
499 OLSR_FOR_ALL_SOCKETS(entry) {
500 if (entry->process_immediate == NULL && entry->process_pollrate == NULL) {
501 /* clean up socket handler */
502 list_remove(&entry->socket_node);
503 free(entry);
504 }
505 } OLSR_FOR_ALL_SOCKETS_END(entry);
506 }
507
508 typedef enum {
509 INIT, RUNNING, STOPPING, ENDED
510 } state_t;
511
512 static volatile state_t state = INIT;
513
olsr_scheduler_is_stopped(void)514 static bool olsr_scheduler_is_stopped(void) {
515 return ((state == INIT) || (state == ENDED));
516 }
517
olsr_scheduler_stop(void)518 void olsr_scheduler_stop(void) {
519 if (olsr_scheduler_is_stopped()) {
520 return;
521 }
522
523 state = STOPPING;
524 }
525
526 /**
527 * Main scheduler event loop. Polls at every
528 * sched_poll_interval and calls all functions
529 * that are timed out or that are triggered.
530 * Also calls the olsr_process_changes()
531 * function at every poll.
532 *
533 * @return nada
534 */
olsr_scheduler(void)535 void olsr_scheduler(void)
536 {
537 state = RUNNING;
538 OLSR_PRINTF(1, "Scheduler started - polling every %d ms\n", (int)(olsr_cnf->pollrate*1000));
539
540 /* Main scheduler loop */
541 while (state == RUNNING) {
542 uint32_t next_interval;
543
544 /*
545 * Update the global timestamp. We are using a non-wallclock timer here
546 * to avoid any undesired side effects if the system clock changes.
547 */
548 now_times = olsr_times();
549 next_interval = GET_TIMESTAMP(olsr_cnf->pollrate * 1000);
550
551 /* Read incoming data */
552 poll_sockets();
553
554 if (state != RUNNING) {
555 break;
556 }
557
558 /* Process timers */
559 walk_timers(&timer_last_run);
560 walk_timers_cleanup();
561
562 if (state != RUNNING) {
563 break;
564 }
565
566 /* Update */
567 olsr_process_changes();
568
569 if (state != RUNNING) {
570 break;
571 }
572
573 /* Check for changes in topology */
574 if (link_changes) {
575 increase_local_ansn();
576 OLSR_PRINTF(3, "ANSN UPDATED %d\n\n", get_local_ansn());
577 link_changes = false;
578 }
579
580 if (state != RUNNING) {
581 break;
582 }
583
584 /* Read incoming data and handle it immediiately */
585 handle_fds(next_interval);
586 }
587 walk_timers_cleanup();
588
589 state = ENDED;
590 }
591
592 /**
593 * Decrement a relative timer by a random number range.
594 *
595 * @param rel_time the relative timer expressed in units of milliseconds.
596 * @param jitter_pct the jitter in percent
597 * @param random_val cached result of random() at system init.
598 * @return the absolute timer in system clock tick units
599 */
600 static uint32_t
calc_jitter(unsigned int rel_time,uint8_t jitter_pct,unsigned int random_val)601 calc_jitter(unsigned int rel_time, uint8_t jitter_pct, unsigned int random_val)
602 {
603 unsigned int jitter_time;
604
605 /*
606 * No jitter or, jitter larger than 99% does not make sense.
607 * Also protect against overflows resulting from > 25 bit timers.
608 */
609 if (jitter_pct == 0 || jitter_pct > 99 || rel_time > (1u << 24)) {
610 return GET_TIMESTAMP(rel_time);
611 }
612
613 /*
614 * Play some tricks to avoid overflows with integer arithmetic.
615 */
616 jitter_time = (jitter_pct * rel_time) / 100;
617 jitter_time = random_val / (1 + OLSR_RANDOM_MAX / (jitter_time + 1));
618
619 OLSR_PRINTF(3, "TIMER: jitter %u%% rel_time %ums to %ums\n", jitter_pct, rel_time, rel_time - jitter_time);
620
621 return GET_TIMESTAMP(rel_time - jitter_time);
622 }
623
624 /**
625 * Init datastructures for maintaining timers.
626 */
627 void
olsr_init_timers(void)628 olsr_init_timers(void)
629 {
630 int idx;
631
632 OLSR_PRINTF(3, "Initializing scheduler.\n");
633
634 /* Grab initial timestamp */
635 if (clock_gettime(CLOCK_MONOTONIC, &first_tv)) {
636 olsr_exit("OS clock is not working, have to shut down OLSR", EXIT_FAILURE);
637 }
638 last_tv = first_tv;
639 now_times = olsr_times();
640
641 avl_init(&timer_cleanup_tree, avl_comp_timer);
642
643 for (idx = 0; idx < TIMER_WHEEL_SLOTS; idx++) {
644 list_head_init(&timer_wheel[idx]);
645 }
646
647 /*
648 * Reset the last timer run.
649 */
650 timer_last_run = now_times;
651
652 /* Allocate a cookie for the block based memory manager. */
653 timer_mem_cookie = olsr_alloc_cookie("timer_entry", OLSR_COOKIE_TYPE_MEMORY);
654 olsr_cookie_set_memory_size(timer_mem_cookie, sizeof(struct timer_entry));
655 }
656
657 /**
658 * Walk through the timer list and check if any timer is ready to fire.
659 * Callback the provided function with the context pointer.
660 */
661 static void
walk_timers(uint32_t * last_run)662 walk_timers(uint32_t * last_run)
663 {
664 unsigned int total_timers_walked = 0, total_timers_fired = 0;
665 unsigned int wheel_slot_walks = 0;
666
667 /*
668 * Check the required wheel slots since the last time a timer walk was invoked,
669 * or check *all* the wheel slots, whatever is less work.
670 * The latter is meant as a safety belt if the scheduler falls behind.
671 */
672 while ((*last_run <= now_times) && (wheel_slot_walks < TIMER_WHEEL_SLOTS)) {
673 struct list_node tmp_head_node;
674 /* keep some statistics */
675 unsigned int timers_walked = 0, timers_fired = 0;
676
677 /* Get the hash slot for this clocktick */
678 struct list_node *const timer_head_node = &timer_wheel[*last_run & TIMER_WHEEL_MASK];
679
680 /* Walk all entries hanging off this hash bucket. We treat this basically as a stack
681 * so that we always know if and where the next element is.
682 */
683 list_head_init(&tmp_head_node);
684 while (!list_is_empty(timer_head_node)) {
685 /* the top element */
686 struct list_node *const timer_node = timer_head_node->next;
687 struct timer_entry *const timer = list2timer(timer_node);
688
689 /*
690 * Dequeue and insert to a temporary list.
691 * We do this to avoid losing our walking context when
692 * multiple timers fire.
693 */
694 list_remove(timer_node);
695 list_add_after(&tmp_head_node, timer_node);
696 timers_walked++;
697
698 if (timer->timer_flags & OLSR_TIMER_REMOVED) {
699 continue;
700 }
701
702 /* Ready to fire ? */
703 if (TIMED_OUT(timer->timer_clock)) {
704
705 OLSR_PRINTF(7, "TIMER: fire %s timer %p, ctx %p, "
706 "at clocktick %u (%s)\n",
707 timer->timer_cookie->ci_name,
708 timer, timer->timer_cb_context, (unsigned int)*last_run, olsr_wallclock_string());
709
710 /* This timer is expired, call into the provided callback function */
711 timer->timer_cb(timer->timer_cb_context);
712
713 /* Only act on actually running timers */
714 if (timer->timer_flags & OLSR_TIMER_RUNNING) {
715 /*
716 * Don't restart the periodic timer if the callback function has
717 * stopped the timer.
718 */
719 if (timer->timer_period) {
720 /* For periodical timers, rehash the random number and restart */
721 timer->timer_random = olsr_random();
722 olsr_change_timer(timer, timer->timer_period, timer->timer_jitter_pct, OLSR_TIMER_PERIODIC);
723 } else {
724 /* Singleshot timers are stopped */
725 olsr_stop_timer(timer);
726 }
727 }
728
729 timers_fired++;
730 }
731 }
732
733 /*
734 * Now merge the temporary list back to the old bucket.
735 */
736 list_merge(timer_head_node, &tmp_head_node);
737
738 /* keep some statistics */
739 total_timers_walked += timers_walked;
740 total_timers_fired += timers_fired;
741
742 /* Increment the time slot and wheel slot walk iteration */
743 (*last_run)++;
744 wheel_slot_walks++;
745 }
746
747 OLSR_PRINTF(7, "TIMER: processed %4u/%d clockwheel slots, "
748 "timers walked %4u/%u, timers fired %u\n",
749 wheel_slot_walks, TIMER_WHEEL_SLOTS, total_timers_walked, timer_mem_cookie->ci_usage, total_timers_fired);
750
751 /*
752 * If the scheduler has slipped and we have walked all wheel slots,
753 * reset the last timer run.
754 */
755 *last_run = now_times;
756 }
757
walk_timers_cleanup(void)758 static void walk_timers_cleanup(void) {
759 struct timer_cleanup_entry * timer;
760
761 OLSR_FOR_ALL_TIMERS_CLEANUP(timer) {
762 olsr_cleanup_timer(timer->timer);
763 avl_delete(&timer_cleanup_tree, &timer->avl);
764 free(timer);
765 } OLSR_FOR_ALL_TIMER_CLEANUP_END(slot)
766 }
767
768 /**
769 * Stop and delete all timers.
770 */
771 void
olsr_flush_timers(void)772 olsr_flush_timers(void)
773 {
774 struct list_node *timer_head_node;
775 struct list_node tmp_head_node;
776 unsigned int wheel_slot = 0;
777
778 list_head_init(&tmp_head_node);
779
780 walk_timers_cleanup();
781
782 for (wheel_slot = 0; wheel_slot < TIMER_WHEEL_SLOTS; wheel_slot++) {
783 timer_head_node = &timer_wheel[wheel_slot & TIMER_WHEEL_MASK];
784
785 /* stop all entries hanging off this hash bucket. */
786 while (!list_is_empty(timer_head_node)) {
787 /* the top element */
788 struct list_node *const timer_node = timer_head_node->next;
789 struct timer_entry *const timer = list2timer(timer_node);
790
791 /*
792 * Dequeue and insert to a temporary list.
793 * We do this to avoid losing our walking context when
794 * multiple timers fire.
795 */
796 list_remove(timer_node);
797 list_add_after(&tmp_head_node, timer_node);
798
799 olsr_stop_timer(timer);
800 }
801 }
802
803 /*
804 * Now merge the temporary list back to the old bucket.
805 */
806 list_merge(timer_head_node, &tmp_head_node);
807
808 walk_timers_cleanup();
809 }
810
811 /**
812 * Returns the difference between gmt and local time in seconds.
813 * Use gmtime() and localtime() to keep things simple.
814 *
815 * taken and slightly modified from www.tcpdump.org.
816 */
817 static int
olsr_get_timezone(void)818 olsr_get_timezone(void)
819 {
820 #define OLSR_TIMEZONE_UNINITIALIZED -1
821 static int time_diff = OLSR_TIMEZONE_UNINITIALIZED;
822 if (time_diff == OLSR_TIMEZONE_UNINITIALIZED) {
823 int dir;
824 const time_t t = time(NULL);
825 const struct tm gmt = *gmtime(&t);
826 const struct tm *loc = localtime(&t);
827
828 time_diff = (loc->tm_hour - gmt.tm_hour) * 60 * 60 + (loc->tm_min - gmt.tm_min) * 60;
829
830 /*
831 * If the year or julian day is different, we span 00:00 GMT
832 * and must add or subtract a day. Check the year first to
833 * avoid problems when the julian day wraps.
834 */
835 dir = loc->tm_year - gmt.tm_year;
836 if (!dir) {
837 dir = loc->tm_yday - gmt.tm_yday;
838 }
839
840 time_diff += dir * 24 * 60 * 60;
841 }
842 return time_diff;
843 }
844
845 /**
846 * Format an absolute wallclock system time string.
847 * May be called upto 4 times in a single printf() statement.
848 * Displays microsecond resolution.
849 *
850 * @return buffer to a formatted system time string.
851 */
852 const char *
olsr_wallclock_string(void)853 olsr_wallclock_string(void)
854 {
855 static char buf[sizeof("00:00:00.000000")];
856 struct timeval now;
857 unsigned long sec, usec;
858
859 gettimeofday(&now, NULL);
860
861 sec = (unsigned long) (now.tv_sec + olsr_get_timezone());
862 usec = (unsigned long) (now.tv_usec % 1000000);
863
864 snprintf(buf, sizeof(buf), "%02lu:%02lu:%02lu.%06lu", (sec % 86400) / 3600, (sec % 3600) / 60, sec % 60, usec);
865
866 return buf;
867 }
868
869 /**
870 * Format an relative non-wallclock system time string.
871 * May be called upto 4 times in a single printf() statement.
872 * Displays millisecond resolution.
873 *
874 * @param clk absolute time expressed in clockticks
875 * @return buffer to a formatted system time string.
876 */
877 const char *
olsr_clock_string(uint32_t clk)878 olsr_clock_string(uint32_t clk)
879 {
880 static char buf[sizeof("00:00:00.000 ")];
881
882 /* On most systems a clocktick is a 10ms quantity. */
883 unsigned int msec = clk % 1000;
884 unsigned int sec = clk / 1000;
885
886 snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%03u", sec / 3600, (sec % 3600) / 60, (sec % 60), (msec % MSEC_PER_SEC));
887
888 return buf;
889 }
890
891 /**
892 * Start a new timer.
893 *
894 * @param rel_time relative time expressed in milliseconds
895 * @param jitter_pct jitter expressed in percent
896 * @param periodical true for a repeating timer, false for a one-shot timer
897 * @param cb_func timer callback function
898 * @param context context for the callback function
899 * @param ci timer cookie
900 * @return a pointer to the created entry
901 */
902 struct timer_entry *
olsr_start_timer(unsigned int rel_time,uint8_t jitter_pct,bool periodical,timer_cb_func cb_func,void * context,struct olsr_cookie_info * ci)903 olsr_start_timer(unsigned int rel_time,
904 uint8_t jitter_pct, bool periodical, timer_cb_func cb_func, void *context, struct olsr_cookie_info *ci)
905 {
906 struct timer_entry *timer;
907
908 if (ci == NULL) {
909 ci = def_timer_ci;
910 }
911 assert(cb_func);
912
913 timer = olsr_cookie_malloc(timer_mem_cookie);
914
915 /*
916 * Compute random numbers only once.
917 */
918 if (!timer->timer_random) {
919 timer->timer_random = olsr_random();
920 }
921
922 /* Fill entry */
923 timer->timer_clock = calc_jitter(rel_time, jitter_pct, timer->timer_random);
924 timer->timer_cb = cb_func;
925 timer->timer_cb_context = context;
926 timer->timer_jitter_pct = jitter_pct;
927 timer->timer_flags = OLSR_TIMER_RUNNING;
928
929 /* The cookie is used for debugging to traceback the originator */
930 timer->timer_cookie = ci;
931 olsr_cookie_usage_incr(ci->ci_id);
932
933 /* Singleshot or periodical timer ? */
934 timer->timer_period = periodical ? rel_time : 0;
935
936 /*
937 * Now insert in the respective timer_wheel slot.
938 */
939 list_add_before(&timer_wheel[timer->timer_clock & TIMER_WHEEL_MASK], &timer->timer_list);
940
941 OLSR_PRINTF(7, "TIMER: start %s timer %p firing in %s, ctx %p\n",
942 ci->ci_name, timer, olsr_clock_string(timer->timer_clock), context);
943
944 return timer;
945 }
946
947 /**
948 * Delete a timer.
949 *
950 * @param timer the timer_entry that shall be removed
951 */
952 void
olsr_stop_timer(struct timer_entry * timer)953 olsr_stop_timer(struct timer_entry *timer)
954 {
955 /* It's okay to get a NULL here */
956 if (!timer //
957 || !(timer->timer_flags & OLSR_TIMER_RUNNING)) {
958 return;
959 }
960
961 assert(timer->timer_cookie); /* we want timer cookies everywhere */
962
963 OLSR_PRINTF(7, "TIMER: stop %s timer %p, ctx %p\n",
964 timer->timer_cookie->ci_name, timer, timer->timer_cb_context);
965
966
967 timer->timer_flags &= ~OLSR_TIMER_RUNNING;
968 timer->timer_flags |= OLSR_TIMER_REMOVED;
969
970 {
971 struct timer_cleanup_entry * node = olsr_malloc(sizeof(struct timer_cleanup_entry), "timer cleanup entry");
972 node->avl.key = timer;
973 node->timer = timer;
974 if (avl_insert(&timer_cleanup_tree, &node->avl, AVL_DUP_NO) == -1) {
975 /* duplicate */
976 free(node);
977 }
978 }
979 }
980
981 /**
982 * Clean up a timer.
983 *
984 * @param timer the timer_entry that shall be cleaned up
985 */
986 static void
olsr_cleanup_timer(struct timer_entry * timer)987 olsr_cleanup_timer(struct timer_entry *timer)
988 {
989 /* It's okay to get a NULL here */
990 if (!timer //
991 || !(timer->timer_flags & OLSR_TIMER_REMOVED)) {
992 return;
993 }
994
995 assert(timer->timer_cookie); /* we want timer cookies everywhere */
996
997 OLSR_PRINTF(7, "TIMER: cleanup %s timer %p, ctx %p\n",
998 timer->timer_cookie->ci_name, timer, timer->timer_cb_context);
999
1000
1001 /*
1002 * Carve out of the existing wheel_slot and free.
1003 */
1004 list_remove(&timer->timer_list);
1005 timer->timer_flags &= ~OLSR_TIMER_REMOVED;
1006 olsr_cookie_usage_decr(timer->timer_cookie->ci_id);
1007
1008 olsr_cookie_free(timer_mem_cookie, timer);
1009 }
1010
1011 /**
1012 * Change a timer_entry.
1013 *
1014 * @param timer timer_entry to be changed.
1015 * @param rel_time new relative time expressed in units of milliseconds.
1016 * @param jitter_pct new jitter expressed in percent.
1017 * @param periodical true for a repeating timer, false for a one-shot timer
1018 */
1019 void
olsr_change_timer(struct timer_entry * timer,unsigned int rel_time,uint8_t jitter_pct,bool periodical)1020 olsr_change_timer(struct timer_entry *timer, unsigned int rel_time, uint8_t jitter_pct, bool periodical)
1021 {
1022 /* Sanity check. */
1023 if (!timer) {
1024 return;
1025 }
1026
1027 assert(timer->timer_cookie); /* we want timer cookies everywhere */
1028
1029 /* Singleshot or periodical timer ? */
1030 timer->timer_period = periodical ? rel_time : 0;
1031
1032 timer->timer_clock = calc_jitter(rel_time, jitter_pct, timer->timer_random);
1033 timer->timer_jitter_pct = jitter_pct;
1034
1035 /*
1036 * Changes are easy: Remove timer from the exisiting timer_wheel slot
1037 * and reinsert into the new slot.
1038 */
1039 list_remove(&timer->timer_list);
1040 list_add_before(&timer_wheel[timer->timer_clock & TIMER_WHEEL_MASK], &timer->timer_list);
1041
1042 OLSR_PRINTF(7, "TIMER: change %s timer %p, firing to %s, ctx %p\n",
1043 timer->timer_cookie->ci_name, timer, olsr_clock_string(timer->timer_clock), timer->timer_cb_context);
1044 }
1045
1046 /*
1047 * This is the one stop shop for all sort of timer manipulation.
1048 * Depending on the paseed in parameters a new timer is started,
1049 * or an existing timer is started or an existing timer is
1050 * terminated.
1051 */
1052 void
olsr_set_timer(struct timer_entry ** timer_ptr,unsigned int rel_time,uint8_t jitter_pct,bool periodical,timer_cb_func cb_func,void * context,struct olsr_cookie_info * cookie)1053 olsr_set_timer(struct timer_entry **timer_ptr,
1054 unsigned int rel_time,
1055 uint8_t jitter_pct, bool periodical, timer_cb_func cb_func, void *context, struct olsr_cookie_info *cookie)
1056 {
1057 if (cookie) {
1058 cookie = def_timer_ci;
1059 }
1060
1061 if (rel_time == 0) {
1062 /* No good future time provided, kill it. */
1063 olsr_stop_timer(*timer_ptr);
1064 *timer_ptr = NULL;
1065 }
1066 else if ((*timer_ptr) == NULL) {
1067 /* No timer running, kick it. */
1068 *timer_ptr = olsr_start_timer(rel_time, jitter_pct, periodical, cb_func, context, cookie);
1069 }
1070 else {
1071 olsr_change_timer(*timer_ptr, rel_time, jitter_pct, periodical);
1072 }
1073 }
1074
1075 /*
1076 * Local Variables:
1077 * c-basic-offset: 2
1078 * indent-tabs-mode: nil
1079 * End:
1080 */
1081