blob: 86d0ff8cb4b247b28aaa4e6134a7e17454dcca37 [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;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000238 strcpy(new->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
Chris Caputo228da422009-07-18 05:44:03 +0000247 XFREE (MTYPE_THREAD_STATS, hist);
248}
249
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100250static void
paule04ab742003-01-17 23:47:00 +0000251vty_out_cpu_thread_history(struct vty* vty,
252 struct cpu_thread_history *a)
253{
ajs8b70d0b2005-04-28 01:31:13 +0000254#ifdef HAVE_RUSAGE
255 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
256 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
257 a->cpu.total/a->total_calls, a->cpu.max,
258 a->real.total/a->total_calls, a->real.max);
259#else
260 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
261 a->real.total/1000, a->real.total%1000, a->total_calls,
262 a->real.total/a->total_calls, a->real.max);
263#endif
264 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000265 a->types & (1 << THREAD_READ) ? 'R':' ',
266 a->types & (1 << THREAD_WRITE) ? 'W':' ',
267 a->types & (1 << THREAD_TIMER) ? 'T':' ',
268 a->types & (1 << THREAD_EVENT) ? 'E':' ',
269 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000270 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000271 a->funcname, VTY_NEWLINE);
272}
273
274static void
275cpu_record_hash_print(struct hash_backet *bucket,
276 void *args[])
277{
278 struct cpu_thread_history *totals = args[0];
279 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100280 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000281 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000282
paule04ab742003-01-17 23:47:00 +0000283 a = bucket->data;
284 if ( !(a->types & *filter) )
285 return;
286 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000287 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000288 totals->real.total += a->real.total;
289 if (totals->real.max < a->real.max)
290 totals->real.max = a->real.max;
291#ifdef HAVE_RUSAGE
292 totals->cpu.total += a->cpu.total;
293 if (totals->cpu.max < a->cpu.max)
294 totals->cpu.max = a->cpu.max;
295#endif
paule04ab742003-01-17 23:47:00 +0000296}
297
298static void
Paul Jakma41b23732009-06-30 16:12:49 +0100299cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000300{
301 struct cpu_thread_history tmp;
302 void *args[3] = {&tmp, vty, &filter};
303
304 memset(&tmp, 0, sizeof tmp);
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000305 strcpy(tmp.funcname, "TOTAL");
paule04ab742003-01-17 23:47:00 +0000306 tmp.types = filter;
307
ajs8b70d0b2005-04-28 01:31:13 +0000308#ifdef HAVE_RUSAGE
309 vty_out(vty, "%21s %18s %18s%s",
310 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
311#endif
312 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
313#ifdef HAVE_RUSAGE
314 vty_out(vty, " Avg uSec Max uSecs");
315#endif
316 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000317 hash_iterate(cpu_record,
318 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
319 args);
320
321 if (tmp.total_calls > 0)
322 vty_out_cpu_thread_history(vty, &tmp);
323}
324
325DEFUN(show_thread_cpu,
326 show_thread_cpu_cmd,
327 "show thread cpu [FILTER]",
328 SHOW_STR
329 "Thread information\n"
330 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000331 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000332{
333 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100334 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000335
336 if (argc > 0)
337 {
338 filter = 0;
339 while (argv[0][i] != '\0')
340 {
341 switch ( argv[0][i] )
342 {
343 case 'r':
344 case 'R':
345 filter |= (1 << THREAD_READ);
346 break;
347 case 'w':
348 case 'W':
349 filter |= (1 << THREAD_WRITE);
350 break;
351 case 't':
352 case 'T':
353 filter |= (1 << THREAD_TIMER);
354 break;
355 case 'e':
356 case 'E':
357 filter |= (1 << THREAD_EVENT);
358 break;
359 case 'x':
360 case 'X':
361 filter |= (1 << THREAD_EXECUTE);
362 break;
paula48b4e62005-04-22 00:43:47 +0000363 case 'b':
364 case 'B':
365 filter |= (1 << THREAD_BACKGROUND);
366 break;
paule04ab742003-01-17 23:47:00 +0000367 default:
368 break;
369 }
370 ++i;
371 }
372 if (filter == 0)
373 {
paula48b4e62005-04-22 00:43:47 +0000374 vty_out(vty, "Invalid filter \"%s\" specified,"
375 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000376 argv[0], VTY_NEWLINE);
377 return CMD_WARNING;
378 }
379 }
380
381 cpu_record_print(vty, filter);
382 return CMD_SUCCESS;
383}
Paul Jakmae276eb82010-01-09 16:15:00 +0000384
385static void
386cpu_record_hash_clear (struct hash_backet *bucket,
387 void *args)
388{
389 thread_type *filter = args;
390 struct cpu_thread_history *a = bucket->data;
391
392 a = bucket->data;
393 if ( !(a->types & *filter) )
394 return;
395
396 hash_release (cpu_record, bucket->data);
397}
398
399static void
400cpu_record_clear (thread_type filter)
401{
402 thread_type *tmp = &filter;
403 hash_iterate (cpu_record,
404 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
405 tmp);
406}
407
408DEFUN(clear_thread_cpu,
409 clear_thread_cpu_cmd,
410 "clear thread cpu [FILTER]",
411 "Clear stored data\n"
412 "Thread information\n"
413 "Thread CPU usage\n"
414 "Display filter (rwtexb)\n")
415{
416 int i = 0;
417 thread_type filter = (thread_type) -1U;
418
419 if (argc > 0)
420 {
421 filter = 0;
422 while (argv[0][i] != '\0')
423 {
424 switch ( argv[0][i] )
425 {
426 case 'r':
427 case 'R':
428 filter |= (1 << THREAD_READ);
429 break;
430 case 'w':
431 case 'W':
432 filter |= (1 << THREAD_WRITE);
433 break;
434 case 't':
435 case 'T':
436 filter |= (1 << THREAD_TIMER);
437 break;
438 case 'e':
439 case 'E':
440 filter |= (1 << THREAD_EVENT);
441 break;
442 case 'x':
443 case 'X':
444 filter |= (1 << THREAD_EXECUTE);
445 break;
446 case 'b':
447 case 'B':
448 filter |= (1 << THREAD_BACKGROUND);
449 break;
450 default:
451 break;
452 }
453 ++i;
454 }
455 if (filter == 0)
456 {
457 vty_out(vty, "Invalid filter \"%s\" specified,"
458 " must contain at least one of 'RWTEXB'%s",
459 argv[0], VTY_NEWLINE);
460 return CMD_WARNING;
461 }
462 }
463
464 cpu_record_clear (filter);
465 return CMD_SUCCESS;
466}
paule04ab742003-01-17 23:47:00 +0000467
paul718e3742002-12-13 20:15:29 +0000468/* List allocation and head/tail print out. */
469static void
470thread_list_debug (struct thread_list *list)
471{
472 printf ("count [%d] head [%p] tail [%p]\n",
473 list->count, list->head, list->tail);
474}
475
476/* Debug print for thread_master. */
paul8cc41982005-05-06 21:25:49 +0000477static void __attribute__ ((unused))
paul718e3742002-12-13 20:15:29 +0000478thread_master_debug (struct thread_master *m)
479{
480 printf ("-----------\n");
481 printf ("readlist : ");
482 thread_list_debug (&m->read);
483 printf ("writelist : ");
484 thread_list_debug (&m->write);
485 printf ("timerlist : ");
486 thread_list_debug (&m->timer);
487 printf ("eventlist : ");
488 thread_list_debug (&m->event);
489 printf ("unuselist : ");
490 thread_list_debug (&m->unuse);
paula48b4e62005-04-22 00:43:47 +0000491 printf ("bgndlist : ");
492 thread_list_debug (&m->background);
paul718e3742002-12-13 20:15:29 +0000493 printf ("total alloc: [%ld]\n", m->alloc);
494 printf ("-----------\n");
495}
496
497/* Allocate new thread master. */
498struct thread_master *
499thread_master_create ()
500{
paule04ab742003-01-17 23:47:00 +0000501 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000502 cpu_record
503 = hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100504 (int (*) (const void *, const void *))cpu_record_hash_cmp);
paula48b4e62005-04-22 00:43:47 +0000505
paul718e3742002-12-13 20:15:29 +0000506 return (struct thread_master *) XCALLOC (MTYPE_THREAD_MASTER,
507 sizeof (struct thread_master));
508}
509
510/* Add a new thread to the list. */
511static void
512thread_list_add (struct thread_list *list, struct thread *thread)
513{
514 thread->next = NULL;
515 thread->prev = list->tail;
516 if (list->tail)
517 list->tail->next = thread;
518 else
519 list->head = thread;
520 list->tail = thread;
521 list->count++;
522}
523
524/* Add a new thread just before the point. */
525static void
526thread_list_add_before (struct thread_list *list,
527 struct thread *point,
528 struct thread *thread)
529{
530 thread->next = point;
531 thread->prev = point->prev;
532 if (point->prev)
533 point->prev->next = thread;
534 else
535 list->head = thread;
536 point->prev = thread;
537 list->count++;
538}
539
540/* Delete a thread from the list. */
541static struct thread *
542thread_list_delete (struct thread_list *list, struct thread *thread)
543{
544 if (thread->next)
545 thread->next->prev = thread->prev;
546 else
547 list->tail = thread->prev;
548 if (thread->prev)
549 thread->prev->next = thread->next;
550 else
551 list->head = thread->next;
552 thread->next = thread->prev = NULL;
553 list->count--;
554 return thread;
555}
556
557/* Move thread to unuse list. */
558static void
559thread_add_unuse (struct thread_master *m, struct thread *thread)
560{
paula48b4e62005-04-22 00:43:47 +0000561 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000562 assert (thread->next == NULL);
563 assert (thread->prev == NULL);
564 assert (thread->type == THREAD_UNUSED);
565 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000566 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000567}
568
569/* Free all unused thread. */
570static void
571thread_list_free (struct thread_master *m, struct thread_list *list)
572{
573 struct thread *t;
574 struct thread *next;
575
576 for (t = list->head; t; t = next)
577 {
578 next = t->next;
579 XFREE (MTYPE_THREAD, t);
580 list->count--;
581 m->alloc--;
582 }
583}
584
585/* Stop thread scheduler. */
586void
587thread_master_free (struct thread_master *m)
588{
589 thread_list_free (m, &m->read);
590 thread_list_free (m, &m->write);
591 thread_list_free (m, &m->timer);
592 thread_list_free (m, &m->event);
593 thread_list_free (m, &m->ready);
594 thread_list_free (m, &m->unuse);
paula48b4e62005-04-22 00:43:47 +0000595 thread_list_free (m, &m->background);
596
paul718e3742002-12-13 20:15:29 +0000597 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000598
599 if (cpu_record)
600 {
601 hash_clean (cpu_record, cpu_record_hash_free);
602 hash_free (cpu_record);
603 cpu_record = NULL;
604 }
paul718e3742002-12-13 20:15:29 +0000605}
606
paul8cc41982005-05-06 21:25:49 +0000607/* Thread list is empty or not. */
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100608static int
paul8cc41982005-05-06 21:25:49 +0000609thread_empty (struct thread_list *list)
610{
611 return list->head ? 0 : 1;
612}
613
paul718e3742002-12-13 20:15:29 +0000614/* Delete top of the list and return it. */
615static struct thread *
616thread_trim_head (struct thread_list *list)
617{
paul8cc41982005-05-06 21:25:49 +0000618 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000619 return thread_list_delete (list, list->head);
620 return NULL;
621}
622
paul718e3742002-12-13 20:15:29 +0000623/* Return remain time in second. */
624unsigned long
625thread_timer_remain_second (struct thread *thread)
626{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000627 quagga_get_relative (NULL);
628
629 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
630 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000631 else
632 return 0;
633}
634
paule04ab742003-01-17 23:47:00 +0000635/* Trim blankspace and "()"s */
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000636void
637strip_funcname (char *dest, const char *funcname)
paule04ab742003-01-17 23:47:00 +0000638{
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000639 char buff[FUNCNAME_LEN];
640 char tmp, *e, *b = buff;
paule04ab742003-01-17 23:47:00 +0000641
642 strncpy(buff, funcname, sizeof(buff));
643 buff[ sizeof(buff) -1] = '\0';
644 e = buff +strlen(buff) -1;
645
646 /* Wont work for funcname == "Word (explanation)" */
647
648 while (*b == ' ' || *b == '(')
649 ++b;
650 while (*e == ' ' || *e == ')')
651 --e;
652 e++;
653
654 tmp = *e;
655 *e = '\0';
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000656 strcpy (dest, b);
paule04ab742003-01-17 23:47:00 +0000657 *e = tmp;
paule04ab742003-01-17 23:47:00 +0000658}
659
paul718e3742002-12-13 20:15:29 +0000660/* Get new thread. */
661static struct thread *
662thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000663 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000664{
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000665 struct thread *thread = thread_trim_head (&m->unuse);
paul718e3742002-12-13 20:15:29 +0000666
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000667 if (! thread)
paul718e3742002-12-13 20:15:29 +0000668 {
669 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
670 m->alloc++;
671 }
672 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000673 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000674 thread->master = m;
675 thread->func = func;
676 thread->arg = arg;
677
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000678 strip_funcname (thread->funcname, funcname);
paule04ab742003-01-17 23:47:00 +0000679
paul718e3742002-12-13 20:15:29 +0000680 return thread;
681}
682
683/* Add new read thread. */
684struct thread *
paule04ab742003-01-17 23:47:00 +0000685funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000686 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000687{
688 struct thread *thread;
689
690 assert (m != NULL);
691
692 if (FD_ISSET (fd, &m->readfd))
693 {
694 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
695 return NULL;
696 }
697
paule04ab742003-01-17 23:47:00 +0000698 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000699 FD_SET (fd, &m->readfd);
700 thread->u.fd = fd;
701 thread_list_add (&m->read, thread);
702
703 return thread;
704}
705
706/* Add new write thread. */
707struct thread *
paule04ab742003-01-17 23:47:00 +0000708funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000709 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000710{
711 struct thread *thread;
712
713 assert (m != NULL);
714
715 if (FD_ISSET (fd, &m->writefd))
716 {
717 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
718 return NULL;
719 }
720
paule04ab742003-01-17 23:47:00 +0000721 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000722 FD_SET (fd, &m->writefd);
723 thread->u.fd = fd;
724 thread_list_add (&m->write, thread);
725
726 return thread;
727}
728
paul98c91ac2004-10-05 14:57:50 +0000729static struct thread *
730funcname_thread_add_timer_timeval (struct thread_master *m,
731 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000732 int type,
paul98c91ac2004-10-05 14:57:50 +0000733 void *arg,
734 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000735 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000736{
paul718e3742002-12-13 20:15:29 +0000737 struct thread *thread;
paula48b4e62005-04-22 00:43:47 +0000738 struct thread_list *list;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000739 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000740 struct thread *tt;
paul718e3742002-12-13 20:15:29 +0000741
742 assert (m != NULL);
743
ajs8b70d0b2005-04-28 01:31:13 +0000744 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000745 assert (time_relative);
746
ajs8b70d0b2005-04-28 01:31:13 +0000747 list = ((type == THREAD_TIMER) ? &m->timer : &m->background);
paula48b4e62005-04-22 00:43:47 +0000748 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000749
750 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100751 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000752 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
753 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000754 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000755
756 /* Sort by timeval. */
paula48b4e62005-04-22 00:43:47 +0000757 for (tt = list->head; tt; tt = tt->next)
paul718e3742002-12-13 20:15:29 +0000758 if (timeval_cmp (thread->u.sands, tt->u.sands) <= 0)
759 break;
760
761 if (tt)
paula48b4e62005-04-22 00:43:47 +0000762 thread_list_add_before (list, tt, thread);
paul718e3742002-12-13 20:15:29 +0000763 else
paula48b4e62005-04-22 00:43:47 +0000764 thread_list_add (list, thread);
paul718e3742002-12-13 20:15:29 +0000765
766 return thread;
767}
768
paul98c91ac2004-10-05 14:57:50 +0000769
770/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000771struct thread *
paul98c91ac2004-10-05 14:57:50 +0000772funcname_thread_add_timer (struct thread_master *m,
773 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000774 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000775{
paul98c91ac2004-10-05 14:57:50 +0000776 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000777
778 assert (m != NULL);
779
paul9076fbd2004-10-11 09:40:58 +0000780 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000781 trel.tv_usec = 0;
782
paula48b4e62005-04-22 00:43:47 +0000783 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
784 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000785}
786
787/* Add timer event thread with "millisecond" resolution */
788struct thread *
789funcname_thread_add_timer_msec (struct thread_master *m,
790 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000791 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000792{
793 struct timeval trel;
794
795 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000796
ajsaf04bd72004-12-28 17:00:12 +0000797 trel.tv_sec = timer / 1000;
798 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000799
paula48b4e62005-04-22 00:43:47 +0000800 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
801 arg, &trel, funcname);
802}
803
804/* Add a background thread, with an optional millisec delay */
805struct thread *
806funcname_thread_add_background (struct thread_master *m,
807 int (*func) (struct thread *),
808 void *arg, long delay,
809 const char *funcname)
810{
811 struct timeval trel;
812
813 assert (m != NULL);
814
815 if (delay)
816 {
817 trel.tv_sec = delay / 1000;
818 trel.tv_usec = 1000*(delay % 1000);
819 }
820 else
821 {
822 trel.tv_sec = 0;
823 trel.tv_usec = 0;
824 }
825
826 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
827 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000828}
829
paul718e3742002-12-13 20:15:29 +0000830/* Add simple event thread. */
831struct thread *
paule04ab742003-01-17 23:47:00 +0000832funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000833 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000834{
835 struct thread *thread;
836
837 assert (m != NULL);
838
paule04ab742003-01-17 23:47:00 +0000839 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000840 thread->u.val = val;
841 thread_list_add (&m->event, thread);
842
843 return thread;
844}
845
846/* Cancel thread from scheduler. */
847void
848thread_cancel (struct thread *thread)
849{
paula48b4e62005-04-22 00:43:47 +0000850 struct thread_list *list;
851
paul718e3742002-12-13 20:15:29 +0000852 switch (thread->type)
853 {
854 case THREAD_READ:
855 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
856 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000857 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000858 break;
859 case THREAD_WRITE:
860 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
861 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000862 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000863 break;
864 case THREAD_TIMER:
paula48b4e62005-04-22 00:43:47 +0000865 list = &thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000866 break;
867 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000868 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000869 break;
870 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000871 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000872 break;
paula48b4e62005-04-22 00:43:47 +0000873 case THREAD_BACKGROUND:
874 list = &thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000875 break;
paul718e3742002-12-13 20:15:29 +0000876 default:
paula48b4e62005-04-22 00:43:47 +0000877 return;
paul718e3742002-12-13 20:15:29 +0000878 break;
879 }
paula48b4e62005-04-22 00:43:47 +0000880 thread_list_delete (list, thread);
paul718e3742002-12-13 20:15:29 +0000881 thread->type = THREAD_UNUSED;
882 thread_add_unuse (thread->master, thread);
883}
884
885/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000886unsigned int
paul718e3742002-12-13 20:15:29 +0000887thread_cancel_event (struct thread_master *m, void *arg)
888{
pauldc818072005-05-19 01:30:53 +0000889 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000890 struct thread *thread;
891
892 thread = m->event.head;
893 while (thread)
894 {
895 struct thread *t;
896
897 t = thread;
898 thread = t->next;
899
900 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000901 {
pauldc818072005-05-19 01:30:53 +0000902 ret++;
paula48b4e62005-04-22 00:43:47 +0000903 thread_list_delete (&m->event, t);
904 t->type = THREAD_UNUSED;
905 thread_add_unuse (m, t);
906 }
paul718e3742002-12-13 20:15:29 +0000907 }
Jorge Boncompte [DTI2]1b79fcb2012-05-07 15:17:31 +0000908
909 /* thread can be on the ready list too */
910 thread = m->ready.head;
911 while (thread)
912 {
913 struct thread *t;
914
915 t = thread;
916 thread = t->next;
917
918 if (t->arg == arg)
919 {
920 ret++;
921 thread_list_delete (&m->ready, t);
922 t->type = THREAD_UNUSED;
923 thread_add_unuse (m, t);
924 }
925 }
pauldc818072005-05-19 01:30:53 +0000926 return ret;
paul718e3742002-12-13 20:15:29 +0000927}
928
paula48b4e62005-04-22 00:43:47 +0000929static struct timeval *
930thread_timer_wait (struct thread_list *tlist, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000931{
paul8cc41982005-05-06 21:25:49 +0000932 if (!thread_empty (tlist))
paul718e3742002-12-13 20:15:29 +0000933 {
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000934 *timer_val = timeval_subtract (tlist->head->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000935 return timer_val;
936 }
937 return NULL;
938}
paul718e3742002-12-13 20:15:29 +0000939
paul8cc41982005-05-06 21:25:49 +0000940static struct thread *
paul718e3742002-12-13 20:15:29 +0000941thread_run (struct thread_master *m, struct thread *thread,
942 struct thread *fetch)
943{
944 *fetch = *thread;
945 thread->type = THREAD_UNUSED;
946 thread_add_unuse (m, thread);
947 return fetch;
948}
949
paula48b4e62005-04-22 00:43:47 +0000950static int
951thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000952{
953 struct thread *thread;
954 struct thread *next;
955 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000956
957 assert (list);
958
paul718e3742002-12-13 20:15:29 +0000959 for (thread = list->head; thread; thread = next)
960 {
961 next = thread->next;
962
963 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +0000964 {
965 assert (FD_ISSET (THREAD_FD (thread), mfdset));
966 FD_CLR(THREAD_FD (thread), mfdset);
967 thread_list_delete (list, thread);
968 thread_list_add (&thread->master->ready, thread);
969 thread->type = THREAD_READY;
970 ready++;
971 }
paul718e3742002-12-13 20:15:29 +0000972 }
973 return ready;
974}
975
ajs8b70d0b2005-04-28 01:31:13 +0000976/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +0000977static unsigned int
978thread_timer_process (struct thread_list *list, struct timeval *timenow)
979{
980 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +0000981 struct thread *next;
paula48b4e62005-04-22 00:43:47 +0000982 unsigned int ready = 0;
983
Paul Jakmab5043aa2012-02-28 18:32:56 +0000984 for (thread = list->head; thread; thread = next)
ajs8b70d0b2005-04-28 01:31:13 +0000985 {
Paul Jakmab5043aa2012-02-28 18:32:56 +0000986 next = thread->next;
ajs8b70d0b2005-04-28 01:31:13 +0000987 if (timeval_cmp (*timenow, thread->u.sands) < 0)
988 return ready;
989 thread_list_delete (list, thread);
990 thread->type = THREAD_READY;
991 thread_list_add (&thread->master->ready, thread);
992 ready++;
993 }
paula48b4e62005-04-22 00:43:47 +0000994 return ready;
995}
996
Paul Jakma2613abe2010-01-11 16:33:07 +0000997/* process a list en masse, e.g. for event thread lists */
998static unsigned int
999thread_process (struct thread_list *list)
1000{
1001 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +00001002 struct thread *next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001003 unsigned int ready = 0;
1004
Paul Jakmab5043aa2012-02-28 18:32:56 +00001005 for (thread = list->head; thread; thread = next)
Paul Jakma2613abe2010-01-11 16:33:07 +00001006 {
Paul Jakmab5043aa2012-02-28 18:32:56 +00001007 next = thread->next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001008 thread_list_delete (list, thread);
1009 thread->type = THREAD_READY;
1010 thread_list_add (&thread->master->ready, thread);
1011 ready++;
1012 }
1013 return ready;
1014}
1015
1016
paul718e3742002-12-13 20:15:29 +00001017/* Fetch next ready thread. */
1018struct thread *
1019thread_fetch (struct thread_master *m, struct thread *fetch)
1020{
paul718e3742002-12-13 20:15:29 +00001021 struct thread *thread;
1022 fd_set readfd;
1023 fd_set writefd;
1024 fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001025 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001026 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001027 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001028 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001029
1030 while (1)
1031 {
paula48b4e62005-04-22 00:43:47 +00001032 int num = 0;
paula48b4e62005-04-22 00:43:47 +00001033
Paul Jakma2613abe2010-01-11 16:33:07 +00001034 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001035 quagga_sigevent_process ();
1036
Paul Jakma2613abe2010-01-11 16:33:07 +00001037 /* Drain the ready queue of already scheduled jobs, before scheduling
1038 * more.
paula48b4e62005-04-22 00:43:47 +00001039 */
paul718e3742002-12-13 20:15:29 +00001040 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001041 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001042
Paul Jakma2613abe2010-01-11 16:33:07 +00001043 /* To be fair to all kinds of threads, and avoid starvation, we
1044 * need to be careful to consider all thread types for scheduling
1045 * in each quanta. I.e. we should not return early from here on.
1046 */
1047
1048 /* Normal event are the next highest priority. */
1049 thread_process (&m->event);
1050
paul718e3742002-12-13 20:15:29 +00001051 /* Structure copy. */
1052 readfd = m->readfd;
1053 writefd = m->writefd;
1054 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +00001055
1056 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001057 if (m->ready.count == 0)
1058 {
1059 quagga_get_relative (NULL);
1060 timer_wait = thread_timer_wait (&m->timer, &timer_val);
1061 timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg);
1062
1063 if (timer_wait_bg &&
1064 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1065 timer_wait = timer_wait_bg;
1066 }
paula48b4e62005-04-22 00:43:47 +00001067
paul718e3742002-12-13 20:15:29 +00001068 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001069
1070 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001071 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001072 {
1073 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001074 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001075 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001076 return NULL;
1077 }
ajs8b70d0b2005-04-28 01:31:13 +00001078
1079 /* Check foreground timers. Historically, they have had higher
1080 priority than I/O threads, so let's push them onto the ready
1081 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001082 quagga_get_relative (NULL);
1083 thread_timer_process (&m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001084
1085 /* Got IO, process it */
1086 if (num > 0)
1087 {
1088 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001089 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +00001090 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001091 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +00001092 }
ajs8b70d0b2005-04-28 01:31:13 +00001093
1094#if 0
1095 /* If any threads were made ready above (I/O or foreground timer),
1096 perhaps we should avoid adding background timers to the ready
1097 list at this time. If this is code is uncommented, then background
1098 timer threads will not run unless there is nothing else to do. */
1099 if ((thread = thread_trim_head (&m->ready)) != NULL)
1100 return thread_run (m, thread, fetch);
1101#endif
1102
paula48b4e62005-04-22 00:43:47 +00001103 /* Background timer/events, lowest priority */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001104 thread_timer_process (&m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001105
ajs8b70d0b2005-04-28 01:31:13 +00001106 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001107 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001108 }
1109}
1110
ajs924b9222005-04-16 17:11:24 +00001111unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001112thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001113{
paul718e3742002-12-13 20:15:29 +00001114#ifdef HAVE_RUSAGE
1115 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001116 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1117 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001118#else
ajs8b70d0b2005-04-28 01:31:13 +00001119 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001120#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001121 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001122}
1123
ajs8b70d0b2005-04-28 01:31:13 +00001124/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1125 Note: we are using real (wall clock) time for this calculation.
1126 It could be argued that CPU time may make more sense in certain
1127 contexts. The things to consider are whether the thread may have
1128 blocked (in which case wall time increases, but CPU time does not),
1129 or whether the system is heavily loaded with other processes competing
1130 for CPU time. On balance, wall clock time seems to make sense.
1131 Plus it has the added benefit that gettimeofday should be faster
1132 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001133int
1134thread_should_yield (struct thread *thread)
1135{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001136 quagga_get_relative (NULL);
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001137 return (timeval_elapsed(relative_time, thread->real) >
ajs8b70d0b2005-04-28 01:31:13 +00001138 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001139}
1140
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001141void
1142thread_getrusage (RUSAGE_T *r)
1143{
1144 quagga_get_relative (NULL);
1145#ifdef HAVE_RUSAGE
1146 getrusage(RUSAGE_SELF, &(r->cpu));
1147#endif
1148 r->real = relative_time;
1149
1150#ifdef HAVE_CLOCK_MONOTONIC
1151 /* quagga_get_relative() only updates recent_time if gettimeofday
1152 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1153 * and guarantee to update it before threads are run...
1154 */
1155 quagga_gettimeofday(&recent_time);
1156#endif /* HAVE_CLOCK_MONOTONIC */
1157}
1158
paul718e3742002-12-13 20:15:29 +00001159/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001160 use that to get in-depth stats on the performance of the thread in addition
1161 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001162void
1163thread_call (struct thread *thread)
1164{
ajs8b70d0b2005-04-28 01:31:13 +00001165 unsigned long realtime, cputime;
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001166 RUSAGE_T before, after;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001167
1168 /* Cache a pointer to the relevant cpu history thread, if the thread
1169 * does not have it yet.
1170 *
1171 * Callers submitting 'dummy threads' hence must take care that
1172 * thread->cpu is NULL
1173 */
1174 if (!thread->hist)
1175 {
1176 struct cpu_thread_history tmp;
1177
1178 tmp.func = thread->func;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001179 strcpy(tmp.funcname, thread->funcname);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001180
1181 thread->hist = hash_get (cpu_record, &tmp,
1182 (void * (*) (void *))cpu_record_hash_alloc);
1183 }
paul718e3742002-12-13 20:15:29 +00001184
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001185 GETRUSAGE (&before);
1186 thread->real = before.real;
paul718e3742002-12-13 20:15:29 +00001187
1188 (*thread->func) (thread);
1189
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001190 GETRUSAGE (&after);
paul718e3742002-12-13 20:15:29 +00001191
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001192 realtime = thread_consumed_time (&after, &before, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001193 thread->hist->real.total += realtime;
1194 if (thread->hist->real.max < realtime)
1195 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001196#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001197 thread->hist->cpu.total += cputime;
1198 if (thread->hist->cpu.max < cputime)
1199 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001200#endif
paule04ab742003-01-17 23:47:00 +00001201
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001202 ++(thread->hist->total_calls);
1203 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001204
ajs924b9222005-04-16 17:11:24 +00001205#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001206 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001207 {
1208 /*
1209 * We have a CPU Hog on our hands.
1210 * Whinge about it now, so we're aware this is yet another task
1211 * to fix.
1212 */
ajs8b70d0b2005-04-28 01:31:13 +00001213 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001214 thread->funcname,
1215 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001216 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001217 }
ajs924b9222005-04-16 17:11:24 +00001218#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +00001219}
1220
1221/* Execute thread */
1222struct thread *
paule04ab742003-01-17 23:47:00 +00001223funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001224 int (*func)(struct thread *),
1225 void *arg,
paule04ab742003-01-17 23:47:00 +00001226 int val,
hasso8c328f12004-10-05 21:01:23 +00001227 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001228{
1229 struct thread dummy;
1230
1231 memset (&dummy, 0, sizeof (struct thread));
1232
1233 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001234 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001235 dummy.master = NULL;
1236 dummy.func = func;
1237 dummy.arg = arg;
1238 dummy.u.val = val;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001239 strip_funcname (dummy.funcname, funcname);
paul718e3742002-12-13 20:15:29 +00001240 thread_call (&dummy);
1241
paul718e3742002-12-13 20:15:29 +00001242 return NULL;
1243}