blob: 9b201de90b21ab9c24943150e068b56e0ef17b8b [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* Thread management routine
2 * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22/* #define DEBUG */
23
24#include <zebra.h>
Denil Vira4ab273b2016-03-04 15:28:54 -050025#include <sys/resource.h>
paul718e3742002-12-13 20:15:29 +000026
27#include "thread.h"
28#include "memory.h"
29#include "log.h"
paule04ab742003-01-17 23:47:00 +000030#include "hash.h"
Christian Franke4becea72013-11-19 14:11:42 +000031#include "pqueue.h"
paule04ab742003-01-17 23:47:00 +000032#include "command.h"
paul05c447d2004-07-22 19:14:27 +000033#include "sigevent.h"
Vincent Bernatd6be5fb2012-05-24 09:44:43 +020034
35#if defined HAVE_SNMP && defined SNMP_AGENTX
36#include <net-snmp/net-snmp-config.h>
37#include <net-snmp/net-snmp-includes.h>
38#include <net-snmp/agent/net-snmp-agent-includes.h>
39#include <net-snmp/agent/snmp_vars.h>
40
41extern int agentx_enabled;
42#endif
43
Hasso Tepper3b96b782012-10-11 11:31:54 +000044#if defined(__APPLE__)
45#include <mach/mach.h>
46#include <mach/mach_time.h>
47#endif
48
David Lamparter6b0655a2014-06-04 06:53:35 +020049
Paul Jakmadb9c0df2006-08-27 06:44:02 +000050/* Recent absolute time of day */
ajs8b70d0b2005-04-28 01:31:13 +000051struct timeval recent_time;
Paul Jakmadb9c0df2006-08-27 06:44:02 +000052static struct timeval last_recent_time;
53/* Relative time, since startup */
54static struct timeval relative_time;
55static struct timeval relative_time_base;
56/* init flag */
57static unsigned short timers_inited;
David Lamparter6b0655a2014-06-04 06:53:35 +020058
paule04ab742003-01-17 23:47:00 +000059static struct hash *cpu_record = NULL;
David Lamparter6b0655a2014-06-04 06:53:35 +020060
paul718e3742002-12-13 20:15:29 +000061/* Struct timeval's tv_usec one second value. */
62#define TIMER_SECOND_MICRO 1000000L
63
ajs8b70d0b2005-04-28 01:31:13 +000064/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
65 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000066static struct timeval
paul718e3742002-12-13 20:15:29 +000067timeval_adjust (struct timeval a)
68{
69 while (a.tv_usec >= TIMER_SECOND_MICRO)
70 {
71 a.tv_usec -= TIMER_SECOND_MICRO;
72 a.tv_sec++;
73 }
74
75 while (a.tv_usec < 0)
76 {
77 a.tv_usec += TIMER_SECOND_MICRO;
78 a.tv_sec--;
79 }
80
81 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000082 /* Change negative timeouts to 0. */
83 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000084
85 return a;
86}
87
88static struct timeval
89timeval_subtract (struct timeval a, struct timeval b)
90{
91 struct timeval ret;
92
93 ret.tv_usec = a.tv_usec - b.tv_usec;
94 ret.tv_sec = a.tv_sec - b.tv_sec;
95
96 return timeval_adjust (ret);
97}
98
ajs8b70d0b2005-04-28 01:31:13 +000099static long
paul718e3742002-12-13 20:15:29 +0000100timeval_cmp (struct timeval a, struct timeval b)
101{
102 return (a.tv_sec == b.tv_sec
103 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
104}
105
Dinesh G Dutt50f38b32014-09-30 12:53:28 -0700106unsigned long
paul718e3742002-12-13 20:15:29 +0000107timeval_elapsed (struct timeval a, struct timeval b)
108{
109 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
110 + (a.tv_usec - b.tv_usec));
111}
David Lamparter6b0655a2014-06-04 06:53:35 +0200112
Hasso Tepper3b96b782012-10-11 11:31:54 +0000113#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__)
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000114static void
115quagga_gettimeofday_relative_adjust (void)
116{
117 struct timeval diff;
118 if (timeval_cmp (recent_time, last_recent_time) < 0)
119 {
120 relative_time.tv_sec++;
121 relative_time.tv_usec = 0;
122 }
123 else
124 {
125 diff = timeval_subtract (recent_time, last_recent_time);
126 relative_time.tv_sec += diff.tv_sec;
127 relative_time.tv_usec += diff.tv_usec;
128 relative_time = timeval_adjust (relative_time);
129 }
130 last_recent_time = recent_time;
131}
Hasso Tepper3b96b782012-10-11 11:31:54 +0000132#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000133
134/* gettimeofday wrapper, to keep recent_time updated */
135static int
136quagga_gettimeofday (struct timeval *tv)
137{
138 int ret;
139
140 assert (tv);
141
142 if (!(ret = gettimeofday (&recent_time, NULL)))
143 {
144 /* init... */
145 if (!timers_inited)
146 {
147 relative_time_base = last_recent_time = recent_time;
148 timers_inited = 1;
149 }
150 /* avoid copy if user passed recent_time pointer.. */
151 if (tv != &recent_time)
152 *tv = recent_time;
153 return 0;
154 }
155 return ret;
156}
157
158static int
159quagga_get_relative (struct timeval *tv)
160{
161 int ret;
162
163#ifdef HAVE_CLOCK_MONOTONIC
164 {
165 struct timespec tp;
166 if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
167 {
168 relative_time.tv_sec = tp.tv_sec;
169 relative_time.tv_usec = tp.tv_nsec / 1000;
170 }
171 }
Hasso Tepper3b96b782012-10-11 11:31:54 +0000172#elif defined(__APPLE__)
173 {
174 uint64_t ticks;
175 uint64_t useconds;
176 static mach_timebase_info_data_t timebase_info;
177
178 ticks = mach_absolute_time();
179 if (timebase_info.denom == 0)
180 mach_timebase_info(&timebase_info);
181
182 useconds = ticks * timebase_info.numer / timebase_info.denom / 1000;
183 relative_time.tv_sec = useconds / 1000000;
184 relative_time.tv_usec = useconds % 1000000;
185
186 return 0;
187 }
188#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000189 if (!(ret = quagga_gettimeofday (&recent_time)))
190 quagga_gettimeofday_relative_adjust();
191#endif /* HAVE_CLOCK_MONOTONIC */
192
193 if (tv)
194 *tv = relative_time;
195
196 return ret;
197}
198
199/* Get absolute time stamp, but in terms of the internal timer
200 * Could be wrong, but at least won't go back.
201 */
202static void
203quagga_real_stabilised (struct timeval *tv)
204{
205 *tv = relative_time_base;
206 tv->tv_sec += relative_time.tv_sec;
207 tv->tv_usec += relative_time.tv_usec;
208 *tv = timeval_adjust (*tv);
209}
210
211/* Exported Quagga timestamp function.
212 * Modelled on POSIX clock_gettime.
213 */
214int
215quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
216{
217 switch (clkid)
218 {
219 case QUAGGA_CLK_REALTIME:
220 return quagga_gettimeofday (tv);
221 case QUAGGA_CLK_MONOTONIC:
222 return quagga_get_relative (tv);
223 case QUAGGA_CLK_REALTIME_STABILISED:
224 quagga_real_stabilised (tv);
225 return 0;
226 default:
227 errno = EINVAL;
228 return -1;
229 }
230}
231
232/* time_t value in terms of stabilised absolute time.
233 * replacement for POSIX time()
234 */
235time_t
236quagga_time (time_t *t)
237{
238 struct timeval tv;
239 quagga_real_stabilised (&tv);
240 if (t)
241 *t = tv.tv_sec;
242 return tv.tv_sec;
243}
244
245/* Public export of recent_relative_time by value */
246struct timeval
247recent_relative_time (void)
248{
249 return relative_time;
250}
David Lamparter6b0655a2014-06-04 06:53:35 +0200251
paula48b4e62005-04-22 00:43:47 +0000252static unsigned int
paule04ab742003-01-17 23:47:00 +0000253cpu_record_hash_key (struct cpu_thread_history *a)
254{
paul8cc41982005-05-06 21:25:49 +0000255 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +0000256}
257
258static int
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100259cpu_record_hash_cmp (const struct cpu_thread_history *a,
260 const struct cpu_thread_history *b)
paule04ab742003-01-17 23:47:00 +0000261{
262 return a->func == b->func;
263}
264
paul8cc41982005-05-06 21:25:49 +0000265static void *
paule04ab742003-01-17 23:47:00 +0000266cpu_record_hash_alloc (struct cpu_thread_history *a)
267{
268 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000269 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000270 new->func = a->func;
David Lamparter3493b772013-11-18 23:04:27 +0100271 new->funcname = a->funcname;
paule04ab742003-01-17 23:47:00 +0000272 return new;
273}
274
Chris Caputo228da422009-07-18 05:44:03 +0000275static void
276cpu_record_hash_free (void *a)
277{
278 struct cpu_thread_history *hist = a;
279
Chris Caputo228da422009-07-18 05:44:03 +0000280 XFREE (MTYPE_THREAD_STATS, hist);
281}
282
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100283static void
paule04ab742003-01-17 23:47:00 +0000284vty_out_cpu_thread_history(struct vty* vty,
285 struct cpu_thread_history *a)
286{
ajs8b70d0b2005-04-28 01:31:13 +0000287#ifdef HAVE_RUSAGE
288 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
289 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
290 a->cpu.total/a->total_calls, a->cpu.max,
291 a->real.total/a->total_calls, a->real.max);
292#else
293 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
294 a->real.total/1000, a->real.total%1000, a->total_calls,
295 a->real.total/a->total_calls, a->real.max);
296#endif
297 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000298 a->types & (1 << THREAD_READ) ? 'R':' ',
299 a->types & (1 << THREAD_WRITE) ? 'W':' ',
300 a->types & (1 << THREAD_TIMER) ? 'T':' ',
301 a->types & (1 << THREAD_EVENT) ? 'E':' ',
302 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000303 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000304 a->funcname, VTY_NEWLINE);
305}
306
307static void
308cpu_record_hash_print(struct hash_backet *bucket,
309 void *args[])
310{
311 struct cpu_thread_history *totals = args[0];
312 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100313 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000314 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000315
paule04ab742003-01-17 23:47:00 +0000316 a = bucket->data;
317 if ( !(a->types & *filter) )
318 return;
319 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000320 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000321 totals->real.total += a->real.total;
322 if (totals->real.max < a->real.max)
323 totals->real.max = a->real.max;
324#ifdef HAVE_RUSAGE
325 totals->cpu.total += a->cpu.total;
326 if (totals->cpu.max < a->cpu.max)
327 totals->cpu.max = a->cpu.max;
328#endif
paule04ab742003-01-17 23:47:00 +0000329}
330
331static void
Paul Jakma41b23732009-06-30 16:12:49 +0100332cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000333{
334 struct cpu_thread_history tmp;
335 void *args[3] = {&tmp, vty, &filter};
336
337 memset(&tmp, 0, sizeof tmp);
David Lamparter3493b772013-11-18 23:04:27 +0100338 tmp.funcname = "TOTAL";
paule04ab742003-01-17 23:47:00 +0000339 tmp.types = filter;
340
ajs8b70d0b2005-04-28 01:31:13 +0000341#ifdef HAVE_RUSAGE
342 vty_out(vty, "%21s %18s %18s%s",
343 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
344#endif
345 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
346#ifdef HAVE_RUSAGE
347 vty_out(vty, " Avg uSec Max uSecs");
348#endif
349 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000350 hash_iterate(cpu_record,
351 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
352 args);
353
354 if (tmp.total_calls > 0)
355 vty_out_cpu_thread_history(vty, &tmp);
356}
357
358DEFUN(show_thread_cpu,
359 show_thread_cpu_cmd,
360 "show thread cpu [FILTER]",
361 SHOW_STR
362 "Thread information\n"
363 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000364 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000365{
366 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100367 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000368
369 if (argc > 0)
370 {
371 filter = 0;
372 while (argv[0][i] != '\0')
373 {
374 switch ( argv[0][i] )
375 {
376 case 'r':
377 case 'R':
378 filter |= (1 << THREAD_READ);
379 break;
380 case 'w':
381 case 'W':
382 filter |= (1 << THREAD_WRITE);
383 break;
384 case 't':
385 case 'T':
386 filter |= (1 << THREAD_TIMER);
387 break;
388 case 'e':
389 case 'E':
390 filter |= (1 << THREAD_EVENT);
391 break;
392 case 'x':
393 case 'X':
394 filter |= (1 << THREAD_EXECUTE);
395 break;
paula48b4e62005-04-22 00:43:47 +0000396 case 'b':
397 case 'B':
398 filter |= (1 << THREAD_BACKGROUND);
399 break;
paule04ab742003-01-17 23:47:00 +0000400 default:
401 break;
402 }
403 ++i;
404 }
405 if (filter == 0)
406 {
paula48b4e62005-04-22 00:43:47 +0000407 vty_out(vty, "Invalid filter \"%s\" specified,"
408 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000409 argv[0], VTY_NEWLINE);
410 return CMD_WARNING;
411 }
412 }
413
414 cpu_record_print(vty, filter);
415 return CMD_SUCCESS;
416}
Paul Jakmae276eb82010-01-09 16:15:00 +0000417
418static void
419cpu_record_hash_clear (struct hash_backet *bucket,
420 void *args)
421{
422 thread_type *filter = args;
423 struct cpu_thread_history *a = bucket->data;
424
425 a = bucket->data;
426 if ( !(a->types & *filter) )
427 return;
428
429 hash_release (cpu_record, bucket->data);
430}
431
432static void
433cpu_record_clear (thread_type filter)
434{
435 thread_type *tmp = &filter;
436 hash_iterate (cpu_record,
437 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
438 tmp);
439}
440
441DEFUN(clear_thread_cpu,
442 clear_thread_cpu_cmd,
443 "clear thread cpu [FILTER]",
444 "Clear stored data\n"
445 "Thread information\n"
446 "Thread CPU usage\n"
447 "Display filter (rwtexb)\n")
448{
449 int i = 0;
450 thread_type filter = (thread_type) -1U;
451
452 if (argc > 0)
453 {
454 filter = 0;
455 while (argv[0][i] != '\0')
456 {
457 switch ( argv[0][i] )
458 {
459 case 'r':
460 case 'R':
461 filter |= (1 << THREAD_READ);
462 break;
463 case 'w':
464 case 'W':
465 filter |= (1 << THREAD_WRITE);
466 break;
467 case 't':
468 case 'T':
469 filter |= (1 << THREAD_TIMER);
470 break;
471 case 'e':
472 case 'E':
473 filter |= (1 << THREAD_EVENT);
474 break;
475 case 'x':
476 case 'X':
477 filter |= (1 << THREAD_EXECUTE);
478 break;
479 case 'b':
480 case 'B':
481 filter |= (1 << THREAD_BACKGROUND);
482 break;
483 default:
484 break;
485 }
486 ++i;
487 }
488 if (filter == 0)
489 {
490 vty_out(vty, "Invalid filter \"%s\" specified,"
491 " must contain at least one of 'RWTEXB'%s",
492 argv[0], VTY_NEWLINE);
493 return CMD_WARNING;
494 }
495 }
496
497 cpu_record_clear (filter);
498 return CMD_SUCCESS;
499}
David Lamparter6b0655a2014-06-04 06:53:35 +0200500
Christian Franke4becea72013-11-19 14:11:42 +0000501static int
502thread_timer_cmp(void *a, void *b)
503{
504 struct thread *thread_a = a;
505 struct thread *thread_b = b;
506
507 long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands);
508
509 if (cmp < 0)
510 return -1;
511 if (cmp > 0)
512 return 1;
513 return 0;
514}
515
516static void
517thread_timer_update(void *node, int actual_position)
518{
519 struct thread *thread = node;
520
521 thread->index = actual_position;
522}
523
paul718e3742002-12-13 20:15:29 +0000524/* Allocate new thread master. */
525struct thread_master *
526thread_master_create ()
527{
Christian Franke4becea72013-11-19 14:11:42 +0000528 struct thread_master *rv;
Denil Vira4ab273b2016-03-04 15:28:54 -0500529 struct rlimit limit;
530
531 getrlimit(RLIMIT_NOFILE, &limit);
Christian Franke4becea72013-11-19 14:11:42 +0000532
paule04ab742003-01-17 23:47:00 +0000533 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000534 cpu_record
Stephen Hemminger90645f52013-01-04 22:29:21 +0000535 = hash_create ((unsigned int (*) (void *))cpu_record_hash_key,
536 (int (*) (const void *, const void *))cpu_record_hash_cmp);
Christian Franke4becea72013-11-19 14:11:42 +0000537
538 rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master));
Denil Vira4ab273b2016-03-04 15:28:54 -0500539 if (rv == NULL)
540 {
541 return NULL;
542 }
543
544 rv->fd_limit = (int)limit.rlim_cur;
545 rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit);
546 if (rv->read == NULL)
547 {
548 XFREE (MTYPE_THREAD_MASTER, rv);
549 return NULL;
550 }
551
552 rv->write = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit);
553 if (rv->write == NULL)
554 {
555 XFREE (MTYPE_THREAD, rv->read);
556 XFREE (MTYPE_THREAD_MASTER, rv);
557 return NULL;
558 }
Christian Franke4becea72013-11-19 14:11:42 +0000559
560 /* Initialize the timer queues */
561 rv->timer = pqueue_create();
562 rv->background = pqueue_create();
563 rv->timer->cmp = rv->background->cmp = thread_timer_cmp;
564 rv->timer->update = rv->background->update = thread_timer_update;
565
566 return rv;
paul718e3742002-12-13 20:15:29 +0000567}
568
569/* Add a new thread to the list. */
570static void
571thread_list_add (struct thread_list *list, struct thread *thread)
572{
573 thread->next = NULL;
574 thread->prev = list->tail;
575 if (list->tail)
576 list->tail->next = thread;
577 else
578 list->head = thread;
579 list->tail = thread;
580 list->count++;
581}
582
paul718e3742002-12-13 20:15:29 +0000583/* Delete a thread from the list. */
584static struct thread *
585thread_list_delete (struct thread_list *list, struct thread *thread)
586{
587 if (thread->next)
588 thread->next->prev = thread->prev;
589 else
590 list->tail = thread->prev;
591 if (thread->prev)
592 thread->prev->next = thread->next;
593 else
594 list->head = thread->next;
595 thread->next = thread->prev = NULL;
596 list->count--;
597 return thread;
598}
599
Denil Vira4ab273b2016-03-04 15:28:54 -0500600static void
601thread_delete_fd (struct thread **thread_array, struct thread *thread)
602{
603 thread_array[thread->u.fd] = NULL;
604}
605
606static void
607thread_add_fd (struct thread **thread_array, struct thread *thread)
608{
609 thread_array[thread->u.fd] = thread;
610}
611
paul718e3742002-12-13 20:15:29 +0000612/* Move thread to unuse list. */
613static void
614thread_add_unuse (struct thread_master *m, struct thread *thread)
615{
paula48b4e62005-04-22 00:43:47 +0000616 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000617 assert (thread->next == NULL);
618 assert (thread->prev == NULL);
619 assert (thread->type == THREAD_UNUSED);
620 thread_list_add (&m->unuse, thread);
621}
622
623/* Free all unused thread. */
624static void
625thread_list_free (struct thread_master *m, struct thread_list *list)
626{
627 struct thread *t;
628 struct thread *next;
629
630 for (t = list->head; t; t = next)
631 {
632 next = t->next;
633 XFREE (MTYPE_THREAD, t);
634 list->count--;
635 m->alloc--;
636 }
637}
638
Christian Franke4becea72013-11-19 14:11:42 +0000639static void
Denil Vira4ab273b2016-03-04 15:28:54 -0500640thread_array_free (struct thread_master *m, struct thread **thread_array)
641{
642 struct thread *t;
643 int index;
644
645 for (index = 0; index < m->fd_limit; ++index)
646 {
647 t = thread_array[index];
648 if (t)
649 {
650 thread_array[index] = NULL;
651 XFREE (MTYPE_THREAD, t);
652 m->alloc--;
653 }
654 }
655 XFREE (MTYPE_THREAD, thread_array);
656}
657
658static void
Christian Franke4becea72013-11-19 14:11:42 +0000659thread_queue_free (struct thread_master *m, struct pqueue *queue)
660{
661 int i;
662
663 for (i = 0; i < queue->size; i++)
664 XFREE(MTYPE_THREAD, queue->array[i]);
665
666 m->alloc -= queue->size;
667 pqueue_delete(queue);
668}
669
paul718e3742002-12-13 20:15:29 +0000670/* Stop thread scheduler. */
671void
672thread_master_free (struct thread_master *m)
673{
Denil Vira4ab273b2016-03-04 15:28:54 -0500674 thread_array_free (m, m->read);
675 thread_array_free (m, m->write);
Christian Franke4becea72013-11-19 14:11:42 +0000676 thread_queue_free (m, m->timer);
paul718e3742002-12-13 20:15:29 +0000677 thread_list_free (m, &m->event);
678 thread_list_free (m, &m->ready);
679 thread_list_free (m, &m->unuse);
Christian Franke4becea72013-11-19 14:11:42 +0000680 thread_queue_free (m, m->background);
paula48b4e62005-04-22 00:43:47 +0000681
paul718e3742002-12-13 20:15:29 +0000682 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000683
684 if (cpu_record)
685 {
686 hash_clean (cpu_record, cpu_record_hash_free);
687 hash_free (cpu_record);
688 cpu_record = NULL;
689 }
paul718e3742002-12-13 20:15:29 +0000690}
691
paul8cc41982005-05-06 21:25:49 +0000692/* Thread list is empty or not. */
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100693static int
paul8cc41982005-05-06 21:25:49 +0000694thread_empty (struct thread_list *list)
695{
696 return list->head ? 0 : 1;
697}
698
paul718e3742002-12-13 20:15:29 +0000699/* Delete top of the list and return it. */
700static struct thread *
701thread_trim_head (struct thread_list *list)
702{
paul8cc41982005-05-06 21:25:49 +0000703 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000704 return thread_list_delete (list, list->head);
705 return NULL;
706}
707
paul718e3742002-12-13 20:15:29 +0000708/* Return remain time in second. */
709unsigned long
710thread_timer_remain_second (struct thread *thread)
711{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000712 quagga_get_relative (NULL);
713
714 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
715 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000716 else
717 return 0;
718}
719
Christian Franke76fbc642015-11-10 18:04:41 +0100720struct timeval
721thread_timer_remain(struct thread *thread)
722{
723 quagga_get_relative(NULL);
724
725 return timeval_subtract(thread->u.sands, relative_time);
726}
727
David Lamparter3493b772013-11-18 23:04:27 +0100728#define debugargdef const char *funcname, const char *schedfrom, int fromln
729#define debugargpass funcname, schedfrom, fromln
paule04ab742003-01-17 23:47:00 +0000730
paul718e3742002-12-13 20:15:29 +0000731/* Get new thread. */
732static struct thread *
733thread_get (struct thread_master *m, u_char type,
David Lamparter3493b772013-11-18 23:04:27 +0100734 int (*func) (struct thread *), void *arg, debugargdef)
paul718e3742002-12-13 20:15:29 +0000735{
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000736 struct thread *thread = thread_trim_head (&m->unuse);
paul718e3742002-12-13 20:15:29 +0000737
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000738 if (! thread)
paul718e3742002-12-13 20:15:29 +0000739 {
740 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
741 m->alloc++;
742 }
743 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000744 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000745 thread->master = m;
746 thread->func = func;
747 thread->arg = arg;
Christian Franke4becea72013-11-19 14:11:42 +0000748 thread->index = -1;
749
David Lamparter3493b772013-11-18 23:04:27 +0100750 thread->funcname = funcname;
751 thread->schedfrom = schedfrom;
752 thread->schedfrom_line = fromln;
paule04ab742003-01-17 23:47:00 +0000753
paul718e3742002-12-13 20:15:29 +0000754 return thread;
755}
756
Donald Sharp19be18a2016-03-04 15:28:55 -0500757#define fd_copy_fd_set(X) (X)
758
759static int
760fd_select (int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *t)
761{
762 return(select(size, read, write, except, t));
763}
764
765static int
766fd_is_set (int fd, thread_fd_set *fdset)
767{
768 return FD_ISSET (fd, fdset);
769}
770
771static int
772fd_set_read_write (int fd, thread_fd_set *fdset)
773{
774 if (FD_ISSET (fd, fdset))
775 return 0;
776
777 FD_SET (fd, fdset);
778
779 return 1;
780}
781
782static int
783fd_clear_read_write (int fd, thread_fd_set *fdset)
784{
785 if (!FD_ISSET (fd, fdset))
786 return 0;
787
788 FD_CLR (fd, fdset);
789 return 1;
790}
791
paul718e3742002-12-13 20:15:29 +0000792/* Add new read thread. */
793struct thread *
paule04ab742003-01-17 23:47:00 +0000794funcname_thread_add_read (struct thread_master *m,
David Lamparter3493b772013-11-18 23:04:27 +0100795 int (*func) (struct thread *), void *arg, int fd,
796 debugargdef)
paul718e3742002-12-13 20:15:29 +0000797{
798 struct thread *thread;
799
800 assert (m != NULL);
801
Donald Sharp19be18a2016-03-04 15:28:55 -0500802 if (!fd_set_read_write (fd, &m->readfd))
paul718e3742002-12-13 20:15:29 +0000803 {
804 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
805 return NULL;
806 }
807
David Lamparter3493b772013-11-18 23:04:27 +0100808 thread = thread_get (m, THREAD_READ, func, arg, debugargpass);
paul718e3742002-12-13 20:15:29 +0000809 thread->u.fd = fd;
Denil Vira4ab273b2016-03-04 15:28:54 -0500810 thread_add_fd (m->read, thread);
paul718e3742002-12-13 20:15:29 +0000811
812 return thread;
813}
814
815/* Add new write thread. */
816struct thread *
paule04ab742003-01-17 23:47:00 +0000817funcname_thread_add_write (struct thread_master *m,
David Lamparter3493b772013-11-18 23:04:27 +0100818 int (*func) (struct thread *), void *arg, int fd,
819 debugargdef)
paul718e3742002-12-13 20:15:29 +0000820{
821 struct thread *thread;
822
823 assert (m != NULL);
824
Donald Sharp19be18a2016-03-04 15:28:55 -0500825 if (!fd_set_read_write (fd, &m->writefd))
paul718e3742002-12-13 20:15:29 +0000826 {
827 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
828 return NULL;
829 }
830
David Lamparter3493b772013-11-18 23:04:27 +0100831 thread = thread_get (m, THREAD_WRITE, func, arg, debugargpass);
paul718e3742002-12-13 20:15:29 +0000832 thread->u.fd = fd;
Denil Vira4ab273b2016-03-04 15:28:54 -0500833 thread_add_fd (m->write, thread);
paul718e3742002-12-13 20:15:29 +0000834
835 return thread;
836}
837
paul98c91ac2004-10-05 14:57:50 +0000838static struct thread *
839funcname_thread_add_timer_timeval (struct thread_master *m,
840 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000841 int type,
paul98c91ac2004-10-05 14:57:50 +0000842 void *arg,
David Lamparter3493b772013-11-18 23:04:27 +0100843 struct timeval *time_relative,
844 debugargdef)
paul718e3742002-12-13 20:15:29 +0000845{
paul718e3742002-12-13 20:15:29 +0000846 struct thread *thread;
Christian Franke4becea72013-11-19 14:11:42 +0000847 struct pqueue *queue;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000848 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000849
850 assert (m != NULL);
851
ajs8b70d0b2005-04-28 01:31:13 +0000852 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000853 assert (time_relative);
854
Christian Franke4becea72013-11-19 14:11:42 +0000855 queue = ((type == THREAD_TIMER) ? m->timer : m->background);
David Lamparter3493b772013-11-18 23:04:27 +0100856 thread = thread_get (m, type, func, arg, debugargpass);
paul718e3742002-12-13 20:15:29 +0000857
858 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100859 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000860 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
861 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000862 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000863
Christian Franke4becea72013-11-19 14:11:42 +0000864 pqueue_enqueue(thread, queue);
paul718e3742002-12-13 20:15:29 +0000865 return thread;
866}
867
paul98c91ac2004-10-05 14:57:50 +0000868
869/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000870struct thread *
paul98c91ac2004-10-05 14:57:50 +0000871funcname_thread_add_timer (struct thread_master *m,
872 int (*func) (struct thread *),
David Lamparter3493b772013-11-18 23:04:27 +0100873 void *arg, long timer,
874 debugargdef)
jardin9e867fe2003-12-23 08:56:18 +0000875{
paul98c91ac2004-10-05 14:57:50 +0000876 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000877
878 assert (m != NULL);
879
paul9076fbd2004-10-11 09:40:58 +0000880 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000881 trel.tv_usec = 0;
882
paula48b4e62005-04-22 00:43:47 +0000883 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
David Lamparter3493b772013-11-18 23:04:27 +0100884 &trel, debugargpass);
paul98c91ac2004-10-05 14:57:50 +0000885}
886
887/* Add timer event thread with "millisecond" resolution */
888struct thread *
889funcname_thread_add_timer_msec (struct thread_master *m,
890 int (*func) (struct thread *),
David Lamparter3493b772013-11-18 23:04:27 +0100891 void *arg, long timer,
892 debugargdef)
paul98c91ac2004-10-05 14:57:50 +0000893{
894 struct timeval trel;
895
896 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000897
ajsaf04bd72004-12-28 17:00:12 +0000898 trel.tv_sec = timer / 1000;
899 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000900
paula48b4e62005-04-22 00:43:47 +0000901 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
David Lamparter3493b772013-11-18 23:04:27 +0100902 arg, &trel, debugargpass);
paula48b4e62005-04-22 00:43:47 +0000903}
904
905/* Add a background thread, with an optional millisec delay */
906struct thread *
907funcname_thread_add_background (struct thread_master *m,
908 int (*func) (struct thread *),
David Lamparter3493b772013-11-18 23:04:27 +0100909 void *arg, long delay,
910 debugargdef)
paula48b4e62005-04-22 00:43:47 +0000911{
912 struct timeval trel;
913
914 assert (m != NULL);
915
916 if (delay)
917 {
918 trel.tv_sec = delay / 1000;
919 trel.tv_usec = 1000*(delay % 1000);
920 }
921 else
922 {
923 trel.tv_sec = 0;
924 trel.tv_usec = 0;
925 }
926
927 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
David Lamparter3493b772013-11-18 23:04:27 +0100928 arg, &trel, debugargpass);
jardin9e867fe2003-12-23 08:56:18 +0000929}
930
paul718e3742002-12-13 20:15:29 +0000931/* Add simple event thread. */
932struct thread *
paule04ab742003-01-17 23:47:00 +0000933funcname_thread_add_event (struct thread_master *m,
David Lamparter3493b772013-11-18 23:04:27 +0100934 int (*func) (struct thread *), void *arg, int val,
935 debugargdef)
paul718e3742002-12-13 20:15:29 +0000936{
937 struct thread *thread;
938
939 assert (m != NULL);
940
David Lamparter3493b772013-11-18 23:04:27 +0100941 thread = thread_get (m, THREAD_EVENT, func, arg, debugargpass);
paul718e3742002-12-13 20:15:29 +0000942 thread->u.val = val;
943 thread_list_add (&m->event, thread);
944
945 return thread;
946}
947
948/* Cancel thread from scheduler. */
949void
950thread_cancel (struct thread *thread)
951{
Christian Franke4becea72013-11-19 14:11:42 +0000952 struct thread_list *list = NULL;
953 struct pqueue *queue = NULL;
Denil Vira4ab273b2016-03-04 15:28:54 -0500954 struct thread **thread_array = NULL;
paula48b4e62005-04-22 00:43:47 +0000955
paul718e3742002-12-13 20:15:29 +0000956 switch (thread->type)
957 {
958 case THREAD_READ:
Donald Sharp19be18a2016-03-04 15:28:55 -0500959 assert (fd_clear_read_write (thread->u.fd, &thread->master->readfd));
Denil Vira4ab273b2016-03-04 15:28:54 -0500960 thread_array = thread->master->read;
paul718e3742002-12-13 20:15:29 +0000961 break;
962 case THREAD_WRITE:
Donald Sharp19be18a2016-03-04 15:28:55 -0500963 assert (fd_clear_read_write (thread->u.fd, &thread->master->writefd));
Denil Vira4ab273b2016-03-04 15:28:54 -0500964 thread_array = thread->master->write;
paul718e3742002-12-13 20:15:29 +0000965 break;
966 case THREAD_TIMER:
Christian Franke4becea72013-11-19 14:11:42 +0000967 queue = thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000968 break;
969 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000970 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000971 break;
972 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000973 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000974 break;
paula48b4e62005-04-22 00:43:47 +0000975 case THREAD_BACKGROUND:
Christian Franke4becea72013-11-19 14:11:42 +0000976 queue = thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000977 break;
paul718e3742002-12-13 20:15:29 +0000978 default:
paula48b4e62005-04-22 00:43:47 +0000979 return;
paul718e3742002-12-13 20:15:29 +0000980 break;
981 }
Christian Franke4becea72013-11-19 14:11:42 +0000982
983 if (queue)
984 {
985 assert(thread->index >= 0);
986 assert(thread == queue->array[thread->index]);
987 pqueue_remove_at(thread->index, queue);
988 }
989 else if (list)
990 {
991 thread_list_delete (list, thread);
992 }
Denil Vira4ab273b2016-03-04 15:28:54 -0500993 else if (thread_array)
994 {
995 thread_delete_fd (thread_array, thread);
996 }
Christian Franke4becea72013-11-19 14:11:42 +0000997 else
998 {
Denil Vira4ab273b2016-03-04 15:28:54 -0500999 assert(!"Thread should be either in queue or list or array!");
Christian Franke4becea72013-11-19 14:11:42 +00001000 }
1001
paul718e3742002-12-13 20:15:29 +00001002 thread->type = THREAD_UNUSED;
1003 thread_add_unuse (thread->master, thread);
1004}
1005
1006/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +00001007unsigned int
paul718e3742002-12-13 20:15:29 +00001008thread_cancel_event (struct thread_master *m, void *arg)
1009{
pauldc818072005-05-19 01:30:53 +00001010 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +00001011 struct thread *thread;
1012
1013 thread = m->event.head;
1014 while (thread)
1015 {
1016 struct thread *t;
1017
1018 t = thread;
1019 thread = t->next;
1020
1021 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +00001022 {
pauldc818072005-05-19 01:30:53 +00001023 ret++;
paula48b4e62005-04-22 00:43:47 +00001024 thread_list_delete (&m->event, t);
1025 t->type = THREAD_UNUSED;
1026 thread_add_unuse (m, t);
1027 }
paul718e3742002-12-13 20:15:29 +00001028 }
Jorge Boncompte [DTI2]1b79fcb2012-05-07 15:17:31 +00001029
1030 /* thread can be on the ready list too */
1031 thread = m->ready.head;
1032 while (thread)
1033 {
1034 struct thread *t;
1035
1036 t = thread;
1037 thread = t->next;
1038
1039 if (t->arg == arg)
1040 {
1041 ret++;
1042 thread_list_delete (&m->ready, t);
1043 t->type = THREAD_UNUSED;
1044 thread_add_unuse (m, t);
1045 }
1046 }
pauldc818072005-05-19 01:30:53 +00001047 return ret;
paul718e3742002-12-13 20:15:29 +00001048}
1049
paula48b4e62005-04-22 00:43:47 +00001050static struct timeval *
Christian Franke4becea72013-11-19 14:11:42 +00001051thread_timer_wait (struct pqueue *queue, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +00001052{
Christian Franke4becea72013-11-19 14:11:42 +00001053 if (queue->size)
paul718e3742002-12-13 20:15:29 +00001054 {
Christian Franke4becea72013-11-19 14:11:42 +00001055 struct thread *next_timer = queue->array[0];
1056 *timer_val = timeval_subtract (next_timer->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +00001057 return timer_val;
1058 }
1059 return NULL;
1060}
paul718e3742002-12-13 20:15:29 +00001061
paul8cc41982005-05-06 21:25:49 +00001062static struct thread *
paul718e3742002-12-13 20:15:29 +00001063thread_run (struct thread_master *m, struct thread *thread,
1064 struct thread *fetch)
1065{
1066 *fetch = *thread;
1067 thread->type = THREAD_UNUSED;
1068 thread_add_unuse (m, thread);
1069 return fetch;
1070}
1071
paula48b4e62005-04-22 00:43:47 +00001072static int
Donald Sharp19be18a2016-03-04 15:28:55 -05001073thread_process_fd (struct thread **thread_array, thread_fd_set *fdset,
1074 thread_fd_set *mfdset, int num, int fd_limit)
paul718e3742002-12-13 20:15:29 +00001075{
1076 struct thread *thread;
Denil Vira4ab273b2016-03-04 15:28:54 -05001077 int ready = 0, index;
paul718e3742002-12-13 20:15:29 +00001078
Denil Vira4ab273b2016-03-04 15:28:54 -05001079 assert (thread_array);
1080
1081 for (index = 0; index < fd_limit && ready < num; ++index)
1082 {
1083 thread = thread_array[index];
Donald Sharp19be18a2016-03-04 15:28:55 -05001084 if (thread && fd_is_set (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +00001085 {
Donald Sharp19be18a2016-03-04 15:28:55 -05001086 assert (fd_clear_read_write (THREAD_FD (thread), mfdset));
Denil Vira4ab273b2016-03-04 15:28:54 -05001087 thread_delete_fd (thread_array, thread);
paula48b4e62005-04-22 00:43:47 +00001088 thread_list_add (&thread->master->ready, thread);
1089 thread->type = THREAD_READY;
1090 ready++;
1091 }
paul718e3742002-12-13 20:15:29 +00001092 }
Denil Vira4ab273b2016-03-04 15:28:54 -05001093 return num - ready;
paul718e3742002-12-13 20:15:29 +00001094}
1095
ajs8b70d0b2005-04-28 01:31:13 +00001096/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +00001097static unsigned int
Christian Franke4becea72013-11-19 14:11:42 +00001098thread_timer_process (struct pqueue *queue, struct timeval *timenow)
paula48b4e62005-04-22 00:43:47 +00001099{
1100 struct thread *thread;
1101 unsigned int ready = 0;
1102
Christian Franke4becea72013-11-19 14:11:42 +00001103 while (queue->size)
ajs8b70d0b2005-04-28 01:31:13 +00001104 {
Christian Franke4becea72013-11-19 14:11:42 +00001105 thread = queue->array[0];
ajs8b70d0b2005-04-28 01:31:13 +00001106 if (timeval_cmp (*timenow, thread->u.sands) < 0)
1107 return ready;
Christian Franke4becea72013-11-19 14:11:42 +00001108 pqueue_dequeue(queue);
ajs8b70d0b2005-04-28 01:31:13 +00001109 thread->type = THREAD_READY;
1110 thread_list_add (&thread->master->ready, thread);
1111 ready++;
1112 }
paula48b4e62005-04-22 00:43:47 +00001113 return ready;
1114}
1115
Paul Jakma2613abe2010-01-11 16:33:07 +00001116/* process a list en masse, e.g. for event thread lists */
1117static unsigned int
1118thread_process (struct thread_list *list)
1119{
1120 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +00001121 struct thread *next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001122 unsigned int ready = 0;
1123
Paul Jakmab5043aa2012-02-28 18:32:56 +00001124 for (thread = list->head; thread; thread = next)
Paul Jakma2613abe2010-01-11 16:33:07 +00001125 {
Paul Jakmab5043aa2012-02-28 18:32:56 +00001126 next = thread->next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001127 thread_list_delete (list, thread);
1128 thread->type = THREAD_READY;
1129 thread_list_add (&thread->master->ready, thread);
1130 ready++;
1131 }
1132 return ready;
1133}
1134
1135
paul718e3742002-12-13 20:15:29 +00001136/* Fetch next ready thread. */
1137struct thread *
1138thread_fetch (struct thread_master *m, struct thread *fetch)
1139{
paul718e3742002-12-13 20:15:29 +00001140 struct thread *thread;
Donald Sharp19be18a2016-03-04 15:28:55 -05001141 thread_fd_set readfd;
1142 thread_fd_set writefd;
1143 thread_fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001144 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001145 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001146 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001147 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001148
1149 while (1)
1150 {
paula48b4e62005-04-22 00:43:47 +00001151 int num = 0;
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001152#if defined HAVE_SNMP && defined SNMP_AGENTX
1153 struct timeval snmp_timer_wait;
1154 int snmpblock = 0;
1155 int fdsetsize;
1156#endif
paula48b4e62005-04-22 00:43:47 +00001157
Paul Jakma2613abe2010-01-11 16:33:07 +00001158 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001159 quagga_sigevent_process ();
1160
Paul Jakma2613abe2010-01-11 16:33:07 +00001161 /* Drain the ready queue of already scheduled jobs, before scheduling
1162 * more.
paula48b4e62005-04-22 00:43:47 +00001163 */
paul718e3742002-12-13 20:15:29 +00001164 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001165 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001166
Paul Jakma2613abe2010-01-11 16:33:07 +00001167 /* To be fair to all kinds of threads, and avoid starvation, we
1168 * need to be careful to consider all thread types for scheduling
1169 * in each quanta. I.e. we should not return early from here on.
1170 */
1171
1172 /* Normal event are the next highest priority. */
1173 thread_process (&m->event);
1174
paul718e3742002-12-13 20:15:29 +00001175 /* Structure copy. */
Donald Sharp19be18a2016-03-04 15:28:55 -05001176 readfd = fd_copy_fd_set(m->readfd);
1177 writefd = fd_copy_fd_set(m->writefd);
1178 exceptfd = fd_copy_fd_set(m->exceptfd);
paula48b4e62005-04-22 00:43:47 +00001179
1180 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001181 if (m->ready.count == 0)
1182 {
1183 quagga_get_relative (NULL);
Christian Franke4becea72013-11-19 14:11:42 +00001184 timer_wait = thread_timer_wait (m->timer, &timer_val);
1185 timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg);
Paul Jakma2613abe2010-01-11 16:33:07 +00001186
1187 if (timer_wait_bg &&
1188 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1189 timer_wait = timer_wait_bg;
1190 }
paula48b4e62005-04-22 00:43:47 +00001191
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001192#if defined HAVE_SNMP && defined SNMP_AGENTX
1193 /* When SNMP is enabled, we may have to select() on additional
1194 FD. snmp_select_info() will add them to `readfd'. The trick
1195 with this function is its last argument. We need to set it to
1196 0 if timer_wait is not NULL and we need to use the provided
1197 new timer only if it is still set to 0. */
1198 if (agentx_enabled)
1199 {
1200 fdsetsize = FD_SETSIZE;
1201 snmpblock = 1;
1202 if (timer_wait)
1203 {
1204 snmpblock = 0;
1205 memcpy(&snmp_timer_wait, timer_wait, sizeof(struct timeval));
1206 }
1207 snmp_select_info(&fdsetsize, &readfd, &snmp_timer_wait, &snmpblock);
1208 if (snmpblock == 0)
1209 timer_wait = &snmp_timer_wait;
1210 }
1211#endif
Donald Sharp19be18a2016-03-04 15:28:55 -05001212 num = fd_select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001213
1214 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001215 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001216 {
1217 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001218 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001219 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001220 return NULL;
1221 }
ajs8b70d0b2005-04-28 01:31:13 +00001222
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001223#if defined HAVE_SNMP && defined SNMP_AGENTX
1224 if (agentx_enabled)
1225 {
1226 if (num > 0)
1227 snmp_read(&readfd);
1228 else if (num == 0)
1229 {
1230 snmp_timeout();
1231 run_alarms();
1232 }
1233 netsnmp_check_outstanding_agent_requests();
1234 }
1235#endif
1236
ajs8b70d0b2005-04-28 01:31:13 +00001237 /* Check foreground timers. Historically, they have had higher
1238 priority than I/O threads, so let's push them onto the ready
1239 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001240 quagga_get_relative (NULL);
Christian Franke4becea72013-11-19 14:11:42 +00001241 thread_timer_process (m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001242
1243 /* Got IO, process it */
1244 if (num > 0)
1245 {
Denil Vira4ab273b2016-03-04 15:28:54 -05001246 /* Normal priority read thread. */
1247 num = thread_process_fd (m->read, &readfd, &m->readfd, num, m->fd_limit);
1248 /* Write thread. */
1249 num = thread_process_fd (m->write, &writefd, &m->writefd, num, m->fd_limit);
paula48b4e62005-04-22 00:43:47 +00001250 }
ajs8b70d0b2005-04-28 01:31:13 +00001251
1252#if 0
1253 /* If any threads were made ready above (I/O or foreground timer),
1254 perhaps we should avoid adding background timers to the ready
1255 list at this time. If this is code is uncommented, then background
1256 timer threads will not run unless there is nothing else to do. */
1257 if ((thread = thread_trim_head (&m->ready)) != NULL)
1258 return thread_run (m, thread, fetch);
1259#endif
1260
paula48b4e62005-04-22 00:43:47 +00001261 /* Background timer/events, lowest priority */
Christian Franke4becea72013-11-19 14:11:42 +00001262 thread_timer_process (m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001263
ajs8b70d0b2005-04-28 01:31:13 +00001264 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001265 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001266 }
1267}
1268
ajs924b9222005-04-16 17:11:24 +00001269unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001270thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001271{
paul718e3742002-12-13 20:15:29 +00001272#ifdef HAVE_RUSAGE
1273 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001274 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1275 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001276#else
ajs8b70d0b2005-04-28 01:31:13 +00001277 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001278#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001279 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001280}
1281
ajs8b70d0b2005-04-28 01:31:13 +00001282/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1283 Note: we are using real (wall clock) time for this calculation.
1284 It could be argued that CPU time may make more sense in certain
1285 contexts. The things to consider are whether the thread may have
1286 blocked (in which case wall time increases, but CPU time does not),
1287 or whether the system is heavily loaded with other processes competing
1288 for CPU time. On balance, wall clock time seems to make sense.
1289 Plus it has the added benefit that gettimeofday should be faster
1290 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001291int
1292thread_should_yield (struct thread *thread)
1293{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001294 quagga_get_relative (NULL);
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001295 return (timeval_elapsed(relative_time, thread->real) >
ajs8b70d0b2005-04-28 01:31:13 +00001296 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001297}
1298
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001299void
1300thread_getrusage (RUSAGE_T *r)
1301{
1302 quagga_get_relative (NULL);
1303#ifdef HAVE_RUSAGE
1304 getrusage(RUSAGE_SELF, &(r->cpu));
1305#endif
1306 r->real = relative_time;
1307
1308#ifdef HAVE_CLOCK_MONOTONIC
1309 /* quagga_get_relative() only updates recent_time if gettimeofday
1310 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1311 * and guarantee to update it before threads are run...
1312 */
1313 quagga_gettimeofday(&recent_time);
1314#endif /* HAVE_CLOCK_MONOTONIC */
1315}
1316
David Lamparter615f9f12013-11-18 23:52:02 +01001317struct thread *thread_current = NULL;
1318
paul718e3742002-12-13 20:15:29 +00001319/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001320 use that to get in-depth stats on the performance of the thread in addition
1321 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001322void
1323thread_call (struct thread *thread)
1324{
ajs8b70d0b2005-04-28 01:31:13 +00001325 unsigned long realtime, cputime;
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001326 RUSAGE_T before, after;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001327
1328 /* Cache a pointer to the relevant cpu history thread, if the thread
1329 * does not have it yet.
1330 *
1331 * Callers submitting 'dummy threads' hence must take care that
1332 * thread->cpu is NULL
1333 */
1334 if (!thread->hist)
1335 {
1336 struct cpu_thread_history tmp;
1337
1338 tmp.func = thread->func;
David Lamparter3493b772013-11-18 23:04:27 +01001339 tmp.funcname = thread->funcname;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001340
1341 thread->hist = hash_get (cpu_record, &tmp,
1342 (void * (*) (void *))cpu_record_hash_alloc);
1343 }
paul718e3742002-12-13 20:15:29 +00001344
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001345 GETRUSAGE (&before);
1346 thread->real = before.real;
paul718e3742002-12-13 20:15:29 +00001347
David Lamparter615f9f12013-11-18 23:52:02 +01001348 thread_current = thread;
paul718e3742002-12-13 20:15:29 +00001349 (*thread->func) (thread);
David Lamparter615f9f12013-11-18 23:52:02 +01001350 thread_current = NULL;
paul718e3742002-12-13 20:15:29 +00001351
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001352 GETRUSAGE (&after);
paul718e3742002-12-13 20:15:29 +00001353
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001354 realtime = thread_consumed_time (&after, &before, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001355 thread->hist->real.total += realtime;
1356 if (thread->hist->real.max < realtime)
1357 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001358#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001359 thread->hist->cpu.total += cputime;
1360 if (thread->hist->cpu.max < cputime)
1361 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001362#endif
paule04ab742003-01-17 23:47:00 +00001363
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001364 ++(thread->hist->total_calls);
1365 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001366
ajs924b9222005-04-16 17:11:24 +00001367#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001368 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001369 {
1370 /*
1371 * We have a CPU Hog on our hands.
1372 * Whinge about it now, so we're aware this is yet another task
1373 * to fix.
1374 */
ajs8b70d0b2005-04-28 01:31:13 +00001375 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001376 thread->funcname,
1377 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001378 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001379 }
ajs924b9222005-04-16 17:11:24 +00001380#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +00001381}
1382
1383/* Execute thread */
1384struct thread *
paule04ab742003-01-17 23:47:00 +00001385funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001386 int (*func)(struct thread *),
1387 void *arg,
paule04ab742003-01-17 23:47:00 +00001388 int val,
David Lamparter3493b772013-11-18 23:04:27 +01001389 debugargdef)
paul718e3742002-12-13 20:15:29 +00001390{
1391 struct thread dummy;
1392
1393 memset (&dummy, 0, sizeof (struct thread));
1394
1395 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001396 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001397 dummy.master = NULL;
1398 dummy.func = func;
1399 dummy.arg = arg;
1400 dummy.u.val = val;
David Lamparter3493b772013-11-18 23:04:27 +01001401
1402 dummy.funcname = funcname;
1403 dummy.schedfrom = schedfrom;
1404 dummy.schedfrom_line = fromln;
1405
paul718e3742002-12-13 20:15:29 +00001406 thread_call (&dummy);
1407
paul718e3742002-12-13 20:15:29 +00001408 return NULL;
1409}