blob: 3740147e20c0c37898dbea1e37ea3f050d48e3d2 [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"
paule04ab742003-01-17 23:47:00 +000032
Paul Jakmadb9c0df2006-08-27 06:44:02 +000033/* Recent absolute time of day */
ajs8b70d0b2005-04-28 01:31:13 +000034struct timeval recent_time;
Paul Jakmadb9c0df2006-08-27 06:44:02 +000035static struct timeval last_recent_time;
36/* Relative time, since startup */
37static struct timeval relative_time;
38static struct timeval relative_time_base;
39/* init flag */
40static unsigned short timers_inited;
41
paule04ab742003-01-17 23:47:00 +000042static struct hash *cpu_record = NULL;
paul718e3742002-12-13 20:15:29 +000043
44/* Struct timeval's tv_usec one second value. */
45#define TIMER_SECOND_MICRO 1000000L
46
ajs8b70d0b2005-04-28 01:31:13 +000047/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
48 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000049static struct timeval
paul718e3742002-12-13 20:15:29 +000050timeval_adjust (struct timeval a)
51{
52 while (a.tv_usec >= TIMER_SECOND_MICRO)
53 {
54 a.tv_usec -= TIMER_SECOND_MICRO;
55 a.tv_sec++;
56 }
57
58 while (a.tv_usec < 0)
59 {
60 a.tv_usec += TIMER_SECOND_MICRO;
61 a.tv_sec--;
62 }
63
64 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000065 /* Change negative timeouts to 0. */
66 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000067
68 return a;
69}
70
71static struct timeval
72timeval_subtract (struct timeval a, struct timeval b)
73{
74 struct timeval ret;
75
76 ret.tv_usec = a.tv_usec - b.tv_usec;
77 ret.tv_sec = a.tv_sec - b.tv_sec;
78
79 return timeval_adjust (ret);
80}
81
ajs8b70d0b2005-04-28 01:31:13 +000082static long
paul718e3742002-12-13 20:15:29 +000083timeval_cmp (struct timeval a, struct timeval b)
84{
85 return (a.tv_sec == b.tv_sec
86 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
87}
88
89static unsigned long
90timeval_elapsed (struct timeval a, struct timeval b)
91{
92 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
93 + (a.tv_usec - b.tv_usec));
94}
95
Paul Jakmadb9c0df2006-08-27 06:44:02 +000096#ifndef HAVE_CLOCK_MONOTONIC
97static void
98quagga_gettimeofday_relative_adjust (void)
99{
100 struct timeval diff;
101 if (timeval_cmp (recent_time, last_recent_time) < 0)
102 {
103 relative_time.tv_sec++;
104 relative_time.tv_usec = 0;
105 }
106 else
107 {
108 diff = timeval_subtract (recent_time, last_recent_time);
109 relative_time.tv_sec += diff.tv_sec;
110 relative_time.tv_usec += diff.tv_usec;
111 relative_time = timeval_adjust (relative_time);
112 }
113 last_recent_time = recent_time;
114}
115#endif /* !HAVE_CLOCK_MONOTONIC */
116
117/* gettimeofday wrapper, to keep recent_time updated */
118static int
119quagga_gettimeofday (struct timeval *tv)
120{
121 int ret;
122
123 assert (tv);
124
125 if (!(ret = gettimeofday (&recent_time, NULL)))
126 {
127 /* init... */
128 if (!timers_inited)
129 {
130 relative_time_base = last_recent_time = recent_time;
131 timers_inited = 1;
132 }
133 /* avoid copy if user passed recent_time pointer.. */
134 if (tv != &recent_time)
135 *tv = recent_time;
136 return 0;
137 }
138 return ret;
139}
140
141static int
142quagga_get_relative (struct timeval *tv)
143{
144 int ret;
145
146#ifdef HAVE_CLOCK_MONOTONIC
147 {
148 struct timespec tp;
149 if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
150 {
151 relative_time.tv_sec = tp.tv_sec;
152 relative_time.tv_usec = tp.tv_nsec / 1000;
153 }
154 }
155#else /* !HAVE_CLOCK_MONOTONIC */
156 if (!(ret = quagga_gettimeofday (&recent_time)))
157 quagga_gettimeofday_relative_adjust();
158#endif /* HAVE_CLOCK_MONOTONIC */
159
160 if (tv)
161 *tv = relative_time;
162
163 return ret;
164}
165
166/* Get absolute time stamp, but in terms of the internal timer
167 * Could be wrong, but at least won't go back.
168 */
169static void
170quagga_real_stabilised (struct timeval *tv)
171{
172 *tv = relative_time_base;
173 tv->tv_sec += relative_time.tv_sec;
174 tv->tv_usec += relative_time.tv_usec;
175 *tv = timeval_adjust (*tv);
176}
177
178/* Exported Quagga timestamp function.
179 * Modelled on POSIX clock_gettime.
180 */
181int
182quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
183{
184 switch (clkid)
185 {
186 case QUAGGA_CLK_REALTIME:
187 return quagga_gettimeofday (tv);
188 case QUAGGA_CLK_MONOTONIC:
189 return quagga_get_relative (tv);
190 case QUAGGA_CLK_REALTIME_STABILISED:
191 quagga_real_stabilised (tv);
192 return 0;
193 default:
194 errno = EINVAL;
195 return -1;
196 }
197}
198
199/* time_t value in terms of stabilised absolute time.
200 * replacement for POSIX time()
201 */
202time_t
203quagga_time (time_t *t)
204{
205 struct timeval tv;
206 quagga_real_stabilised (&tv);
207 if (t)
208 *t = tv.tv_sec;
209 return tv.tv_sec;
210}
211
212/* Public export of recent_relative_time by value */
213struct timeval
214recent_relative_time (void)
215{
216 return relative_time;
217}
218
paula48b4e62005-04-22 00:43:47 +0000219static unsigned int
paule04ab742003-01-17 23:47:00 +0000220cpu_record_hash_key (struct cpu_thread_history *a)
221{
paul8cc41982005-05-06 21:25:49 +0000222 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +0000223}
224
225static int
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100226cpu_record_hash_cmp (const struct cpu_thread_history *a,
227 const struct cpu_thread_history *b)
paule04ab742003-01-17 23:47:00 +0000228{
229 return a->func == b->func;
230}
231
paul8cc41982005-05-06 21:25:49 +0000232static void *
paule04ab742003-01-17 23:47:00 +0000233cpu_record_hash_alloc (struct cpu_thread_history *a)
234{
235 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000236 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000237 new->func = a->func;
paul9d11a192004-10-31 16:19:24 +0000238 new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname);
paule04ab742003-01-17 23:47:00 +0000239 return new;
240}
241
Chris Caputo228da422009-07-18 05:44:03 +0000242static void
243cpu_record_hash_free (void *a)
244{
245 struct cpu_thread_history *hist = a;
246
247 XFREE (MTYPE_THREAD_FUNCNAME, hist->funcname);
248 XFREE (MTYPE_THREAD_STATS, hist);
249}
250
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100251static void
paule04ab742003-01-17 23:47:00 +0000252vty_out_cpu_thread_history(struct vty* vty,
253 struct cpu_thread_history *a)
254{
ajs8b70d0b2005-04-28 01:31:13 +0000255#ifdef HAVE_RUSAGE
256 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
257 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
258 a->cpu.total/a->total_calls, a->cpu.max,
259 a->real.total/a->total_calls, a->real.max);
260#else
261 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
262 a->real.total/1000, a->real.total%1000, a->total_calls,
263 a->real.total/a->total_calls, a->real.max);
264#endif
265 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000266 a->types & (1 << THREAD_READ) ? 'R':' ',
267 a->types & (1 << THREAD_WRITE) ? 'W':' ',
268 a->types & (1 << THREAD_TIMER) ? 'T':' ',
269 a->types & (1 << THREAD_EVENT) ? 'E':' ',
270 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000271 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000272 a->funcname, VTY_NEWLINE);
273}
274
275static void
276cpu_record_hash_print(struct hash_backet *bucket,
277 void *args[])
278{
279 struct cpu_thread_history *totals = args[0];
280 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100281 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000282 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000283
paule04ab742003-01-17 23:47:00 +0000284 a = bucket->data;
285 if ( !(a->types & *filter) )
286 return;
287 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000288 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000289 totals->real.total += a->real.total;
290 if (totals->real.max < a->real.max)
291 totals->real.max = a->real.max;
292#ifdef HAVE_RUSAGE
293 totals->cpu.total += a->cpu.total;
294 if (totals->cpu.max < a->cpu.max)
295 totals->cpu.max = a->cpu.max;
296#endif
paule04ab742003-01-17 23:47:00 +0000297}
298
299static void
Paul Jakma41b23732009-06-30 16:12:49 +0100300cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000301{
302 struct cpu_thread_history tmp;
303 void *args[3] = {&tmp, vty, &filter};
304
305 memset(&tmp, 0, sizeof tmp);
Paul Jakma85261002010-01-11 16:30:45 +0000306 tmp.funcname = (char *)"TOTAL";
paule04ab742003-01-17 23:47:00 +0000307 tmp.types = filter;
308
ajs8b70d0b2005-04-28 01:31:13 +0000309#ifdef HAVE_RUSAGE
310 vty_out(vty, "%21s %18s %18s%s",
311 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
312#endif
313 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
314#ifdef HAVE_RUSAGE
315 vty_out(vty, " Avg uSec Max uSecs");
316#endif
317 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000318 hash_iterate(cpu_record,
319 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
320 args);
321
322 if (tmp.total_calls > 0)
323 vty_out_cpu_thread_history(vty, &tmp);
324}
325
326DEFUN(show_thread_cpu,
327 show_thread_cpu_cmd,
328 "show thread cpu [FILTER]",
329 SHOW_STR
330 "Thread information\n"
331 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000332 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000333{
334 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100335 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000336
337 if (argc > 0)
338 {
339 filter = 0;
340 while (argv[0][i] != '\0')
341 {
342 switch ( argv[0][i] )
343 {
344 case 'r':
345 case 'R':
346 filter |= (1 << THREAD_READ);
347 break;
348 case 'w':
349 case 'W':
350 filter |= (1 << THREAD_WRITE);
351 break;
352 case 't':
353 case 'T':
354 filter |= (1 << THREAD_TIMER);
355 break;
356 case 'e':
357 case 'E':
358 filter |= (1 << THREAD_EVENT);
359 break;
360 case 'x':
361 case 'X':
362 filter |= (1 << THREAD_EXECUTE);
363 break;
paula48b4e62005-04-22 00:43:47 +0000364 case 'b':
365 case 'B':
366 filter |= (1 << THREAD_BACKGROUND);
367 break;
paule04ab742003-01-17 23:47:00 +0000368 default:
369 break;
370 }
371 ++i;
372 }
373 if (filter == 0)
374 {
paula48b4e62005-04-22 00:43:47 +0000375 vty_out(vty, "Invalid filter \"%s\" specified,"
376 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000377 argv[0], VTY_NEWLINE);
378 return CMD_WARNING;
379 }
380 }
381
382 cpu_record_print(vty, filter);
383 return CMD_SUCCESS;
384}
Paul Jakmae276eb82010-01-09 16:15:00 +0000385
386static void
387cpu_record_hash_clear (struct hash_backet *bucket,
388 void *args)
389{
390 thread_type *filter = args;
391 struct cpu_thread_history *a = bucket->data;
392
393 a = bucket->data;
394 if ( !(a->types & *filter) )
395 return;
396
397 hash_release (cpu_record, bucket->data);
398}
399
400static void
401cpu_record_clear (thread_type filter)
402{
403 thread_type *tmp = &filter;
404 hash_iterate (cpu_record,
405 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
406 tmp);
407}
408
409DEFUN(clear_thread_cpu,
410 clear_thread_cpu_cmd,
411 "clear thread cpu [FILTER]",
412 "Clear stored data\n"
413 "Thread information\n"
414 "Thread CPU usage\n"
415 "Display filter (rwtexb)\n")
416{
417 int i = 0;
418 thread_type filter = (thread_type) -1U;
419
420 if (argc > 0)
421 {
422 filter = 0;
423 while (argv[0][i] != '\0')
424 {
425 switch ( argv[0][i] )
426 {
427 case 'r':
428 case 'R':
429 filter |= (1 << THREAD_READ);
430 break;
431 case 'w':
432 case 'W':
433 filter |= (1 << THREAD_WRITE);
434 break;
435 case 't':
436 case 'T':
437 filter |= (1 << THREAD_TIMER);
438 break;
439 case 'e':
440 case 'E':
441 filter |= (1 << THREAD_EVENT);
442 break;
443 case 'x':
444 case 'X':
445 filter |= (1 << THREAD_EXECUTE);
446 break;
447 case 'b':
448 case 'B':
449 filter |= (1 << THREAD_BACKGROUND);
450 break;
451 default:
452 break;
453 }
454 ++i;
455 }
456 if (filter == 0)
457 {
458 vty_out(vty, "Invalid filter \"%s\" specified,"
459 " must contain at least one of 'RWTEXB'%s",
460 argv[0], VTY_NEWLINE);
461 return CMD_WARNING;
462 }
463 }
464
465 cpu_record_clear (filter);
466 return CMD_SUCCESS;
467}
paule04ab742003-01-17 23:47:00 +0000468
paul718e3742002-12-13 20:15:29 +0000469/* List allocation and head/tail print out. */
470static void
471thread_list_debug (struct thread_list *list)
472{
473 printf ("count [%d] head [%p] tail [%p]\n",
474 list->count, list->head, list->tail);
475}
476
477/* Debug print for thread_master. */
paul8cc41982005-05-06 21:25:49 +0000478static void __attribute__ ((unused))
paul718e3742002-12-13 20:15:29 +0000479thread_master_debug (struct thread_master *m)
480{
481 printf ("-----------\n");
482 printf ("readlist : ");
483 thread_list_debug (&m->read);
484 printf ("writelist : ");
485 thread_list_debug (&m->write);
486 printf ("timerlist : ");
487 thread_list_debug (&m->timer);
488 printf ("eventlist : ");
489 thread_list_debug (&m->event);
490 printf ("unuselist : ");
491 thread_list_debug (&m->unuse);
paula48b4e62005-04-22 00:43:47 +0000492 printf ("bgndlist : ");
493 thread_list_debug (&m->background);
paul718e3742002-12-13 20:15:29 +0000494 printf ("total alloc: [%ld]\n", m->alloc);
495 printf ("-----------\n");
496}
497
498/* Allocate new thread master. */
499struct thread_master *
500thread_master_create ()
501{
paule04ab742003-01-17 23:47:00 +0000502 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000503 cpu_record
504 = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100505 (int (*) (const void *, const void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000506
paul718e3742002-12-13 20:15:29 +0000507 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
508 sizeof (struct thread_master));
509}
510
511/* Add a new thread to the list. */
512static void
513thread_list_add (struct thread_list *list, struct thread *thread)
514{
515 thread->next = NULL;
516 thread->prev = list->tail;
517 if (list->tail)
518 list->tail->next = thread;
519 else
520 list->head = thread;
521 list->tail = thread;
522 list->count++;
523}
524
525/* Add a new thread just before the point. */
526static void
527thread_list_add_before (struct thread_list *list,
528 struct thread *point,
529 struct thread *thread)
530{
531 thread->next = point;
532 thread->prev = point->prev;
533 if (point->prev)
534 point->prev->next = thread;
535 else
536 list->head = thread;
537 point->prev = thread;
538 list->count++;
539}
540
541/* Delete a thread from the list. */
542static struct thread *
543thread_list_delete (struct thread_list *list, struct thread *thread)
544{
545 if (thread->next)
546 thread->next->prev = thread->prev;
547 else
548 list->tail = thread->prev;
549 if (thread->prev)
550 thread->prev->next = thread->next;
551 else
552 list->head = thread->next;
553 thread->next = thread->prev = NULL;
554 list->count--;
555 return thread;
556}
557
558/* Move thread to unuse list. */
559static void
560thread_add_unuse (struct thread_master *m, struct thread *thread)
561{
paula48b4e62005-04-22 00:43:47 +0000562 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000563 assert (thread->next == NULL);
564 assert (thread->prev == NULL);
565 assert (thread->type == THREAD_UNUSED);
566 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000567 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000568}
569
570/* Free all unused thread. */
571static void
572thread_list_free (struct thread_master *m, struct thread_list *list)
573{
574 struct thread *t;
575 struct thread *next;
576
577 for (t = list->head; t; t = next)
578 {
579 next = t->next;
Chris Caputo228da422009-07-18 05:44:03 +0000580 if (t->funcname)
581 XFREE (MTYPE_THREAD_FUNCNAME, t->funcname);
paul718e3742002-12-13 20:15:29 +0000582 XFREE (MTYPE_THREAD, t);
583 list->count--;
584 m->alloc--;
585 }
586}
587
588/* Stop thread scheduler. */
589void
590thread_master_free (struct thread_master *m)
591{
592 thread_list_free (m, &m->read);
593 thread_list_free (m, &m->write);
594 thread_list_free (m, &m->timer);
595 thread_list_free (m, &m->event);
596 thread_list_free (m, &m->ready);
597 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000598 thread_list_free (m, &m->background);
599
paul718e3742002-12-13 20:15:29 +0000600 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000601
602 if (cpu_record)
603 {
604 hash_clean (cpu_record, cpu_record_hash_free);
605 hash_free (cpu_record);
606 cpu_record = NULL;
607 }
paul718e3742002-12-13 20:15:29 +0000608}
609
paul8cc41982005-05-06 21:25:49 +0000610/* Thread list is empty or not. */
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100611static int
paul8cc41982005-05-06 21:25:49 +0000612thread_empty (struct thread_list *list)
613{
614 return list->head ? 0 : 1;
615}
616
paul718e3742002-12-13 20:15:29 +0000617/* Delete top of the list and return it. */
618static struct thread *
619thread_trim_head (struct thread_list *list)
620{
paul8cc41982005-05-06 21:25:49 +0000621 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000622 return thread_list_delete (list, list->head);
623 return NULL;
624}
625
paul718e3742002-12-13 20:15:29 +0000626/* Return remain time in second. */
627unsigned long
628thread_timer_remain_second (struct thread *thread)
629{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000630 quagga_get_relative (NULL);
631
632 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
633 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000634 else
635 return 0;
636}
637
paule04ab742003-01-17 23:47:00 +0000638/* Trim blankspace and "()"s */
639static char *
hasso8c328f12004-10-05 21:01:23 +0000640strip_funcname (const char *funcname)
paule04ab742003-01-17 23:47:00 +0000641{
642 char buff[100];
643 char tmp, *ret, *e, *b = buff;
644
645 strncpy(buff, funcname, sizeof(buff));
646 buff[ sizeof(buff) -1] = '\0';
647 e = buff +strlen(buff) -1;
648
649 /* Wont work for funcname == "Word (explanation)" */
650
651 while (*b == ' ' || *b == '(')
652 ++b;
653 while (*e == ' ' || *e == ')')
654 --e;
655 e++;
656
657 tmp = *e;
658 *e = '\0';
paul9d11a192004-10-31 16:19:24 +0000659 ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b);
paule04ab742003-01-17 23:47:00 +0000660 *e = tmp;
661
662 return ret;
663}
664
paul718e3742002-12-13 20:15:29 +0000665/* Get new thread. */
666static struct thread *
667thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000668 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000669{
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000670 struct thread *thread = thread_trim_head (&m->unuse);
paul718e3742002-12-13 20:15:29 +0000671
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000672 if (thread)
paul2946f652003-03-27 23:48:24 +0000673 {
paul2946f652003-03-27 23:48:24 +0000674 if (thread->funcname)
paul9d11a192004-10-31 16:19:24 +0000675 XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname);
paul2946f652003-03-27 23:48:24 +0000676 }
paul718e3742002-12-13 20:15:29 +0000677 else
678 {
679 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
680 m->alloc++;
681 }
682 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000683 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000684 thread->master = m;
685 thread->func = func;
686 thread->arg = arg;
687
paule04ab742003-01-17 23:47:00 +0000688 thread->funcname = strip_funcname(funcname);
689
paul718e3742002-12-13 20:15:29 +0000690 return thread;
691}
692
693/* Add new read thread. */
694struct thread *
paule04ab742003-01-17 23:47:00 +0000695funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000696 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000697{
698 struct thread *thread;
699
700 assert (m != NULL);
701
702 if (FD_ISSET (fd, &m->readfd))
703 {
704 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
705 return NULL;
706 }
707
paule04ab742003-01-17 23:47:00 +0000708 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000709 FD_SET (fd, &m->readfd);
710 thread->u.fd = fd;
711 thread_list_add (&m->read, thread);
712
713 return thread;
714}
715
716/* Add new write thread. */
717struct thread *
paule04ab742003-01-17 23:47:00 +0000718funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000719 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000720{
721 struct thread *thread;
722
723 assert (m != NULL);
724
725 if (FD_ISSET (fd, &m->writefd))
726 {
727 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
728 return NULL;
729 }
730
paule04ab742003-01-17 23:47:00 +0000731 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000732 FD_SET (fd, &m->writefd);
733 thread->u.fd = fd;
734 thread_list_add (&m->write, thread);
735
736 return thread;
737}
738
paul98c91ac2004-10-05 14:57:50 +0000739static struct thread *
740funcname_thread_add_timer_timeval (struct thread_master *m,
741 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000742 int type,
paul98c91ac2004-10-05 14:57:50 +0000743 void *arg,
744 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000745 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000746{
paul718e3742002-12-13 20:15:29 +0000747 struct thread *thread;
paula48b4e62005-04-22 00:43:47 +0000748 struct thread_list *list;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000749 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000750 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000751
752 assert (m != NULL);
753
ajs8b70d0b2005-04-28 01:31:13 +0000754 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000755 assert (time_relative);
756
ajs8b70d0b2005-04-28 01:31:13 +0000757 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000758 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000759
760 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100761 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000762 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
763 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000764 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000765
766 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000767 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000768 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
769 break;
770
771 if (tt)
paula48b4e62005-04-22 00:43:47 +0000772 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000773 else
paula48b4e62005-04-22 00:43:47 +0000774 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000775
776 return thread;
777}
778
paul98c91ac2004-10-05 14:57:50 +0000779
780/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000781struct thread *
paul98c91ac2004-10-05 14:57:50 +0000782funcname_thread_add_timer (struct thread_master *m,
783 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000784 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000785{
paul98c91ac2004-10-05 14:57:50 +0000786 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000787
788 assert (m != NULL);
789
paul9076fbd2004-10-11 09:40:58 +0000790 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000791 trel.tv_usec = 0;
792
paula48b4e62005-04-22 00:43:47 +0000793 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
794 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000795}
796
797/* Add timer event thread with "millisecond" resolution */
798struct thread *
799funcname_thread_add_timer_msec (struct thread_master *m,
800 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000801 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000802{
803 struct timeval trel;
804
805 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000806
ajsaf04bd72004-12-28 17:00:12 +0000807 trel.tv_sec = timer / 1000;
808 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000809
paula48b4e62005-04-22 00:43:47 +0000810 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
811 arg, &trel, funcname);
812}
813
814/* Add a background thread, with an optional millisec delay */
815struct thread *
816funcname_thread_add_background (struct thread_master *m,
817 int (*func) (struct thread *),
818 void *arg, long delay,
819 const char *funcname)
820{
821 struct timeval trel;
822
823 assert (m != NULL);
824
825 if (delay)
826 {
827 trel.tv_sec = delay / 1000;
828 trel.tv_usec = 1000*(delay % 1000);
829 }
830 else
831 {
832 trel.tv_sec = 0;
833 trel.tv_usec = 0;
834 }
835
836 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
837 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000838}
839
paul718e3742002-12-13 20:15:29 +0000840/* Add simple event thread. */
841struct thread *
paule04ab742003-01-17 23:47:00 +0000842funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000843 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000844{
845 struct thread *thread;
846
847 assert (m != NULL);
848
paule04ab742003-01-17 23:47:00 +0000849 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000850 thread->u.val = val;
851 thread_list_add (&m->event, thread);
852
853 return thread;
854}
855
856/* Cancel thread from scheduler. */
857void
858thread_cancel (struct thread *thread)
859{
paula48b4e62005-04-22 00:43:47 +0000860 struct thread_list *list;
861
paul718e3742002-12-13 20:15:29 +0000862 switch (thread->type)
863 {
864 case THREAD_READ:
865 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
866 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000867 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000868 break;
869 case THREAD_WRITE:
870 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
871 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000872 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000873 break;
874 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000875 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000876 break;
877 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000878 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000879 break;
880 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000881 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000882 break;
paula48b4e62005-04-22 00:43:47 +0000883 case THREAD_BACKGROUND:
884 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000885 break;
paul718e3742002-12-13 20:15:29 +0000886 default:
paula48b4e62005-04-22 00:43:47 +0000887 return;
paul718e3742002-12-13 20:15:29 +0000888 break;
889 }
paula48b4e62005-04-22 00:43:47 +0000890 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000891 thread->type = THREAD_UNUSED;
892 thread_add_unuse (thread->master, thread);
893}
894
895/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000896unsigned int
paul718e3742002-12-13 20:15:29 +0000897thread_cancel_event (struct thread_master *m, void *arg)
898{
pauldc818072005-05-19 01:30:53 +0000899 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000900 struct thread *thread;
901
902 thread = m->event.head;
903 while (thread)
904 {
905 struct thread *t;
906
907 t = thread;
908 thread = t->next;
909
910 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000911 {
pauldc818072005-05-19 01:30:53 +0000912 ret++;
paula48b4e62005-04-22 00:43:47 +0000913 thread_list_delete (&m->event, t);
914 t->type = THREAD_UNUSED;
915 thread_add_unuse (m, t);
916 }
paul718e3742002-12-13 20:15:29 +0000917 }
Jorge Boncompte [DTI2]1b79fcb2012-05-07 15:17:31 +0000918
919 /* thread can be on the ready list too */
920 thread = m->ready.head;
921 while (thread)
922 {
923 struct thread *t;
924
925 t = thread;
926 thread = t->next;
927
928 if (t->arg == arg)
929 {
930 ret++;
931 thread_list_delete (&m->ready, t);
932 t->type = THREAD_UNUSED;
933 thread_add_unuse (m, t);
934 }
935 }
pauldc818072005-05-19 01:30:53 +0000936 return ret;
paul718e3742002-12-13 20:15:29 +0000937}
938
paula48b4e62005-04-22 00:43:47 +0000939static struct timeval *
940thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000941{
paul8cc41982005-05-06 21:25:49 +0000942 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000943 {
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000944 *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000945 return timer_val;
946 }
947 return NULL;
948}
paul718e3742002-12-13 20:15:29 +0000949
paul8cc41982005-05-06 21:25:49 +0000950static struct thread *
paul718e3742002-12-13 20:15:29 +0000951thread_run (struct thread_master *m, struct thread *thread,
952 struct thread *fetch)
953{
954 *fetch = *thread;
955 thread->type = THREAD_UNUSED;
Chris Caputo228da422009-07-18 05:44:03 +0000956 thread->funcname = NULL; /* thread_call will free fetch's copied pointer */
paul718e3742002-12-13 20:15:29 +0000957 thread_add_unuse (m, thread);
958 return fetch;
959}
960
paula48b4e62005-04-22 00:43:47 +0000961static int
962thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000963{
964 struct thread *thread;
965 struct thread *next;
966 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000967
968 assert (list);
969
paul718e3742002-12-13 20:15:29 +0000970 for (thread = list->head; thread; thread = next)
971 {
972 next = thread->next;
973
974 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000975 {
976 assert (FD_ISSET (THREAD_FD (thread), mfdset));
977 FD_CLR(THREAD_FD (thread), mfdset);
978 thread_list_delete (list, thread);
979 thread_list_add (&thread->master->ready, thread);
980 thread->type = THREAD_READY;
981 ready++;
982 }
paul718e3742002-12-13 20:15:29 +0000983 }
984 return ready;
985}
986
ajs8b70d0b2005-04-28 01:31:13 +0000987/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000988static unsigned int
989thread_timer_process (struct thread_list *list, struct timeval *timenow)
990{
991 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +0000992 struct thread *next;
paula48b4e62005-04-22 00:43:47 +0000993 unsigned int ready = 0;
994
Paul Jakmab5043aa2012-02-28 18:32:56 +0000995 for (thread = list->head; thread; thread = next)
ajs8b70d0b2005-04-28 01:31:13 +0000996 {
Paul Jakmab5043aa2012-02-28 18:32:56 +0000997 next = thread->next;
ajs8b70d0b2005-04-28 01:31:13 +0000998 if (timeval_cmp (*timenow, thread->u.sands) < 0)
999 return ready;
1000 thread_list_delete (list, thread);
1001 thread->type = THREAD_READY;
1002 thread_list_add (&thread->master->ready, thread);
1003 ready++;
1004 }
paula48b4e62005-04-22 00:43:47 +00001005 return ready;
1006}
1007
Paul Jakma2613abe2010-01-11 16:33:07 +00001008/* process a list en masse, e.g. for event thread lists */
1009static unsigned int
1010thread_process (struct thread_list *list)
1011{
1012 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +00001013 struct thread *next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001014 unsigned int ready = 0;
1015
Paul Jakmab5043aa2012-02-28 18:32:56 +00001016 for (thread = list->head; thread; thread = next)
Paul Jakma2613abe2010-01-11 16:33:07 +00001017 {
Paul Jakmab5043aa2012-02-28 18:32:56 +00001018 next = thread->next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001019 thread_list_delete (list, thread);
1020 thread->type = THREAD_READY;
1021 thread_list_add (&thread->master->ready, thread);
1022 ready++;
1023 }
1024 return ready;
1025}
1026
1027
paul718e3742002-12-13 20:15:29 +00001028/* Fetch next ready thread. */
1029struct thread *
1030thread_fetch (struct thread_master *m, struct thread *fetch)
1031{
paul718e3742002-12-13 20:15:29 +00001032 struct thread *thread;
1033 fd_set readfd;
1034 fd_set writefd;
1035 fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001036 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001037 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001038 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001039 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001040
1041 while (1)
1042 {
paula48b4e62005-04-22 00:43:47 +00001043 int num = 0;
paula48b4e62005-04-22 00:43:47 +00001044
Paul Jakma2613abe2010-01-11 16:33:07 +00001045 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001046 quagga_sigevent_process ();
1047
Paul Jakma2613abe2010-01-11 16:33:07 +00001048 /* Drain the ready queue of already scheduled jobs, before scheduling
1049 * more.
paula48b4e62005-04-22 00:43:47 +00001050 */
paul718e3742002-12-13 20:15:29 +00001051 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001052 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001053
Paul Jakma2613abe2010-01-11 16:33:07 +00001054 /* To be fair to all kinds of threads, and avoid starvation, we
1055 * need to be careful to consider all thread types for scheduling
1056 * in each quanta. I.e. we should not return early from here on.
1057 */
1058
1059 /* Normal event are the next highest priority. */
1060 thread_process (&m->event);
1061
paul718e3742002-12-13 20:15:29 +00001062 /* Structure copy. */
1063 readfd = m->readfd;
1064 writefd = m->writefd;
1065 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +00001066
1067 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001068 if (m->ready.count == 0)
1069 {
1070 quagga_get_relative (NULL);
1071 timer_wait = thread_timer_wait (&m->timer, &timer_val);
1072 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
1073
1074 if (timer_wait_bg &&
1075 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1076 timer_wait = timer_wait_bg;
1077 }
paula48b4e62005-04-22 00:43:47 +00001078
paul718e3742002-12-13 20:15:29 +00001079 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001080
1081 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001082 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001083 {
1084 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001085 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001086 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001087 return NULL;
1088 }
ajs8b70d0b2005-04-28 01:31:13 +00001089
1090 /* Check foreground timers. Historically, they have had higher
1091 priority than I/O threads, so let's push them onto the ready
1092 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001093 quagga_get_relative (NULL);
1094 thread_timer_process (&m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001095
1096 /* Got IO, process it */
1097 if (num > 0)
1098 {
1099 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001100 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +00001101 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001102 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +00001103 }
ajs8b70d0b2005-04-28 01:31:13 +00001104
1105#if 0
1106 /* If any threads were made ready above (I/O or foreground timer),
1107 perhaps we should avoid adding background timers to the ready
1108 list at this time. If this is code is uncommented, then background
1109 timer threads will not run unless there is nothing else to do. */
1110 if ((thread = thread_trim_head (&m->ready)) != NULL)
1111 return thread_run (m, thread, fetch);
1112#endif
1113
paula48b4e62005-04-22 00:43:47 +00001114 /* Background timer/events, lowest priority */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001115 thread_timer_process (&m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001116
ajs8b70d0b2005-04-28 01:31:13 +00001117 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001118 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001119 }
1120}
1121
ajs924b9222005-04-16 17:11:24 +00001122unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001123thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001124{
paul718e3742002-12-13 20:15:29 +00001125#ifdef HAVE_RUSAGE
1126 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001127 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1128 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001129#else
ajs8b70d0b2005-04-28 01:31:13 +00001130 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001131#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001132 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001133}
1134
ajs8b70d0b2005-04-28 01:31:13 +00001135/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1136 Note: we are using real (wall clock) time for this calculation.
1137 It could be argued that CPU time may make more sense in certain
1138 contexts. The things to consider are whether the thread may have
1139 blocked (in which case wall time increases, but CPU time does not),
1140 or whether the system is heavily loaded with other processes competing
1141 for CPU time. On balance, wall clock time seems to make sense.
1142 Plus it has the added benefit that gettimeofday should be faster
1143 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001144int
1145thread_should_yield (struct thread *thread)
1146{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001147 quagga_get_relative (NULL);
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001148 return (timeval_elapsed(relative_time, thread->real) >
ajs8b70d0b2005-04-28 01:31:13 +00001149 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001150}
1151
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001152void
1153thread_getrusage (RUSAGE_T *r)
1154{
1155 quagga_get_relative (NULL);
1156#ifdef HAVE_RUSAGE
1157 getrusage(RUSAGE_SELF, &(r->cpu));
1158#endif
1159 r->real = relative_time;
1160
1161#ifdef HAVE_CLOCK_MONOTONIC
1162 /* quagga_get_relative() only updates recent_time if gettimeofday
1163 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1164 * and guarantee to update it before threads are run...
1165 */
1166 quagga_gettimeofday(&recent_time);
1167#endif /* HAVE_CLOCK_MONOTONIC */
1168}
1169
paul718e3742002-12-13 20:15:29 +00001170/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001171 use that to get in-depth stats on the performance of the thread in addition
1172 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001173void
1174thread_call (struct thread *thread)
1175{
ajs8b70d0b2005-04-28 01:31:13 +00001176 unsigned long realtime, cputime;
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001177 RUSAGE_T before, after;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001178
1179 /* Cache a pointer to the relevant cpu history thread, if the thread
1180 * does not have it yet.
1181 *
1182 * Callers submitting 'dummy threads' hence must take care that
1183 * thread->cpu is NULL
1184 */
1185 if (!thread->hist)
1186 {
1187 struct cpu_thread_history tmp;
1188
1189 tmp.func = thread->func;
1190 tmp.funcname = thread->funcname;
1191
1192 thread->hist = hash_get (cpu_record, &tmp,
1193 (void * (*) (void *))cpu_record_hash_alloc);
1194 }
paul718e3742002-12-13 20:15:29 +00001195
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001196 GETRUSAGE (&before);
1197 thread->real = before.real;
paul718e3742002-12-13 20:15:29 +00001198
1199 (*thread->func) (thread);
1200
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001201 GETRUSAGE (&after);
paul718e3742002-12-13 20:15:29 +00001202
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001203 realtime = thread_consumed_time (&after, &before, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001204 thread->hist->real.total += realtime;
1205 if (thread->hist->real.max < realtime)
1206 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001207#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001208 thread->hist->cpu.total += cputime;
1209 if (thread->hist->cpu.max < cputime)
1210 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001211#endif
paule04ab742003-01-17 23:47:00 +00001212
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001213 ++(thread->hist->total_calls);
1214 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001215
ajs924b9222005-04-16 17:11:24 +00001216#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001217 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001218 {
1219 /*
1220 * We have a CPU Hog on our hands.
1221 * Whinge about it now, so we're aware this is yet another task
1222 * to fix.
1223 */
ajs8b70d0b2005-04-28 01:31:13 +00001224 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001225 thread->funcname,
1226 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001227 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001228 }
ajs924b9222005-04-16 17:11:24 +00001229#endif /* CONSUMED_TIME_CHECK */
Chris Caputo228da422009-07-18 05:44:03 +00001230
1231 XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname);
paul718e3742002-12-13 20:15:29 +00001232}
1233
1234/* Execute thread */
1235struct thread *
paule04ab742003-01-17 23:47:00 +00001236funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001237 int (*func)(struct thread *),
1238 void *arg,
paule04ab742003-01-17 23:47:00 +00001239 int val,
hasso8c328f12004-10-05 21:01:23 +00001240 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001241{
1242 struct thread dummy;
1243
1244 memset (&dummy, 0, sizeof (struct thread));
1245
1246 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001247 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001248 dummy.master = NULL;
1249 dummy.func = func;
1250 dummy.arg = arg;
1251 dummy.u.val = val;
paule04ab742003-01-17 23:47:00 +00001252 dummy.funcname = strip_funcname (funcname);
paul718e3742002-12-13 20:15:29 +00001253 thread_call (&dummy);
1254
paul9d11a192004-10-31 16:19:24 +00001255 XFREE (MTYPE_THREAD_FUNCNAME, dummy.funcname);
paul2946f652003-03-27 23:48:24 +00001256
paul718e3742002-12-13 20:15:29 +00001257 return NULL;
1258}