blob: ddb95c015cc18ff2878df4179dec7c093f3cc2ae [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>
25
26#include "thread.h"
27#include "memory.h"
28#include "log.h"
paule04ab742003-01-17 23:47:00 +000029#include "hash.h"
30#include "command.h"
paul05c447d2004-07-22 19:14:27 +000031#include "sigevent.h"
Vincent Bernatd6be5fb2012-05-24 09:44:43 +020032
33#if defined HAVE_SNMP && defined SNMP_AGENTX
34#include <net-snmp/net-snmp-config.h>
35#include <net-snmp/net-snmp-includes.h>
36#include <net-snmp/agent/net-snmp-agent-includes.h>
37#include <net-snmp/agent/snmp_vars.h>
38
39extern int agentx_enabled;
40#endif
41
Hasso Tepper3b96b782012-10-11 11:31:54 +000042#if defined(__APPLE__)
43#include <mach/mach.h>
44#include <mach/mach_time.h>
45#endif
46
paule04ab742003-01-17 23:47:00 +000047
Paul Jakmadb9c0df2006-08-27 06:44:02 +000048/* Recent absolute time of day */
ajs8b70d0b2005-04-28 01:31:13 +000049struct timeval recent_time;
Paul Jakmadb9c0df2006-08-27 06:44:02 +000050static struct timeval last_recent_time;
51/* Relative time, since startup */
52static struct timeval relative_time;
53static struct timeval relative_time_base;
54/* init flag */
55static unsigned short timers_inited;
56
paule04ab742003-01-17 23:47:00 +000057static struct hash *cpu_record = NULL;
paul718e3742002-12-13 20:15:29 +000058
59/* Struct timeval's tv_usec one second value. */
60#define TIMER_SECOND_MICRO 1000000L
61
ajs8b70d0b2005-04-28 01:31:13 +000062/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
63 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000064static struct timeval
paul718e3742002-12-13 20:15:29 +000065timeval_adjust (struct timeval a)
66{
67 while (a.tv_usec >= TIMER_SECOND_MICRO)
68 {
69 a.tv_usec -= TIMER_SECOND_MICRO;
70 a.tv_sec++;
71 }
72
73 while (a.tv_usec < 0)
74 {
75 a.tv_usec += TIMER_SECOND_MICRO;
76 a.tv_sec--;
77 }
78
79 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000080 /* Change negative timeouts to 0. */
81 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000082
83 return a;
84}
85
86static struct timeval
87timeval_subtract (struct timeval a, struct timeval b)
88{
89 struct timeval ret;
90
91 ret.tv_usec = a.tv_usec - b.tv_usec;
92 ret.tv_sec = a.tv_sec - b.tv_sec;
93
94 return timeval_adjust (ret);
95}
96
ajs8b70d0b2005-04-28 01:31:13 +000097static long
paul718e3742002-12-13 20:15:29 +000098timeval_cmp (struct timeval a, struct timeval b)
99{
100 return (a.tv_sec == b.tv_sec
101 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
102}
103
104static unsigned long
105timeval_elapsed (struct timeval a, struct timeval b)
106{
107 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
108 + (a.tv_usec - b.tv_usec));
109}
110
Hasso Tepper3b96b782012-10-11 11:31:54 +0000111#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__)
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000112static void
113quagga_gettimeofday_relative_adjust (void)
114{
115 struct timeval diff;
116 if (timeval_cmp (recent_time, last_recent_time) < 0)
117 {
118 relative_time.tv_sec++;
119 relative_time.tv_usec = 0;
120 }
121 else
122 {
123 diff = timeval_subtract (recent_time, last_recent_time);
124 relative_time.tv_sec += diff.tv_sec;
125 relative_time.tv_usec += diff.tv_usec;
126 relative_time = timeval_adjust (relative_time);
127 }
128 last_recent_time = recent_time;
129}
Hasso Tepper3b96b782012-10-11 11:31:54 +0000130#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000131
132/* gettimeofday wrapper, to keep recent_time updated */
133static int
134quagga_gettimeofday (struct timeval *tv)
135{
136 int ret;
137
138 assert (tv);
139
140 if (!(ret = gettimeofday (&recent_time, NULL)))
141 {
142 /* init... */
143 if (!timers_inited)
144 {
145 relative_time_base = last_recent_time = recent_time;
146 timers_inited = 1;
147 }
148 /* avoid copy if user passed recent_time pointer.. */
149 if (tv != &recent_time)
150 *tv = recent_time;
151 return 0;
152 }
153 return ret;
154}
155
156static int
157quagga_get_relative (struct timeval *tv)
158{
159 int ret;
160
161#ifdef HAVE_CLOCK_MONOTONIC
162 {
163 struct timespec tp;
164 if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
165 {
166 relative_time.tv_sec = tp.tv_sec;
167 relative_time.tv_usec = tp.tv_nsec / 1000;
168 }
169 }
Hasso Tepper3b96b782012-10-11 11:31:54 +0000170#elif defined(__APPLE__)
171 {
172 uint64_t ticks;
173 uint64_t useconds;
174 static mach_timebase_info_data_t timebase_info;
175
176 ticks = mach_absolute_time();
177 if (timebase_info.denom == 0)
178 mach_timebase_info(&timebase_info);
179
180 useconds = ticks * timebase_info.numer / timebase_info.denom / 1000;
181 relative_time.tv_sec = useconds / 1000000;
182 relative_time.tv_usec = useconds % 1000000;
183
184 return 0;
185 }
186#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000187 if (!(ret = quagga_gettimeofday (&recent_time)))
188 quagga_gettimeofday_relative_adjust();
189#endif /* HAVE_CLOCK_MONOTONIC */
190
191 if (tv)
192 *tv = relative_time;
193
194 return ret;
195}
196
197/* Get absolute time stamp, but in terms of the internal timer
198 * Could be wrong, but at least won't go back.
199 */
200static void
201quagga_real_stabilised (struct timeval *tv)
202{
203 *tv = relative_time_base;
204 tv->tv_sec += relative_time.tv_sec;
205 tv->tv_usec += relative_time.tv_usec;
206 *tv = timeval_adjust (*tv);
207}
208
209/* Exported Quagga timestamp function.
210 * Modelled on POSIX clock_gettime.
211 */
212int
213quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
214{
215 switch (clkid)
216 {
217 case QUAGGA_CLK_REALTIME:
218 return quagga_gettimeofday (tv);
219 case QUAGGA_CLK_MONOTONIC:
220 return quagga_get_relative (tv);
221 case QUAGGA_CLK_REALTIME_STABILISED:
222 quagga_real_stabilised (tv);
223 return 0;
224 default:
225 errno = EINVAL;
226 return -1;
227 }
228}
229
230/* time_t value in terms of stabilised absolute time.
231 * replacement for POSIX time()
232 */
233time_t
234quagga_time (time_t *t)
235{
236 struct timeval tv;
237 quagga_real_stabilised (&tv);
238 if (t)
239 *t = tv.tv_sec;
240 return tv.tv_sec;
241}
242
243/* Public export of recent_relative_time by value */
244struct timeval
245recent_relative_time (void)
246{
247 return relative_time;
248}
249
paula48b4e62005-04-22 00:43:47 +0000250static unsigned int
paule04ab742003-01-17 23:47:00 +0000251cpu_record_hash_key (struct cpu_thread_history *a)
252{
paul8cc41982005-05-06 21:25:49 +0000253 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +0000254}
255
256static int
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100257cpu_record_hash_cmp (const struct cpu_thread_history *a,
258 const struct cpu_thread_history *b)
paule04ab742003-01-17 23:47:00 +0000259{
260 return a->func == b->func;
261}
262
paul8cc41982005-05-06 21:25:49 +0000263static void *
paule04ab742003-01-17 23:47:00 +0000264cpu_record_hash_alloc (struct cpu_thread_history *a)
265{
266 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000267 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000268 new->func = a->func;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000269 strcpy(new->funcname, a->funcname);
paule04ab742003-01-17 23:47:00 +0000270 return new;
271}
272
Chris Caputo228da422009-07-18 05:44:03 +0000273static void
274cpu_record_hash_free (void *a)
275{
276 struct cpu_thread_history *hist = a;
277
Chris Caputo228da422009-07-18 05:44:03 +0000278 XFREE (MTYPE_THREAD_STATS, hist);
279}
280
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100281static void
paule04ab742003-01-17 23:47:00 +0000282vty_out_cpu_thread_history(struct vty* vty,
283 struct cpu_thread_history *a)
284{
ajs8b70d0b2005-04-28 01:31:13 +0000285#ifdef HAVE_RUSAGE
286 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
287 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
288 a->cpu.total/a->total_calls, a->cpu.max,
289 a->real.total/a->total_calls, a->real.max);
290#else
291 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
292 a->real.total/1000, a->real.total%1000, a->total_calls,
293 a->real.total/a->total_calls, a->real.max);
294#endif
295 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000296 a->types & (1 << THREAD_READ) ? 'R':' ',
297 a->types & (1 << THREAD_WRITE) ? 'W':' ',
298 a->types & (1 << THREAD_TIMER) ? 'T':' ',
299 a->types & (1 << THREAD_EVENT) ? 'E':' ',
300 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000301 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000302 a->funcname, VTY_NEWLINE);
303}
304
305static void
306cpu_record_hash_print(struct hash_backet *bucket,
307 void *args[])
308{
309 struct cpu_thread_history *totals = args[0];
310 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100311 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000312 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000313
paule04ab742003-01-17 23:47:00 +0000314 a = bucket->data;
315 if ( !(a->types & *filter) )
316 return;
317 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000318 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000319 totals->real.total += a->real.total;
320 if (totals->real.max < a->real.max)
321 totals->real.max = a->real.max;
322#ifdef HAVE_RUSAGE
323 totals->cpu.total += a->cpu.total;
324 if (totals->cpu.max < a->cpu.max)
325 totals->cpu.max = a->cpu.max;
326#endif
paule04ab742003-01-17 23:47:00 +0000327}
328
329static void
Paul Jakma41b23732009-06-30 16:12:49 +0100330cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000331{
332 struct cpu_thread_history tmp;
333 void *args[3] = {&tmp, vty, &filter};
334
335 memset(&tmp, 0, sizeof tmp);
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000336 strcpy(tmp.funcname, "TOTAL");
paule04ab742003-01-17 23:47:00 +0000337 tmp.types = filter;
338
ajs8b70d0b2005-04-28 01:31:13 +0000339#ifdef HAVE_RUSAGE
340 vty_out(vty, "%21s %18s %18s%s",
341 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
342#endif
343 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
344#ifdef HAVE_RUSAGE
345 vty_out(vty, " Avg uSec Max uSecs");
346#endif
347 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000348 hash_iterate(cpu_record,
349 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
350 args);
351
352 if (tmp.total_calls > 0)
353 vty_out_cpu_thread_history(vty, &tmp);
354}
355
356DEFUN(show_thread_cpu,
357 show_thread_cpu_cmd,
358 "show thread cpu [FILTER]",
359 SHOW_STR
360 "Thread information\n"
361 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000362 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000363{
364 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100365 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000366
367 if (argc > 0)
368 {
369 filter = 0;
370 while (argv[0][i] != '\0')
371 {
372 switch ( argv[0][i] )
373 {
374 case 'r':
375 case 'R':
376 filter |= (1 << THREAD_READ);
377 break;
378 case 'w':
379 case 'W':
380 filter |= (1 << THREAD_WRITE);
381 break;
382 case 't':
383 case 'T':
384 filter |= (1 << THREAD_TIMER);
385 break;
386 case 'e':
387 case 'E':
388 filter |= (1 << THREAD_EVENT);
389 break;
390 case 'x':
391 case 'X':
392 filter |= (1 << THREAD_EXECUTE);
393 break;
paula48b4e62005-04-22 00:43:47 +0000394 case 'b':
395 case 'B':
396 filter |= (1 << THREAD_BACKGROUND);
397 break;
paule04ab742003-01-17 23:47:00 +0000398 default:
399 break;
400 }
401 ++i;
402 }
403 if (filter == 0)
404 {
paula48b4e62005-04-22 00:43:47 +0000405 vty_out(vty, "Invalid filter \"%s\" specified,"
406 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000407 argv[0], VTY_NEWLINE);
408 return CMD_WARNING;
409 }
410 }
411
412 cpu_record_print(vty, filter);
413 return CMD_SUCCESS;
414}
Paul Jakmae276eb82010-01-09 16:15:00 +0000415
416static void
417cpu_record_hash_clear (struct hash_backet *bucket,
418 void *args)
419{
420 thread_type *filter = args;
421 struct cpu_thread_history *a = bucket->data;
422
423 a = bucket->data;
424 if ( !(a->types & *filter) )
425 return;
426
427 hash_release (cpu_record, bucket->data);
428}
429
430static void
431cpu_record_clear (thread_type filter)
432{
433 thread_type *tmp = &filter;
434 hash_iterate (cpu_record,
435 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
436 tmp);
437}
438
439DEFUN(clear_thread_cpu,
440 clear_thread_cpu_cmd,
441 "clear thread cpu [FILTER]",
442 "Clear stored data\n"
443 "Thread information\n"
444 "Thread CPU usage\n"
445 "Display filter (rwtexb)\n")
446{
447 int i = 0;
448 thread_type filter = (thread_type) -1U;
449
450 if (argc > 0)
451 {
452 filter = 0;
453 while (argv[0][i] != '\0')
454 {
455 switch ( argv[0][i] )
456 {
457 case 'r':
458 case 'R':
459 filter |= (1 << THREAD_READ);
460 break;
461 case 'w':
462 case 'W':
463 filter |= (1 << THREAD_WRITE);
464 break;
465 case 't':
466 case 'T':
467 filter |= (1 << THREAD_TIMER);
468 break;
469 case 'e':
470 case 'E':
471 filter |= (1 << THREAD_EVENT);
472 break;
473 case 'x':
474 case 'X':
475 filter |= (1 << THREAD_EXECUTE);
476 break;
477 case 'b':
478 case 'B':
479 filter |= (1 << THREAD_BACKGROUND);
480 break;
481 default:
482 break;
483 }
484 ++i;
485 }
486 if (filter == 0)
487 {
488 vty_out(vty, "Invalid filter \"%s\" specified,"
489 " must contain at least one of 'RWTEXB'%s",
490 argv[0], VTY_NEWLINE);
491 return CMD_WARNING;
492 }
493 }
494
495 cpu_record_clear (filter);
496 return CMD_SUCCESS;
497}
paule04ab742003-01-17 23:47:00 +0000498
paul718e3742002-12-13 20:15:29 +0000499/* Allocate new thread master. */
500struct thread_master *
501thread_master_create ()
502{
paule04ab742003-01-17 23:47:00 +0000503 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000504 cpu_record
Stephen Hemminger90645f52013-01-04 22:29:21 +0000505 = hash_create ((unsigned int (*) (void *))cpu_record_hash_key,
506 (int (*) (const void *, const void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000507
paul718e3742002-12-13 20:15:29 +0000508 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
509 sizeof (struct thread_master));
510}
511
512/* Add a new thread to the list. */
513static void
514thread_list_add (struct thread_list *list, struct thread *thread)
515{
516 thread->next = NULL;
517 thread->prev = list->tail;
518 if (list->tail)
519 list->tail->next = thread;
520 else
521 list->head = thread;
522 list->tail = thread;
523 list->count++;
524}
525
526/* Add a new thread just before the point. */
527static void
528thread_list_add_before (struct thread_list *list,
529 struct thread *point,
530 struct thread *thread)
531{
532 thread->next = point;
533 thread->prev = point->prev;
534 if (point->prev)
535 point->prev->next = thread;
536 else
537 list->head = thread;
538 point->prev = thread;
539 list->count++;
540}
541
542/* Delete a thread from the list. */
543static struct thread *
544thread_list_delete (struct thread_list *list, struct thread *thread)
545{
546 if (thread->next)
547 thread->next->prev = thread->prev;
548 else
549 list->tail = thread->prev;
550 if (thread->prev)
551 thread->prev->next = thread->next;
552 else
553 list->head = thread->next;
554 thread->next = thread->prev = NULL;
555 list->count--;
556 return thread;
557}
558
559/* Move thread to unuse list. */
560static void
561thread_add_unuse (struct thread_master *m, struct thread *thread)
562{
paula48b4e62005-04-22 00:43:47 +0000563 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000564 assert (thread->next == NULL);
565 assert (thread->prev == NULL);
566 assert (thread->type == THREAD_UNUSED);
567 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000568 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000569}
570
571/* Free all unused thread. */
572static void
573thread_list_free (struct thread_master *m, struct thread_list *list)
574{
575 struct thread *t;
576 struct thread *next;
577
578 for (t = list->head; t; t = next)
579 {
580 next = t->next;
581 XFREE (MTYPE_THREAD, t);
582 list->count--;
583 m->alloc--;
584 }
585}
586
587/* Stop thread scheduler. */
588void
589thread_master_free (struct thread_master *m)
590{
591 thread_list_free (m, &m->read);
592 thread_list_free (m, &m->write);
593 thread_list_free (m, &m->timer);
594 thread_list_free (m, &m->event);
595 thread_list_free (m, &m->ready);
596 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000597 thread_list_free (m, &m->background);
598
paul718e3742002-12-13 20:15:29 +0000599 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000600
601 if (cpu_record)
602 {
603 hash_clean (cpu_record, cpu_record_hash_free);
604 hash_free (cpu_record);
605 cpu_record = NULL;
606 }
paul718e3742002-12-13 20:15:29 +0000607}
608
paul8cc41982005-05-06 21:25:49 +0000609/* Thread list is empty or not. */
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100610static int
paul8cc41982005-05-06 21:25:49 +0000611thread_empty (struct thread_list *list)
612{
613 return list->head ? 0 : 1;
614}
615
paul718e3742002-12-13 20:15:29 +0000616/* Delete top of the list and return it. */
617static struct thread *
618thread_trim_head (struct thread_list *list)
619{
paul8cc41982005-05-06 21:25:49 +0000620 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000621 return thread_list_delete (list, list->head);
622 return NULL;
623}
624
paul718e3742002-12-13 20:15:29 +0000625/* Return remain time in second. */
626unsigned long
627thread_timer_remain_second (struct thread *thread)
628{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000629 quagga_get_relative (NULL);
630
631 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
632 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000633 else
634 return 0;
635}
636
paule04ab742003-01-17 23:47:00 +0000637/* Trim blankspace and "()"s */
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000638void
639strip_funcname (char *dest, const char *funcname)
paule04ab742003-01-17 23:47:00 +0000640{
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000641 char buff[FUNCNAME_LEN];
642 char tmp, *e, *b = buff;
paule04ab742003-01-17 23:47:00 +0000643
644 strncpy(buff, funcname, sizeof(buff));
645 buff[ sizeof(buff) -1] = '\0';
646 e = buff +strlen(buff) -1;
647
648 /* Wont work for funcname == "Word (explanation)" */
649
650 while (*b == ' ' || *b == '(')
651 ++b;
652 while (*e == ' ' || *e == ')')
653 --e;
654 e++;
655
656 tmp = *e;
657 *e = '\0';
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000658 strcpy (dest, b);
paule04ab742003-01-17 23:47:00 +0000659 *e = tmp;
paule04ab742003-01-17 23:47:00 +0000660}
661
paul718e3742002-12-13 20:15:29 +0000662/* Get new thread. */
663static struct thread *
664thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000665 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000666{
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000667 struct thread *thread = thread_trim_head (&m->unuse);
paul718e3742002-12-13 20:15:29 +0000668
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000669 if (! thread)
paul718e3742002-12-13 20:15:29 +0000670 {
671 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
672 m->alloc++;
673 }
674 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000675 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000676 thread->master = m;
677 thread->func = func;
678 thread->arg = arg;
679
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000680 strip_funcname (thread->funcname, funcname);
paule04ab742003-01-17 23:47:00 +0000681
paul718e3742002-12-13 20:15:29 +0000682 return thread;
683}
684
685/* Add new read thread. */
686struct thread *
paule04ab742003-01-17 23:47:00 +0000687funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000688 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000689{
690 struct thread *thread;
691
692 assert (m != NULL);
693
694 if (FD_ISSET (fd, &m->readfd))
695 {
696 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
697 return NULL;
698 }
699
paule04ab742003-01-17 23:47:00 +0000700 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000701 FD_SET (fd, &m->readfd);
702 thread->u.fd = fd;
703 thread_list_add (&m->read, thread);
704
705 return thread;
706}
707
708/* Add new write thread. */
709struct thread *
paule04ab742003-01-17 23:47:00 +0000710funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000711 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000712{
713 struct thread *thread;
714
715 assert (m != NULL);
716
717 if (FD_ISSET (fd, &m->writefd))
718 {
719 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
720 return NULL;
721 }
722
paule04ab742003-01-17 23:47:00 +0000723 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000724 FD_SET (fd, &m->writefd);
725 thread->u.fd = fd;
726 thread_list_add (&m->write, thread);
727
728 return thread;
729}
730
paul98c91ac2004-10-05 14:57:50 +0000731static struct thread *
732funcname_thread_add_timer_timeval (struct thread_master *m,
733 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000734 int type,
paul98c91ac2004-10-05 14:57:50 +0000735 void *arg,
736 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000737 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000738{
paul718e3742002-12-13 20:15:29 +0000739 struct thread *thread;
paula48b4e62005-04-22 00:43:47 +0000740 struct thread_list *list;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000741 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000742 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000743
744 assert (m != NULL);
745
ajs8b70d0b2005-04-28 01:31:13 +0000746 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000747 assert (time_relative);
748
ajs8b70d0b2005-04-28 01:31:13 +0000749 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000750 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000751
752 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100753 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000754 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
755 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000756 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000757
758 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000759 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000760 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
761 break;
762
763 if (tt)
paula48b4e62005-04-22 00:43:47 +0000764 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000765 else
paula48b4e62005-04-22 00:43:47 +0000766 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000767
768 return thread;
769}
770
paul98c91ac2004-10-05 14:57:50 +0000771
772/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000773struct thread *
paul98c91ac2004-10-05 14:57:50 +0000774funcname_thread_add_timer (struct thread_master *m,
775 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000776 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000777{
paul98c91ac2004-10-05 14:57:50 +0000778 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000779
780 assert (m != NULL);
781
paul9076fbd2004-10-11 09:40:58 +0000782 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000783 trel.tv_usec = 0;
784
paula48b4e62005-04-22 00:43:47 +0000785 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
786 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000787}
788
789/* Add timer event thread with "millisecond" resolution */
790struct thread *
791funcname_thread_add_timer_msec (struct thread_master *m,
792 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000793 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000794{
795 struct timeval trel;
796
797 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000798
ajsaf04bd72004-12-28 17:00:12 +0000799 trel.tv_sec = timer / 1000;
800 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000801
paula48b4e62005-04-22 00:43:47 +0000802 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
803 arg, &trel, funcname);
804}
805
806/* Add a background thread, with an optional millisec delay */
807struct thread *
808funcname_thread_add_background (struct thread_master *m,
809 int (*func) (struct thread *),
810 void *arg, long delay,
811 const char *funcname)
812{
813 struct timeval trel;
814
815 assert (m != NULL);
816
817 if (delay)
818 {
819 trel.tv_sec = delay / 1000;
820 trel.tv_usec = 1000*(delay % 1000);
821 }
822 else
823 {
824 trel.tv_sec = 0;
825 trel.tv_usec = 0;
826 }
827
828 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
829 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000830}
831
paul718e3742002-12-13 20:15:29 +0000832/* Add simple event thread. */
833struct thread *
paule04ab742003-01-17 23:47:00 +0000834funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000835 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000836{
837 struct thread *thread;
838
839 assert (m != NULL);
840
paule04ab742003-01-17 23:47:00 +0000841 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000842 thread->u.val = val;
843 thread_list_add (&m->event, thread);
844
845 return thread;
846}
847
848/* Cancel thread from scheduler. */
849void
850thread_cancel (struct thread *thread)
851{
paula48b4e62005-04-22 00:43:47 +0000852 struct thread_list *list;
853
paul718e3742002-12-13 20:15:29 +0000854 switch (thread->type)
855 {
856 case THREAD_READ:
857 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
858 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000859 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000860 break;
861 case THREAD_WRITE:
862 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
863 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000864 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000865 break;
866 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000867 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000868 break;
869 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000870 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000871 break;
872 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000873 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000874 break;
paula48b4e62005-04-22 00:43:47 +0000875 case THREAD_BACKGROUND:
876 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000877 break;
paul718e3742002-12-13 20:15:29 +0000878 default:
paula48b4e62005-04-22 00:43:47 +0000879 return;
paul718e3742002-12-13 20:15:29 +0000880 break;
881 }
paula48b4e62005-04-22 00:43:47 +0000882 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000883 thread->type = THREAD_UNUSED;
884 thread_add_unuse (thread->master, thread);
885}
886
887/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000888unsigned int
paul718e3742002-12-13 20:15:29 +0000889thread_cancel_event (struct thread_master *m, void *arg)
890{
pauldc818072005-05-19 01:30:53 +0000891 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000892 struct thread *thread;
893
894 thread = m->event.head;
895 while (thread)
896 {
897 struct thread *t;
898
899 t = thread;
900 thread = t->next;
901
902 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000903 {
pauldc818072005-05-19 01:30:53 +0000904 ret++;
paula48b4e62005-04-22 00:43:47 +0000905 thread_list_delete (&m->event, t);
906 t->type = THREAD_UNUSED;
907 thread_add_unuse (m, t);
908 }
paul718e3742002-12-13 20:15:29 +0000909 }
Jorge Boncompte [DTI2]1b79fcb2012-05-07 15:17:31 +0000910
911 /* thread can be on the ready list too */
912 thread = m->ready.head;
913 while (thread)
914 {
915 struct thread *t;
916
917 t = thread;
918 thread = t->next;
919
920 if (t->arg == arg)
921 {
922 ret++;
923 thread_list_delete (&m->ready, t);
924 t->type = THREAD_UNUSED;
925 thread_add_unuse (m, t);
926 }
927 }
pauldc818072005-05-19 01:30:53 +0000928 return ret;
paul718e3742002-12-13 20:15:29 +0000929}
930
paula48b4e62005-04-22 00:43:47 +0000931static struct timeval *
932thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000933{
paul8cc41982005-05-06 21:25:49 +0000934 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000935 {
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000936 *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000937 return timer_val;
938 }
939 return NULL;
940}
paul718e3742002-12-13 20:15:29 +0000941
paul8cc41982005-05-06 21:25:49 +0000942static struct thread *
paul718e3742002-12-13 20:15:29 +0000943thread_run (struct thread_master *m, struct thread *thread,
944 struct thread *fetch)
945{
946 *fetch = *thread;
947 thread->type = THREAD_UNUSED;
948 thread_add_unuse (m, thread);
949 return fetch;
950}
951
paula48b4e62005-04-22 00:43:47 +0000952static int
953thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000954{
955 struct thread *thread;
956 struct thread *next;
957 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000958
959 assert (list);
960
paul718e3742002-12-13 20:15:29 +0000961 for (thread = list->head; thread; thread = next)
962 {
963 next = thread->next;
964
965 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000966 {
967 assert (FD_ISSET (THREAD_FD (thread), mfdset));
968 FD_CLR(THREAD_FD (thread), mfdset);
969 thread_list_delete (list, thread);
970 thread_list_add (&thread->master->ready, thread);
971 thread->type = THREAD_READY;
972 ready++;
973 }
paul718e3742002-12-13 20:15:29 +0000974 }
975 return ready;
976}
977
ajs8b70d0b2005-04-28 01:31:13 +0000978/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000979static unsigned int
980thread_timer_process (struct thread_list *list, struct timeval *timenow)
981{
982 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +0000983 struct thread *next;
paula48b4e62005-04-22 00:43:47 +0000984 unsigned int ready = 0;
985
Paul Jakmab5043aa2012-02-28 18:32:56 +0000986 for (thread = list->head; thread; thread = next)
ajs8b70d0b2005-04-28 01:31:13 +0000987 {
Paul Jakmab5043aa2012-02-28 18:32:56 +0000988 next = thread->next;
ajs8b70d0b2005-04-28 01:31:13 +0000989 if (timeval_cmp (*timenow, thread->u.sands) < 0)
990 return ready;
991 thread_list_delete (list, thread);
992 thread->type = THREAD_READY;
993 thread_list_add (&thread->master->ready, thread);
994 ready++;
995 }
paula48b4e62005-04-22 00:43:47 +0000996 return ready;
997}
998
Paul Jakma2613abe2010-01-11 16:33:07 +0000999/* process a list en masse, e.g. for event thread lists */
1000static unsigned int
1001thread_process (struct thread_list *list)
1002{
1003 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +00001004 struct thread *next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001005 unsigned int ready = 0;
1006
Paul Jakmab5043aa2012-02-28 18:32:56 +00001007 for (thread = list->head; thread; thread = next)
Paul Jakma2613abe2010-01-11 16:33:07 +00001008 {
Paul Jakmab5043aa2012-02-28 18:32:56 +00001009 next = thread->next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001010 thread_list_delete (list, thread);
1011 thread->type = THREAD_READY;
1012 thread_list_add (&thread->master->ready, thread);
1013 ready++;
1014 }
1015 return ready;
1016}
1017
1018
paul718e3742002-12-13 20:15:29 +00001019/* Fetch next ready thread. */
1020struct thread *
1021thread_fetch (struct thread_master *m, struct thread *fetch)
1022{
paul718e3742002-12-13 20:15:29 +00001023 struct thread *thread;
1024 fd_set readfd;
1025 fd_set writefd;
1026 fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001027 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001028 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001029 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001030 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001031
1032 while (1)
1033 {
paula48b4e62005-04-22 00:43:47 +00001034 int num = 0;
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001035#if defined HAVE_SNMP && defined SNMP_AGENTX
1036 struct timeval snmp_timer_wait;
1037 int snmpblock = 0;
1038 int fdsetsize;
1039#endif
paula48b4e62005-04-22 00:43:47 +00001040
Paul Jakma2613abe2010-01-11 16:33:07 +00001041 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001042 quagga_sigevent_process ();
1043
Paul Jakma2613abe2010-01-11 16:33:07 +00001044 /* Drain the ready queue of already scheduled jobs, before scheduling
1045 * more.
paula48b4e62005-04-22 00:43:47 +00001046 */
paul718e3742002-12-13 20:15:29 +00001047 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001048 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001049
Paul Jakma2613abe2010-01-11 16:33:07 +00001050 /* To be fair to all kinds of threads, and avoid starvation, we
1051 * need to be careful to consider all thread types for scheduling
1052 * in each quanta. I.e. we should not return early from here on.
1053 */
1054
1055 /* Normal event are the next highest priority. */
1056 thread_process (&m->event);
1057
paul718e3742002-12-13 20:15:29 +00001058 /* Structure copy. */
1059 readfd = m->readfd;
1060 writefd = m->writefd;
1061 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +00001062
1063 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001064 if (m->ready.count == 0)
1065 {
1066 quagga_get_relative (NULL);
1067 timer_wait = thread_timer_wait (&m->timer, &timer_val);
1068 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
1069
1070 if (timer_wait_bg &&
1071 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1072 timer_wait = timer_wait_bg;
1073 }
paula48b4e62005-04-22 00:43:47 +00001074
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001075#if defined HAVE_SNMP && defined SNMP_AGENTX
1076 /* When SNMP is enabled, we may have to select() on additional
1077 FD. snmp_select_info() will add them to `readfd'. The trick
1078 with this function is its last argument. We need to set it to
1079 0 if timer_wait is not NULL and we need to use the provided
1080 new timer only if it is still set to 0. */
1081 if (agentx_enabled)
1082 {
1083 fdsetsize = FD_SETSIZE;
1084 snmpblock = 1;
1085 if (timer_wait)
1086 {
1087 snmpblock = 0;
1088 memcpy(&snmp_timer_wait, timer_wait, sizeof(struct timeval));
1089 }
1090 snmp_select_info(&fdsetsize, &readfd, &snmp_timer_wait, &snmpblock);
1091 if (snmpblock == 0)
1092 timer_wait = &snmp_timer_wait;
1093 }
1094#endif
paul718e3742002-12-13 20:15:29 +00001095 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001096
1097 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001098 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001099 {
1100 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001101 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001102 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001103 return NULL;
1104 }
ajs8b70d0b2005-04-28 01:31:13 +00001105
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001106#if defined HAVE_SNMP && defined SNMP_AGENTX
1107 if (agentx_enabled)
1108 {
1109 if (num > 0)
1110 snmp_read(&readfd);
1111 else if (num == 0)
1112 {
1113 snmp_timeout();
1114 run_alarms();
1115 }
1116 netsnmp_check_outstanding_agent_requests();
1117 }
1118#endif
1119
ajs8b70d0b2005-04-28 01:31:13 +00001120 /* Check foreground timers. Historically, they have had higher
1121 priority than I/O threads, so let's push them onto the ready
1122 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001123 quagga_get_relative (NULL);
1124 thread_timer_process (&m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001125
1126 /* Got IO, process it */
1127 if (num > 0)
1128 {
1129 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001130 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +00001131 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001132 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +00001133 }
ajs8b70d0b2005-04-28 01:31:13 +00001134
1135#if 0
1136 /* If any threads were made ready above (I/O or foreground timer),
1137 perhaps we should avoid adding background timers to the ready
1138 list at this time. If this is code is uncommented, then background
1139 timer threads will not run unless there is nothing else to do. */
1140 if ((thread = thread_trim_head (&m->ready)) != NULL)
1141 return thread_run (m, thread, fetch);
1142#endif
1143
paula48b4e62005-04-22 00:43:47 +00001144 /* Background timer/events, lowest priority */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001145 thread_timer_process (&m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001146
ajs8b70d0b2005-04-28 01:31:13 +00001147 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001148 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001149 }
1150}
1151
ajs924b9222005-04-16 17:11:24 +00001152unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001153thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001154{
paul718e3742002-12-13 20:15:29 +00001155#ifdef HAVE_RUSAGE
1156 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001157 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1158 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001159#else
ajs8b70d0b2005-04-28 01:31:13 +00001160 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001161#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001162 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001163}
1164
ajs8b70d0b2005-04-28 01:31:13 +00001165/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1166 Note: we are using real (wall clock) time for this calculation.
1167 It could be argued that CPU time may make more sense in certain
1168 contexts. The things to consider are whether the thread may have
1169 blocked (in which case wall time increases, but CPU time does not),
1170 or whether the system is heavily loaded with other processes competing
1171 for CPU time. On balance, wall clock time seems to make sense.
1172 Plus it has the added benefit that gettimeofday should be faster
1173 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001174int
1175thread_should_yield (struct thread *thread)
1176{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001177 quagga_get_relative (NULL);
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001178 return (timeval_elapsed(relative_time, thread->real) >
ajs8b70d0b2005-04-28 01:31:13 +00001179 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001180}
1181
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001182void
1183thread_getrusage (RUSAGE_T *r)
1184{
1185 quagga_get_relative (NULL);
1186#ifdef HAVE_RUSAGE
1187 getrusage(RUSAGE_SELF, &(r->cpu));
1188#endif
1189 r->real = relative_time;
1190
1191#ifdef HAVE_CLOCK_MONOTONIC
1192 /* quagga_get_relative() only updates recent_time if gettimeofday
1193 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1194 * and guarantee to update it before threads are run...
1195 */
1196 quagga_gettimeofday(&recent_time);
1197#endif /* HAVE_CLOCK_MONOTONIC */
1198}
1199
paul718e3742002-12-13 20:15:29 +00001200/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001201 use that to get in-depth stats on the performance of the thread in addition
1202 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001203void
1204thread_call (struct thread *thread)
1205{
ajs8b70d0b2005-04-28 01:31:13 +00001206 unsigned long realtime, cputime;
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001207 RUSAGE_T before, after;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001208
1209 /* Cache a pointer to the relevant cpu history thread, if the thread
1210 * does not have it yet.
1211 *
1212 * Callers submitting 'dummy threads' hence must take care that
1213 * thread->cpu is NULL
1214 */
1215 if (!thread->hist)
1216 {
1217 struct cpu_thread_history tmp;
1218
1219 tmp.func = thread->func;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001220 strcpy(tmp.funcname, thread->funcname);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001221
1222 thread->hist = hash_get (cpu_record, &tmp,
1223 (void * (*) (void *))cpu_record_hash_alloc);
1224 }
paul718e3742002-12-13 20:15:29 +00001225
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001226 GETRUSAGE (&before);
1227 thread->real = before.real;
paul718e3742002-12-13 20:15:29 +00001228
1229 (*thread->func) (thread);
1230
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001231 GETRUSAGE (&after);
paul718e3742002-12-13 20:15:29 +00001232
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001233 realtime = thread_consumed_time (&after, &before, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001234 thread->hist->real.total += realtime;
1235 if (thread->hist->real.max < realtime)
1236 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001237#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001238 thread->hist->cpu.total += cputime;
1239 if (thread->hist->cpu.max < cputime)
1240 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001241#endif
paule04ab742003-01-17 23:47:00 +00001242
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001243 ++(thread->hist->total_calls);
1244 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001245
ajs924b9222005-04-16 17:11:24 +00001246#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001247 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001248 {
1249 /*
1250 * We have a CPU Hog on our hands.
1251 * Whinge about it now, so we're aware this is yet another task
1252 * to fix.
1253 */
ajs8b70d0b2005-04-28 01:31:13 +00001254 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001255 thread->funcname,
1256 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001257 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001258 }
ajs924b9222005-04-16 17:11:24 +00001259#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +00001260}
1261
1262/* Execute thread */
1263struct thread *
paule04ab742003-01-17 23:47:00 +00001264funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001265 int (*func)(struct thread *),
1266 void *arg,
paule04ab742003-01-17 23:47:00 +00001267 int val,
hasso8c328f12004-10-05 21:01:23 +00001268 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001269{
1270 struct thread dummy;
1271
1272 memset (&dummy, 0, sizeof (struct thread));
1273
1274 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001275 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001276 dummy.master = NULL;
1277 dummy.func = func;
1278 dummy.arg = arg;
1279 dummy.u.val = val;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001280 strip_funcname (dummy.funcname, funcname);
paul718e3742002-12-13 20:15:29 +00001281 thread_call (&dummy);
1282
paul718e3742002-12-13 20:15:29 +00001283 return NULL;
1284}