blob: e2a37b14960da6ba95554d5228da77bcad93e805 [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"
Christian Franke4becea72013-11-19 14:11:42 +000030#include "pqueue.h"
paule04ab742003-01-17 23:47:00 +000031#include "command.h"
paul05c447d2004-07-22 19:14:27 +000032#include "sigevent.h"
Vincent Bernatd6be5fb2012-05-24 09:44:43 +020033
34#if defined HAVE_SNMP && defined SNMP_AGENTX
35#include <net-snmp/net-snmp-config.h>
36#include <net-snmp/net-snmp-includes.h>
37#include <net-snmp/agent/net-snmp-agent-includes.h>
38#include <net-snmp/agent/snmp_vars.h>
39
40extern int agentx_enabled;
41#endif
42
Hasso Tepper3b96b782012-10-11 11:31:54 +000043#if defined(__APPLE__)
44#include <mach/mach.h>
45#include <mach/mach_time.h>
46#endif
47
paule04ab742003-01-17 23:47:00 +000048
Paul Jakmadb9c0df2006-08-27 06:44:02 +000049/* Recent absolute time of day */
ajs8b70d0b2005-04-28 01:31:13 +000050struct timeval recent_time;
Paul Jakmadb9c0df2006-08-27 06:44:02 +000051static struct timeval last_recent_time;
52/* Relative time, since startup */
53static struct timeval relative_time;
54static struct timeval relative_time_base;
55/* init flag */
56static unsigned short timers_inited;
57
paule04ab742003-01-17 23:47:00 +000058static struct hash *cpu_record = NULL;
paul718e3742002-12-13 20:15:29 +000059
60/* Struct timeval's tv_usec one second value. */
61#define TIMER_SECOND_MICRO 1000000L
62
ajs8b70d0b2005-04-28 01:31:13 +000063/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO).
64 And change negative values to 0. */
paula48b4e62005-04-22 00:43:47 +000065static struct timeval
paul718e3742002-12-13 20:15:29 +000066timeval_adjust (struct timeval a)
67{
68 while (a.tv_usec >= TIMER_SECOND_MICRO)
69 {
70 a.tv_usec -= TIMER_SECOND_MICRO;
71 a.tv_sec++;
72 }
73
74 while (a.tv_usec < 0)
75 {
76 a.tv_usec += TIMER_SECOND_MICRO;
77 a.tv_sec--;
78 }
79
80 if (a.tv_sec < 0)
ajs8b70d0b2005-04-28 01:31:13 +000081 /* Change negative timeouts to 0. */
82 a.tv_sec = a.tv_usec = 0;
paul718e3742002-12-13 20:15:29 +000083
84 return a;
85}
86
87static struct timeval
88timeval_subtract (struct timeval a, struct timeval b)
89{
90 struct timeval ret;
91
92 ret.tv_usec = a.tv_usec - b.tv_usec;
93 ret.tv_sec = a.tv_sec - b.tv_sec;
94
95 return timeval_adjust (ret);
96}
97
ajs8b70d0b2005-04-28 01:31:13 +000098static long
paul718e3742002-12-13 20:15:29 +000099timeval_cmp (struct timeval a, struct timeval b)
100{
101 return (a.tv_sec == b.tv_sec
102 ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec);
103}
104
105static unsigned long
106timeval_elapsed (struct timeval a, struct timeval b)
107{
108 return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO)
109 + (a.tv_usec - b.tv_usec));
110}
111
Hasso Tepper3b96b782012-10-11 11:31:54 +0000112#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__)
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000113static void
114quagga_gettimeofday_relative_adjust (void)
115{
116 struct timeval diff;
117 if (timeval_cmp (recent_time, last_recent_time) < 0)
118 {
119 relative_time.tv_sec++;
120 relative_time.tv_usec = 0;
121 }
122 else
123 {
124 diff = timeval_subtract (recent_time, last_recent_time);
125 relative_time.tv_sec += diff.tv_sec;
126 relative_time.tv_usec += diff.tv_usec;
127 relative_time = timeval_adjust (relative_time);
128 }
129 last_recent_time = recent_time;
130}
Hasso Tepper3b96b782012-10-11 11:31:54 +0000131#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000132
133/* gettimeofday wrapper, to keep recent_time updated */
134static int
135quagga_gettimeofday (struct timeval *tv)
136{
137 int ret;
138
139 assert (tv);
140
141 if (!(ret = gettimeofday (&recent_time, NULL)))
142 {
143 /* init... */
144 if (!timers_inited)
145 {
146 relative_time_base = last_recent_time = recent_time;
147 timers_inited = 1;
148 }
149 /* avoid copy if user passed recent_time pointer.. */
150 if (tv != &recent_time)
151 *tv = recent_time;
152 return 0;
153 }
154 return ret;
155}
156
157static int
158quagga_get_relative (struct timeval *tv)
159{
160 int ret;
161
162#ifdef HAVE_CLOCK_MONOTONIC
163 {
164 struct timespec tp;
165 if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp)))
166 {
167 relative_time.tv_sec = tp.tv_sec;
168 relative_time.tv_usec = tp.tv_nsec / 1000;
169 }
170 }
Hasso Tepper3b96b782012-10-11 11:31:54 +0000171#elif defined(__APPLE__)
172 {
173 uint64_t ticks;
174 uint64_t useconds;
175 static mach_timebase_info_data_t timebase_info;
176
177 ticks = mach_absolute_time();
178 if (timebase_info.denom == 0)
179 mach_timebase_info(&timebase_info);
180
181 useconds = ticks * timebase_info.numer / timebase_info.denom / 1000;
182 relative_time.tv_sec = useconds / 1000000;
183 relative_time.tv_usec = useconds % 1000000;
184
185 return 0;
186 }
187#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000188 if (!(ret = quagga_gettimeofday (&recent_time)))
189 quagga_gettimeofday_relative_adjust();
190#endif /* HAVE_CLOCK_MONOTONIC */
191
192 if (tv)
193 *tv = relative_time;
194
195 return ret;
196}
197
198/* Get absolute time stamp, but in terms of the internal timer
199 * Could be wrong, but at least won't go back.
200 */
201static void
202quagga_real_stabilised (struct timeval *tv)
203{
204 *tv = relative_time_base;
205 tv->tv_sec += relative_time.tv_sec;
206 tv->tv_usec += relative_time.tv_usec;
207 *tv = timeval_adjust (*tv);
208}
209
210/* Exported Quagga timestamp function.
211 * Modelled on POSIX clock_gettime.
212 */
213int
214quagga_gettime (enum quagga_clkid clkid, struct timeval *tv)
215{
216 switch (clkid)
217 {
218 case QUAGGA_CLK_REALTIME:
219 return quagga_gettimeofday (tv);
220 case QUAGGA_CLK_MONOTONIC:
221 return quagga_get_relative (tv);
222 case QUAGGA_CLK_REALTIME_STABILISED:
223 quagga_real_stabilised (tv);
224 return 0;
225 default:
226 errno = EINVAL;
227 return -1;
228 }
229}
230
231/* time_t value in terms of stabilised absolute time.
232 * replacement for POSIX time()
233 */
234time_t
235quagga_time (time_t *t)
236{
237 struct timeval tv;
238 quagga_real_stabilised (&tv);
239 if (t)
240 *t = tv.tv_sec;
241 return tv.tv_sec;
242}
243
244/* Public export of recent_relative_time by value */
245struct timeval
246recent_relative_time (void)
247{
248 return relative_time;
249}
250
paula48b4e62005-04-22 00:43:47 +0000251static unsigned int
paule04ab742003-01-17 23:47:00 +0000252cpu_record_hash_key (struct cpu_thread_history *a)
253{
paul8cc41982005-05-06 21:25:49 +0000254 return (uintptr_t) a->func;
paule04ab742003-01-17 23:47:00 +0000255}
256
257static int
Stephen Hemmingerffe11cf2008-08-14 16:25:25 +0100258cpu_record_hash_cmp (const struct cpu_thread_history *a,
259 const struct cpu_thread_history *b)
paule04ab742003-01-17 23:47:00 +0000260{
261 return a->func == b->func;
262}
263
paul8cc41982005-05-06 21:25:49 +0000264static void *
paule04ab742003-01-17 23:47:00 +0000265cpu_record_hash_alloc (struct cpu_thread_history *a)
266{
267 struct cpu_thread_history *new;
paul039b9572004-10-31 16:43:17 +0000268 new = XCALLOC (MTYPE_THREAD_STATS, sizeof (struct cpu_thread_history));
paule04ab742003-01-17 23:47:00 +0000269 new->func = a->func;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000270 strcpy(new->funcname, a->funcname);
paule04ab742003-01-17 23:47:00 +0000271 return new;
272}
273
Chris Caputo228da422009-07-18 05:44:03 +0000274static void
275cpu_record_hash_free (void *a)
276{
277 struct cpu_thread_history *hist = a;
278
Chris Caputo228da422009-07-18 05:44:03 +0000279 XFREE (MTYPE_THREAD_STATS, hist);
280}
281
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100282static void
paule04ab742003-01-17 23:47:00 +0000283vty_out_cpu_thread_history(struct vty* vty,
284 struct cpu_thread_history *a)
285{
ajs8b70d0b2005-04-28 01:31:13 +0000286#ifdef HAVE_RUSAGE
287 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld",
288 a->cpu.total/1000, a->cpu.total%1000, a->total_calls,
289 a->cpu.total/a->total_calls, a->cpu.max,
290 a->real.total/a->total_calls, a->real.max);
291#else
292 vty_out(vty, "%7ld.%03ld %9d %8ld %9ld",
293 a->real.total/1000, a->real.total%1000, a->total_calls,
294 a->real.total/a->total_calls, a->real.max);
295#endif
296 vty_out(vty, " %c%c%c%c%c%c %s%s",
paule04ab742003-01-17 23:47:00 +0000297 a->types & (1 << THREAD_READ) ? 'R':' ',
298 a->types & (1 << THREAD_WRITE) ? 'W':' ',
299 a->types & (1 << THREAD_TIMER) ? 'T':' ',
300 a->types & (1 << THREAD_EVENT) ? 'E':' ',
301 a->types & (1 << THREAD_EXECUTE) ? 'X':' ',
paula48b4e62005-04-22 00:43:47 +0000302 a->types & (1 << THREAD_BACKGROUND) ? 'B' : ' ',
paule04ab742003-01-17 23:47:00 +0000303 a->funcname, VTY_NEWLINE);
304}
305
306static void
307cpu_record_hash_print(struct hash_backet *bucket,
308 void *args[])
309{
310 struct cpu_thread_history *totals = args[0];
311 struct vty *vty = args[1];
Paul Jakma41b23732009-06-30 16:12:49 +0100312 thread_type *filter = args[2];
paule04ab742003-01-17 23:47:00 +0000313 struct cpu_thread_history *a = bucket->data;
paula48b4e62005-04-22 00:43:47 +0000314
paule04ab742003-01-17 23:47:00 +0000315 a = bucket->data;
316 if ( !(a->types & *filter) )
317 return;
318 vty_out_cpu_thread_history(vty,a);
paule04ab742003-01-17 23:47:00 +0000319 totals->total_calls += a->total_calls;
ajs8b70d0b2005-04-28 01:31:13 +0000320 totals->real.total += a->real.total;
321 if (totals->real.max < a->real.max)
322 totals->real.max = a->real.max;
323#ifdef HAVE_RUSAGE
324 totals->cpu.total += a->cpu.total;
325 if (totals->cpu.max < a->cpu.max)
326 totals->cpu.max = a->cpu.max;
327#endif
paule04ab742003-01-17 23:47:00 +0000328}
329
330static void
Paul Jakma41b23732009-06-30 16:12:49 +0100331cpu_record_print(struct vty *vty, thread_type filter)
paule04ab742003-01-17 23:47:00 +0000332{
333 struct cpu_thread_history tmp;
334 void *args[3] = {&tmp, vty, &filter};
335
336 memset(&tmp, 0, sizeof tmp);
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000337 strcpy(tmp.funcname, "TOTAL");
paule04ab742003-01-17 23:47:00 +0000338 tmp.types = filter;
339
ajs8b70d0b2005-04-28 01:31:13 +0000340#ifdef HAVE_RUSAGE
341 vty_out(vty, "%21s %18s %18s%s",
342 "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE);
343#endif
344 vty_out(vty, "Runtime(ms) Invoked Avg uSec Max uSecs");
345#ifdef HAVE_RUSAGE
346 vty_out(vty, " Avg uSec Max uSecs");
347#endif
348 vty_out(vty, " Type Thread%s", VTY_NEWLINE);
paule04ab742003-01-17 23:47:00 +0000349 hash_iterate(cpu_record,
350 (void(*)(struct hash_backet*,void*))cpu_record_hash_print,
351 args);
352
353 if (tmp.total_calls > 0)
354 vty_out_cpu_thread_history(vty, &tmp);
355}
356
357DEFUN(show_thread_cpu,
358 show_thread_cpu_cmd,
359 "show thread cpu [FILTER]",
360 SHOW_STR
361 "Thread information\n"
362 "Thread CPU usage\n"
paula48b4e62005-04-22 00:43:47 +0000363 "Display filter (rwtexb)\n")
paule04ab742003-01-17 23:47:00 +0000364{
365 int i = 0;
Paul Jakma41b23732009-06-30 16:12:49 +0100366 thread_type filter = (thread_type) -1U;
paule04ab742003-01-17 23:47:00 +0000367
368 if (argc > 0)
369 {
370 filter = 0;
371 while (argv[0][i] != '\0')
372 {
373 switch ( argv[0][i] )
374 {
375 case 'r':
376 case 'R':
377 filter |= (1 << THREAD_READ);
378 break;
379 case 'w':
380 case 'W':
381 filter |= (1 << THREAD_WRITE);
382 break;
383 case 't':
384 case 'T':
385 filter |= (1 << THREAD_TIMER);
386 break;
387 case 'e':
388 case 'E':
389 filter |= (1 << THREAD_EVENT);
390 break;
391 case 'x':
392 case 'X':
393 filter |= (1 << THREAD_EXECUTE);
394 break;
paula48b4e62005-04-22 00:43:47 +0000395 case 'b':
396 case 'B':
397 filter |= (1 << THREAD_BACKGROUND);
398 break;
paule04ab742003-01-17 23:47:00 +0000399 default:
400 break;
401 }
402 ++i;
403 }
404 if (filter == 0)
405 {
paula48b4e62005-04-22 00:43:47 +0000406 vty_out(vty, "Invalid filter \"%s\" specified,"
407 " must contain at least one of 'RWTEXB'%s",
paule04ab742003-01-17 23:47:00 +0000408 argv[0], VTY_NEWLINE);
409 return CMD_WARNING;
410 }
411 }
412
413 cpu_record_print(vty, filter);
414 return CMD_SUCCESS;
415}
Paul Jakmae276eb82010-01-09 16:15:00 +0000416
417static void
418cpu_record_hash_clear (struct hash_backet *bucket,
419 void *args)
420{
421 thread_type *filter = args;
422 struct cpu_thread_history *a = bucket->data;
423
424 a = bucket->data;
425 if ( !(a->types & *filter) )
426 return;
427
428 hash_release (cpu_record, bucket->data);
429}
430
431static void
432cpu_record_clear (thread_type filter)
433{
434 thread_type *tmp = &filter;
435 hash_iterate (cpu_record,
436 (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear,
437 tmp);
438}
439
440DEFUN(clear_thread_cpu,
441 clear_thread_cpu_cmd,
442 "clear thread cpu [FILTER]",
443 "Clear stored data\n"
444 "Thread information\n"
445 "Thread CPU usage\n"
446 "Display filter (rwtexb)\n")
447{
448 int i = 0;
449 thread_type filter = (thread_type) -1U;
450
451 if (argc > 0)
452 {
453 filter = 0;
454 while (argv[0][i] != '\0')
455 {
456 switch ( argv[0][i] )
457 {
458 case 'r':
459 case 'R':
460 filter |= (1 << THREAD_READ);
461 break;
462 case 'w':
463 case 'W':
464 filter |= (1 << THREAD_WRITE);
465 break;
466 case 't':
467 case 'T':
468 filter |= (1 << THREAD_TIMER);
469 break;
470 case 'e':
471 case 'E':
472 filter |= (1 << THREAD_EVENT);
473 break;
474 case 'x':
475 case 'X':
476 filter |= (1 << THREAD_EXECUTE);
477 break;
478 case 'b':
479 case 'B':
480 filter |= (1 << THREAD_BACKGROUND);
481 break;
482 default:
483 break;
484 }
485 ++i;
486 }
487 if (filter == 0)
488 {
489 vty_out(vty, "Invalid filter \"%s\" specified,"
490 " must contain at least one of 'RWTEXB'%s",
491 argv[0], VTY_NEWLINE);
492 return CMD_WARNING;
493 }
494 }
495
496 cpu_record_clear (filter);
497 return CMD_SUCCESS;
498}
paule04ab742003-01-17 23:47:00 +0000499
Christian Franke4becea72013-11-19 14:11:42 +0000500static int
501thread_timer_cmp(void *a, void *b)
502{
503 struct thread *thread_a = a;
504 struct thread *thread_b = b;
505
506 long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands);
507
508 if (cmp < 0)
509 return -1;
510 if (cmp > 0)
511 return 1;
512 return 0;
513}
514
515static void
516thread_timer_update(void *node, int actual_position)
517{
518 struct thread *thread = node;
519
520 thread->index = actual_position;
521}
522
paul718e3742002-12-13 20:15:29 +0000523/* Allocate new thread master. */
524struct thread_master *
525thread_master_create ()
526{
Christian Franke4becea72013-11-19 14:11:42 +0000527 struct thread_master *rv;
528
paule04ab742003-01-17 23:47:00 +0000529 if (cpu_record == NULL)
paul8cc41982005-05-06 21:25:49 +0000530 cpu_record
Stephen Hemminger90645f52013-01-04 22:29:21 +0000531 = hash_create ((unsigned int (*) (void *))cpu_record_hash_key,
532 (int (*) (const void *, const void *))cpu_record_hash_cmp);
Christian Franke4becea72013-11-19 14:11:42 +0000533
534 rv = XCALLOC (MTYPE_THREAD_MASTER, sizeof (struct thread_master));
535
536 /* Initialize the timer queues */
537 rv->timer = pqueue_create();
538 rv->background = pqueue_create();
539 rv->timer->cmp = rv->background->cmp = thread_timer_cmp;
540 rv->timer->update = rv->background->update = thread_timer_update;
541
542 return rv;
paul718e3742002-12-13 20:15:29 +0000543}
544
545/* Add a new thread to the list. */
546static void
547thread_list_add (struct thread_list *list, struct thread *thread)
548{
549 thread->next = NULL;
550 thread->prev = list->tail;
551 if (list->tail)
552 list->tail->next = thread;
553 else
554 list->head = thread;
555 list->tail = thread;
556 list->count++;
557}
558
paul718e3742002-12-13 20:15:29 +0000559/* Delete a thread from the list. */
560static struct thread *
561thread_list_delete (struct thread_list *list, struct thread *thread)
562{
563 if (thread->next)
564 thread->next->prev = thread->prev;
565 else
566 list->tail = thread->prev;
567 if (thread->prev)
568 thread->prev->next = thread->next;
569 else
570 list->head = thread->next;
571 thread->next = thread->prev = NULL;
572 list->count--;
573 return thread;
574}
575
576/* Move thread to unuse list. */
577static void
578thread_add_unuse (struct thread_master *m, struct thread *thread)
579{
paula48b4e62005-04-22 00:43:47 +0000580 assert (m != NULL && thread != NULL);
paul718e3742002-12-13 20:15:29 +0000581 assert (thread->next == NULL);
582 assert (thread->prev == NULL);
583 assert (thread->type == THREAD_UNUSED);
584 thread_list_add (&m->unuse, thread);
paul9d11a192004-10-31 16:19:24 +0000585 /* XXX: Should we deallocate funcname here? */
paul718e3742002-12-13 20:15:29 +0000586}
587
588/* Free all unused thread. */
589static void
590thread_list_free (struct thread_master *m, struct thread_list *list)
591{
592 struct thread *t;
593 struct thread *next;
594
595 for (t = list->head; t; t = next)
596 {
597 next = t->next;
598 XFREE (MTYPE_THREAD, t);
599 list->count--;
600 m->alloc--;
601 }
602}
603
Christian Franke4becea72013-11-19 14:11:42 +0000604static void
605thread_queue_free (struct thread_master *m, struct pqueue *queue)
606{
607 int i;
608
609 for (i = 0; i < queue->size; i++)
610 XFREE(MTYPE_THREAD, queue->array[i]);
611
612 m->alloc -= queue->size;
613 pqueue_delete(queue);
614}
615
paul718e3742002-12-13 20:15:29 +0000616/* Stop thread scheduler. */
617void
618thread_master_free (struct thread_master *m)
619{
620 thread_list_free (m, &m->read);
621 thread_list_free (m, &m->write);
Christian Franke4becea72013-11-19 14:11:42 +0000622 thread_queue_free (m, m->timer);
paul718e3742002-12-13 20:15:29 +0000623 thread_list_free (m, &m->event);
624 thread_list_free (m, &m->ready);
625 thread_list_free (m, &m->unuse);
Christian Franke4becea72013-11-19 14:11:42 +0000626 thread_queue_free (m, m->background);
paula48b4e62005-04-22 00:43:47 +0000627
paul718e3742002-12-13 20:15:29 +0000628 XFREE (MTYPE_THREAD_MASTER, m);
Chris Caputo228da422009-07-18 05:44:03 +0000629
630 if (cpu_record)
631 {
632 hash_clean (cpu_record, cpu_record_hash_free);
633 hash_free (cpu_record);
634 cpu_record = NULL;
635 }
paul718e3742002-12-13 20:15:29 +0000636}
637
paul8cc41982005-05-06 21:25:49 +0000638/* Thread list is empty or not. */
Paul Jakmaf63f06d2011-04-08 12:44:43 +0100639static int
paul8cc41982005-05-06 21:25:49 +0000640thread_empty (struct thread_list *list)
641{
642 return list->head ? 0 : 1;
643}
644
paul718e3742002-12-13 20:15:29 +0000645/* Delete top of the list and return it. */
646static struct thread *
647thread_trim_head (struct thread_list *list)
648{
paul8cc41982005-05-06 21:25:49 +0000649 if (!thread_empty (list))
paul718e3742002-12-13 20:15:29 +0000650 return thread_list_delete (list, list->head);
651 return NULL;
652}
653
paul718e3742002-12-13 20:15:29 +0000654/* Return remain time in second. */
655unsigned long
656thread_timer_remain_second (struct thread *thread)
657{
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000658 quagga_get_relative (NULL);
659
660 if (thread->u.sands.tv_sec - relative_time.tv_sec > 0)
661 return thread->u.sands.tv_sec - relative_time.tv_sec;
paul718e3742002-12-13 20:15:29 +0000662 else
663 return 0;
664}
665
paule04ab742003-01-17 23:47:00 +0000666/* Trim blankspace and "()"s */
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000667void
668strip_funcname (char *dest, const char *funcname)
paule04ab742003-01-17 23:47:00 +0000669{
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000670 char buff[FUNCNAME_LEN];
671 char tmp, *e, *b = buff;
paule04ab742003-01-17 23:47:00 +0000672
673 strncpy(buff, funcname, sizeof(buff));
674 buff[ sizeof(buff) -1] = '\0';
675 e = buff +strlen(buff) -1;
676
677 /* Wont work for funcname == "Word (explanation)" */
678
679 while (*b == ' ' || *b == '(')
680 ++b;
681 while (*e == ' ' || *e == ')')
682 --e;
683 e++;
684
685 tmp = *e;
686 *e = '\0';
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000687 strcpy (dest, b);
paule04ab742003-01-17 23:47:00 +0000688 *e = tmp;
paule04ab742003-01-17 23:47:00 +0000689}
690
paul718e3742002-12-13 20:15:29 +0000691/* Get new thread. */
692static struct thread *
693thread_get (struct thread_master *m, u_char type,
hasso8c328f12004-10-05 21:01:23 +0000694 int (*func) (struct thread *), void *arg, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000695{
Jorge Boncompte [DTI2]64018322012-05-07 16:53:13 +0000696 struct thread *thread = thread_trim_head (&m->unuse);
paul718e3742002-12-13 20:15:29 +0000697
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000698 if (! thread)
paul718e3742002-12-13 20:15:29 +0000699 {
700 thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread));
701 m->alloc++;
702 }
703 thread->type = type;
paule04ab742003-01-17 23:47:00 +0000704 thread->add_type = type;
paul718e3742002-12-13 20:15:29 +0000705 thread->master = m;
706 thread->func = func;
707 thread->arg = arg;
Christian Franke4becea72013-11-19 14:11:42 +0000708 thread->index = -1;
709
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +0000710 strip_funcname (thread->funcname, funcname);
paule04ab742003-01-17 23:47:00 +0000711
paul718e3742002-12-13 20:15:29 +0000712 return thread;
713}
714
715/* Add new read thread. */
716struct thread *
paule04ab742003-01-17 23:47:00 +0000717funcname_thread_add_read (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000718 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000719{
720 struct thread *thread;
721
722 assert (m != NULL);
723
724 if (FD_ISSET (fd, &m->readfd))
725 {
726 zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd);
727 return NULL;
728 }
729
paule04ab742003-01-17 23:47:00 +0000730 thread = thread_get (m, THREAD_READ, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000731 FD_SET (fd, &m->readfd);
732 thread->u.fd = fd;
733 thread_list_add (&m->read, thread);
734
735 return thread;
736}
737
738/* Add new write thread. */
739struct thread *
paule04ab742003-01-17 23:47:00 +0000740funcname_thread_add_write (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000741 int (*func) (struct thread *), void *arg, int fd, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000742{
743 struct thread *thread;
744
745 assert (m != NULL);
746
747 if (FD_ISSET (fd, &m->writefd))
748 {
749 zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd);
750 return NULL;
751 }
752
paule04ab742003-01-17 23:47:00 +0000753 thread = thread_get (m, THREAD_WRITE, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000754 FD_SET (fd, &m->writefd);
755 thread->u.fd = fd;
756 thread_list_add (&m->write, thread);
757
758 return thread;
759}
760
paul98c91ac2004-10-05 14:57:50 +0000761static struct thread *
762funcname_thread_add_timer_timeval (struct thread_master *m,
763 int (*func) (struct thread *),
paula48b4e62005-04-22 00:43:47 +0000764 int type,
paul98c91ac2004-10-05 14:57:50 +0000765 void *arg,
766 struct timeval *time_relative,
hasso8c328f12004-10-05 21:01:23 +0000767 const char* funcname)
paul718e3742002-12-13 20:15:29 +0000768{
paul718e3742002-12-13 20:15:29 +0000769 struct thread *thread;
Christian Franke4becea72013-11-19 14:11:42 +0000770 struct pqueue *queue;
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000771 struct timeval alarm_time;
paul718e3742002-12-13 20:15:29 +0000772
773 assert (m != NULL);
774
ajs8b70d0b2005-04-28 01:31:13 +0000775 assert (type == THREAD_TIMER || type == THREAD_BACKGROUND);
paula48b4e62005-04-22 00:43:47 +0000776 assert (time_relative);
777
Christian Franke4becea72013-11-19 14:11:42 +0000778 queue = ((type == THREAD_TIMER) ? m->timer : m->background);
paula48b4e62005-04-22 00:43:47 +0000779 thread = thread_get (m, type, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000780
781 /* Do we need jitter here? */
Joakim Tjernlundb8192762008-11-10 09:33:30 +0100782 quagga_get_relative (NULL);
Paul Jakmadb9c0df2006-08-27 06:44:02 +0000783 alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec;
784 alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec;
ajs8b70d0b2005-04-28 01:31:13 +0000785 thread->u.sands = timeval_adjust(alarm_time);
paul718e3742002-12-13 20:15:29 +0000786
Christian Franke4becea72013-11-19 14:11:42 +0000787 pqueue_enqueue(thread, queue);
paul718e3742002-12-13 20:15:29 +0000788 return thread;
789}
790
paul98c91ac2004-10-05 14:57:50 +0000791
792/* Add timer event thread. */
jardin9e867fe2003-12-23 08:56:18 +0000793struct thread *
paul98c91ac2004-10-05 14:57:50 +0000794funcname_thread_add_timer (struct thread_master *m,
795 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000796 void *arg, long timer, const char* funcname)
jardin9e867fe2003-12-23 08:56:18 +0000797{
paul98c91ac2004-10-05 14:57:50 +0000798 struct timeval trel;
jardin9e867fe2003-12-23 08:56:18 +0000799
800 assert (m != NULL);
801
paul9076fbd2004-10-11 09:40:58 +0000802 trel.tv_sec = timer;
paul98c91ac2004-10-05 14:57:50 +0000803 trel.tv_usec = 0;
804
paula48b4e62005-04-22 00:43:47 +0000805 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, arg,
806 &trel, funcname);
paul98c91ac2004-10-05 14:57:50 +0000807}
808
809/* Add timer event thread with "millisecond" resolution */
810struct thread *
811funcname_thread_add_timer_msec (struct thread_master *m,
812 int (*func) (struct thread *),
hasso8c328f12004-10-05 21:01:23 +0000813 void *arg, long timer, const char* funcname)
paul98c91ac2004-10-05 14:57:50 +0000814{
815 struct timeval trel;
816
817 assert (m != NULL);
jardin9e867fe2003-12-23 08:56:18 +0000818
ajsaf04bd72004-12-28 17:00:12 +0000819 trel.tv_sec = timer / 1000;
820 trel.tv_usec = 1000*(timer % 1000);
jardin9e867fe2003-12-23 08:56:18 +0000821
paula48b4e62005-04-22 00:43:47 +0000822 return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER,
823 arg, &trel, funcname);
824}
825
826/* Add a background thread, with an optional millisec delay */
827struct thread *
828funcname_thread_add_background (struct thread_master *m,
829 int (*func) (struct thread *),
830 void *arg, long delay,
831 const char *funcname)
832{
833 struct timeval trel;
834
835 assert (m != NULL);
836
837 if (delay)
838 {
839 trel.tv_sec = delay / 1000;
840 trel.tv_usec = 1000*(delay % 1000);
841 }
842 else
843 {
844 trel.tv_sec = 0;
845 trel.tv_usec = 0;
846 }
847
848 return funcname_thread_add_timer_timeval (m, func, THREAD_BACKGROUND,
849 arg, &trel, funcname);
jardin9e867fe2003-12-23 08:56:18 +0000850}
851
paul718e3742002-12-13 20:15:29 +0000852/* Add simple event thread. */
853struct thread *
paule04ab742003-01-17 23:47:00 +0000854funcname_thread_add_event (struct thread_master *m,
hasso8c328f12004-10-05 21:01:23 +0000855 int (*func) (struct thread *), void *arg, int val, const char* funcname)
paul718e3742002-12-13 20:15:29 +0000856{
857 struct thread *thread;
858
859 assert (m != NULL);
860
paule04ab742003-01-17 23:47:00 +0000861 thread = thread_get (m, THREAD_EVENT, func, arg, funcname);
paul718e3742002-12-13 20:15:29 +0000862 thread->u.val = val;
863 thread_list_add (&m->event, thread);
864
865 return thread;
866}
867
868/* Cancel thread from scheduler. */
869void
870thread_cancel (struct thread *thread)
871{
Christian Franke4becea72013-11-19 14:11:42 +0000872 struct thread_list *list = NULL;
873 struct pqueue *queue = NULL;
paula48b4e62005-04-22 00:43:47 +0000874
paul718e3742002-12-13 20:15:29 +0000875 switch (thread->type)
876 {
877 case THREAD_READ:
878 assert (FD_ISSET (thread->u.fd, &thread->master->readfd));
879 FD_CLR (thread->u.fd, &thread->master->readfd);
paula48b4e62005-04-22 00:43:47 +0000880 list = &thread->master->read;
paul718e3742002-12-13 20:15:29 +0000881 break;
882 case THREAD_WRITE:
883 assert (FD_ISSET (thread->u.fd, &thread->master->writefd));
884 FD_CLR (thread->u.fd, &thread->master->writefd);
paula48b4e62005-04-22 00:43:47 +0000885 list = &thread->master->write;
paul718e3742002-12-13 20:15:29 +0000886 break;
887 case THREAD_TIMER:
Christian Franke4becea72013-11-19 14:11:42 +0000888 queue = thread->master->timer;
paul718e3742002-12-13 20:15:29 +0000889 break;
890 case THREAD_EVENT:
paula48b4e62005-04-22 00:43:47 +0000891 list = &thread->master->event;
paul718e3742002-12-13 20:15:29 +0000892 break;
893 case THREAD_READY:
paula48b4e62005-04-22 00:43:47 +0000894 list = &thread->master->ready;
paul718e3742002-12-13 20:15:29 +0000895 break;
paula48b4e62005-04-22 00:43:47 +0000896 case THREAD_BACKGROUND:
Christian Franke4becea72013-11-19 14:11:42 +0000897 queue = thread->master->background;
ajs8b70d0b2005-04-28 01:31:13 +0000898 break;
paul718e3742002-12-13 20:15:29 +0000899 default:
paula48b4e62005-04-22 00:43:47 +0000900 return;
paul718e3742002-12-13 20:15:29 +0000901 break;
902 }
Christian Franke4becea72013-11-19 14:11:42 +0000903
904 if (queue)
905 {
906 assert(thread->index >= 0);
907 assert(thread == queue->array[thread->index]);
908 pqueue_remove_at(thread->index, queue);
909 }
910 else if (list)
911 {
912 thread_list_delete (list, thread);
913 }
914 else
915 {
916 assert(!"Thread should be either in queue or list!");
917 }
918
paul718e3742002-12-13 20:15:29 +0000919 thread->type = THREAD_UNUSED;
920 thread_add_unuse (thread->master, thread);
921}
922
923/* Delete all events which has argument value arg. */
pauldc818072005-05-19 01:30:53 +0000924unsigned int
paul718e3742002-12-13 20:15:29 +0000925thread_cancel_event (struct thread_master *m, void *arg)
926{
pauldc818072005-05-19 01:30:53 +0000927 unsigned int ret = 0;
paul718e3742002-12-13 20:15:29 +0000928 struct thread *thread;
929
930 thread = m->event.head;
931 while (thread)
932 {
933 struct thread *t;
934
935 t = thread;
936 thread = t->next;
937
938 if (t->arg == arg)
paula48b4e62005-04-22 00:43:47 +0000939 {
pauldc818072005-05-19 01:30:53 +0000940 ret++;
paula48b4e62005-04-22 00:43:47 +0000941 thread_list_delete (&m->event, t);
942 t->type = THREAD_UNUSED;
943 thread_add_unuse (m, t);
944 }
paul718e3742002-12-13 20:15:29 +0000945 }
Jorge Boncompte [DTI2]1b79fcb2012-05-07 15:17:31 +0000946
947 /* thread can be on the ready list too */
948 thread = m->ready.head;
949 while (thread)
950 {
951 struct thread *t;
952
953 t = thread;
954 thread = t->next;
955
956 if (t->arg == arg)
957 {
958 ret++;
959 thread_list_delete (&m->ready, t);
960 t->type = THREAD_UNUSED;
961 thread_add_unuse (m, t);
962 }
963 }
pauldc818072005-05-19 01:30:53 +0000964 return ret;
paul718e3742002-12-13 20:15:29 +0000965}
966
paula48b4e62005-04-22 00:43:47 +0000967static struct timeval *
Christian Franke4becea72013-11-19 14:11:42 +0000968thread_timer_wait (struct pqueue *queue, struct timeval *timer_val)
paul718e3742002-12-13 20:15:29 +0000969{
Christian Franke4becea72013-11-19 14:11:42 +0000970 if (queue->size)
paul718e3742002-12-13 20:15:29 +0000971 {
Christian Franke4becea72013-11-19 14:11:42 +0000972 struct thread *next_timer = queue->array[0];
973 *timer_val = timeval_subtract (next_timer->u.sands, relative_time);
paul718e3742002-12-13 20:15:29 +0000974 return timer_val;
975 }
976 return NULL;
977}
paul718e3742002-12-13 20:15:29 +0000978
paul8cc41982005-05-06 21:25:49 +0000979static struct thread *
paul718e3742002-12-13 20:15:29 +0000980thread_run (struct thread_master *m, struct thread *thread,
981 struct thread *fetch)
982{
983 *fetch = *thread;
984 thread->type = THREAD_UNUSED;
985 thread_add_unuse (m, thread);
986 return fetch;
987}
988
paula48b4e62005-04-22 00:43:47 +0000989static int
990thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset)
paul718e3742002-12-13 20:15:29 +0000991{
992 struct thread *thread;
993 struct thread *next;
994 int ready = 0;
paula48b4e62005-04-22 00:43:47 +0000995
996 assert (list);
997
paul718e3742002-12-13 20:15:29 +0000998 for (thread = list->head; thread; thread = next)
999 {
1000 next = thread->next;
1001
1002 if (FD_ISSET (THREAD_FD (thread), fdset))
paula48b4e62005-04-22 00:43:47 +00001003 {
1004 assert (FD_ISSET (THREAD_FD (thread), mfdset));
1005 FD_CLR(THREAD_FD (thread), mfdset);
1006 thread_list_delete (list, thread);
1007 thread_list_add (&thread->master->ready, thread);
1008 thread->type = THREAD_READY;
1009 ready++;
1010 }
paul718e3742002-12-13 20:15:29 +00001011 }
1012 return ready;
1013}
1014
ajs8b70d0b2005-04-28 01:31:13 +00001015/* Add all timers that have popped to the ready list. */
paula48b4e62005-04-22 00:43:47 +00001016static unsigned int
Christian Franke4becea72013-11-19 14:11:42 +00001017thread_timer_process (struct pqueue *queue, struct timeval *timenow)
paula48b4e62005-04-22 00:43:47 +00001018{
1019 struct thread *thread;
1020 unsigned int ready = 0;
1021
Christian Franke4becea72013-11-19 14:11:42 +00001022 while (queue->size)
ajs8b70d0b2005-04-28 01:31:13 +00001023 {
Christian Franke4becea72013-11-19 14:11:42 +00001024 thread = queue->array[0];
ajs8b70d0b2005-04-28 01:31:13 +00001025 if (timeval_cmp (*timenow, thread->u.sands) < 0)
1026 return ready;
Christian Franke4becea72013-11-19 14:11:42 +00001027 pqueue_dequeue(queue);
ajs8b70d0b2005-04-28 01:31:13 +00001028 thread->type = THREAD_READY;
1029 thread_list_add (&thread->master->ready, thread);
1030 ready++;
1031 }
paula48b4e62005-04-22 00:43:47 +00001032 return ready;
1033}
1034
Paul Jakma2613abe2010-01-11 16:33:07 +00001035/* process a list en masse, e.g. for event thread lists */
1036static unsigned int
1037thread_process (struct thread_list *list)
1038{
1039 struct thread *thread;
Paul Jakmab5043aa2012-02-28 18:32:56 +00001040 struct thread *next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001041 unsigned int ready = 0;
1042
Paul Jakmab5043aa2012-02-28 18:32:56 +00001043 for (thread = list->head; thread; thread = next)
Paul Jakma2613abe2010-01-11 16:33:07 +00001044 {
Paul Jakmab5043aa2012-02-28 18:32:56 +00001045 next = thread->next;
Paul Jakma2613abe2010-01-11 16:33:07 +00001046 thread_list_delete (list, thread);
1047 thread->type = THREAD_READY;
1048 thread_list_add (&thread->master->ready, thread);
1049 ready++;
1050 }
1051 return ready;
1052}
1053
1054
paul718e3742002-12-13 20:15:29 +00001055/* Fetch next ready thread. */
1056struct thread *
1057thread_fetch (struct thread_master *m, struct thread *fetch)
1058{
paul718e3742002-12-13 20:15:29 +00001059 struct thread *thread;
1060 fd_set readfd;
1061 fd_set writefd;
1062 fd_set exceptfd;
Paul Jakma2613abe2010-01-11 16:33:07 +00001063 struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 };
paula48b4e62005-04-22 00:43:47 +00001064 struct timeval timer_val_bg;
Paul Jakma2613abe2010-01-11 16:33:07 +00001065 struct timeval *timer_wait = &timer_val;
paula48b4e62005-04-22 00:43:47 +00001066 struct timeval *timer_wait_bg;
paul718e3742002-12-13 20:15:29 +00001067
1068 while (1)
1069 {
paula48b4e62005-04-22 00:43:47 +00001070 int num = 0;
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001071#if defined HAVE_SNMP && defined SNMP_AGENTX
1072 struct timeval snmp_timer_wait;
1073 int snmpblock = 0;
1074 int fdsetsize;
1075#endif
paula48b4e62005-04-22 00:43:47 +00001076
Paul Jakma2613abe2010-01-11 16:33:07 +00001077 /* Signals pre-empt everything */
paul05c447d2004-07-22 19:14:27 +00001078 quagga_sigevent_process ();
1079
Paul Jakma2613abe2010-01-11 16:33:07 +00001080 /* Drain the ready queue of already scheduled jobs, before scheduling
1081 * more.
paula48b4e62005-04-22 00:43:47 +00001082 */
paul718e3742002-12-13 20:15:29 +00001083 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001084 return thread_run (m, thread, fetch);
paula48b4e62005-04-22 00:43:47 +00001085
Paul Jakma2613abe2010-01-11 16:33:07 +00001086 /* To be fair to all kinds of threads, and avoid starvation, we
1087 * need to be careful to consider all thread types for scheduling
1088 * in each quanta. I.e. we should not return early from here on.
1089 */
1090
1091 /* Normal event are the next highest priority. */
1092 thread_process (&m->event);
1093
paul718e3742002-12-13 20:15:29 +00001094 /* Structure copy. */
1095 readfd = m->readfd;
1096 writefd = m->writefd;
1097 exceptfd = m->exceptfd;
paula48b4e62005-04-22 00:43:47 +00001098
1099 /* Calculate select wait timer if nothing else to do */
Paul Jakma2613abe2010-01-11 16:33:07 +00001100 if (m->ready.count == 0)
1101 {
1102 quagga_get_relative (NULL);
Christian Franke4becea72013-11-19 14:11:42 +00001103 timer_wait = thread_timer_wait (m->timer, &timer_val);
1104 timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg);
Paul Jakma2613abe2010-01-11 16:33:07 +00001105
1106 if (timer_wait_bg &&
1107 (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
1108 timer_wait = timer_wait_bg;
1109 }
paula48b4e62005-04-22 00:43:47 +00001110
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001111#if defined HAVE_SNMP && defined SNMP_AGENTX
1112 /* When SNMP is enabled, we may have to select() on additional
1113 FD. snmp_select_info() will add them to `readfd'. The trick
1114 with this function is its last argument. We need to set it to
1115 0 if timer_wait is not NULL and we need to use the provided
1116 new timer only if it is still set to 0. */
1117 if (agentx_enabled)
1118 {
1119 fdsetsize = FD_SETSIZE;
1120 snmpblock = 1;
1121 if (timer_wait)
1122 {
1123 snmpblock = 0;
1124 memcpy(&snmp_timer_wait, timer_wait, sizeof(struct timeval));
1125 }
1126 snmp_select_info(&fdsetsize, &readfd, &snmp_timer_wait, &snmpblock);
1127 if (snmpblock == 0)
1128 timer_wait = &snmp_timer_wait;
1129 }
1130#endif
paul718e3742002-12-13 20:15:29 +00001131 num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
paula48b4e62005-04-22 00:43:47 +00001132
1133 /* Signals should get quick treatment */
paul718e3742002-12-13 20:15:29 +00001134 if (num < 0)
paul05c447d2004-07-22 19:14:27 +00001135 {
1136 if (errno == EINTR)
paula48b4e62005-04-22 00:43:47 +00001137 continue; /* signal received - process it */
ajs6099b3b2004-11-20 02:06:59 +00001138 zlog_warn ("select() error: %s", safe_strerror (errno));
paul05c447d2004-07-22 19:14:27 +00001139 return NULL;
1140 }
ajs8b70d0b2005-04-28 01:31:13 +00001141
Vincent Bernatd6be5fb2012-05-24 09:44:43 +02001142#if defined HAVE_SNMP && defined SNMP_AGENTX
1143 if (agentx_enabled)
1144 {
1145 if (num > 0)
1146 snmp_read(&readfd);
1147 else if (num == 0)
1148 {
1149 snmp_timeout();
1150 run_alarms();
1151 }
1152 netsnmp_check_outstanding_agent_requests();
1153 }
1154#endif
1155
ajs8b70d0b2005-04-28 01:31:13 +00001156 /* Check foreground timers. Historically, they have had higher
1157 priority than I/O threads, so let's push them onto the ready
1158 list in front of the I/O threads. */
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001159 quagga_get_relative (NULL);
Christian Franke4becea72013-11-19 14:11:42 +00001160 thread_timer_process (m->timer, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001161
1162 /* Got IO, process it */
1163 if (num > 0)
1164 {
1165 /* Normal priority read thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001166 thread_process_fd (&m->read, &readfd, &m->readfd);
paula48b4e62005-04-22 00:43:47 +00001167 /* Write thead. */
ajs8b70d0b2005-04-28 01:31:13 +00001168 thread_process_fd (&m->write, &writefd, &m->writefd);
paula48b4e62005-04-22 00:43:47 +00001169 }
ajs8b70d0b2005-04-28 01:31:13 +00001170
1171#if 0
1172 /* If any threads were made ready above (I/O or foreground timer),
1173 perhaps we should avoid adding background timers to the ready
1174 list at this time. If this is code is uncommented, then background
1175 timer threads will not run unless there is nothing else to do. */
1176 if ((thread = thread_trim_head (&m->ready)) != NULL)
1177 return thread_run (m, thread, fetch);
1178#endif
1179
paula48b4e62005-04-22 00:43:47 +00001180 /* Background timer/events, lowest priority */
Christian Franke4becea72013-11-19 14:11:42 +00001181 thread_timer_process (m->background, &relative_time);
paula48b4e62005-04-22 00:43:47 +00001182
ajs8b70d0b2005-04-28 01:31:13 +00001183 if ((thread = thread_trim_head (&m->ready)) != NULL)
paul05c447d2004-07-22 19:14:27 +00001184 return thread_run (m, thread, fetch);
paul718e3742002-12-13 20:15:29 +00001185 }
1186}
1187
ajs924b9222005-04-16 17:11:24 +00001188unsigned long
ajs8b70d0b2005-04-28 01:31:13 +00001189thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
paul718e3742002-12-13 20:15:29 +00001190{
paul718e3742002-12-13 20:15:29 +00001191#ifdef HAVE_RUSAGE
1192 /* This is 'user + sys' time. */
ajs8b70d0b2005-04-28 01:31:13 +00001193 *cputime = timeval_elapsed (now->cpu.ru_utime, start->cpu.ru_utime) +
1194 timeval_elapsed (now->cpu.ru_stime, start->cpu.ru_stime);
paul718e3742002-12-13 20:15:29 +00001195#else
ajs8b70d0b2005-04-28 01:31:13 +00001196 *cputime = 0;
paul718e3742002-12-13 20:15:29 +00001197#endif /* HAVE_RUSAGE */
ajs8b70d0b2005-04-28 01:31:13 +00001198 return timeval_elapsed (now->real, start->real);
paul718e3742002-12-13 20:15:29 +00001199}
1200
ajs8b70d0b2005-04-28 01:31:13 +00001201/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
1202 Note: we are using real (wall clock) time for this calculation.
1203 It could be argued that CPU time may make more sense in certain
1204 contexts. The things to consider are whether the thread may have
1205 blocked (in which case wall time increases, but CPU time does not),
1206 or whether the system is heavily loaded with other processes competing
1207 for CPU time. On balance, wall clock time seems to make sense.
1208 Plus it has the added benefit that gettimeofday should be faster
1209 than calling getrusage. */
paul718e3742002-12-13 20:15:29 +00001210int
1211thread_should_yield (struct thread *thread)
1212{
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001213 quagga_get_relative (NULL);
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001214 return (timeval_elapsed(relative_time, thread->real) >
ajs8b70d0b2005-04-28 01:31:13 +00001215 THREAD_YIELD_TIME_SLOT);
paul718e3742002-12-13 20:15:29 +00001216}
1217
Paul Jakmadb9c0df2006-08-27 06:44:02 +00001218void
1219thread_getrusage (RUSAGE_T *r)
1220{
1221 quagga_get_relative (NULL);
1222#ifdef HAVE_RUSAGE
1223 getrusage(RUSAGE_SELF, &(r->cpu));
1224#endif
1225 r->real = relative_time;
1226
1227#ifdef HAVE_CLOCK_MONOTONIC
1228 /* quagga_get_relative() only updates recent_time if gettimeofday
1229 * based, not when using CLOCK_MONOTONIC. As we export recent_time
1230 * and guarantee to update it before threads are run...
1231 */
1232 quagga_gettimeofday(&recent_time);
1233#endif /* HAVE_CLOCK_MONOTONIC */
1234}
1235
paul718e3742002-12-13 20:15:29 +00001236/* We check thread consumed time. If the system has getrusage, we'll
ajs8b70d0b2005-04-28 01:31:13 +00001237 use that to get in-depth stats on the performance of the thread in addition
1238 to wall clock time stats from gettimeofday. */
paul718e3742002-12-13 20:15:29 +00001239void
1240thread_call (struct thread *thread)
1241{
ajs8b70d0b2005-04-28 01:31:13 +00001242 unsigned long realtime, cputime;
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001243 RUSAGE_T before, after;
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001244
1245 /* Cache a pointer to the relevant cpu history thread, if the thread
1246 * does not have it yet.
1247 *
1248 * Callers submitting 'dummy threads' hence must take care that
1249 * thread->cpu is NULL
1250 */
1251 if (!thread->hist)
1252 {
1253 struct cpu_thread_history tmp;
1254
1255 tmp.func = thread->func;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001256 strcpy(tmp.funcname, thread->funcname);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001257
1258 thread->hist = hash_get (cpu_record, &tmp,
1259 (void * (*) (void *))cpu_record_hash_alloc);
1260 }
paul718e3742002-12-13 20:15:29 +00001261
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001262 GETRUSAGE (&before);
1263 thread->real = before.real;
paul718e3742002-12-13 20:15:29 +00001264
1265 (*thread->func) (thread);
1266
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001267 GETRUSAGE (&after);
paul718e3742002-12-13 20:15:29 +00001268
Jorge Boncompte [DTI2]41af3382012-05-07 16:53:12 +00001269 realtime = thread_consumed_time (&after, &before, &cputime);
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001270 thread->hist->real.total += realtime;
1271 if (thread->hist->real.max < realtime)
1272 thread->hist->real.max = realtime;
ajs8b70d0b2005-04-28 01:31:13 +00001273#ifdef HAVE_RUSAGE
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001274 thread->hist->cpu.total += cputime;
1275 if (thread->hist->cpu.max < cputime)
1276 thread->hist->cpu.max = cputime;
ajs8b70d0b2005-04-28 01:31:13 +00001277#endif
paule04ab742003-01-17 23:47:00 +00001278
Paul Jakmacc8b13a2006-07-25 20:40:40 +00001279 ++(thread->hist->total_calls);
1280 thread->hist->types |= (1 << thread->add_type);
paul718e3742002-12-13 20:15:29 +00001281
ajs924b9222005-04-16 17:11:24 +00001282#ifdef CONSUMED_TIME_CHECK
ajs8b70d0b2005-04-28 01:31:13 +00001283 if (realtime > CONSUMED_TIME_CHECK)
paul718e3742002-12-13 20:15:29 +00001284 {
1285 /*
1286 * We have a CPU Hog on our hands.
1287 * Whinge about it now, so we're aware this is yet another task
1288 * to fix.
1289 */
ajs8b70d0b2005-04-28 01:31:13 +00001290 zlog_warn ("SLOW THREAD: task %s (%lx) ran for %lums (cpu time %lums)",
ajs924b9222005-04-16 17:11:24 +00001291 thread->funcname,
1292 (unsigned long) thread->func,
ajs8b70d0b2005-04-28 01:31:13 +00001293 realtime/1000, cputime/1000);
paul718e3742002-12-13 20:15:29 +00001294 }
ajs924b9222005-04-16 17:11:24 +00001295#endif /* CONSUMED_TIME_CHECK */
paul718e3742002-12-13 20:15:29 +00001296}
1297
1298/* Execute thread */
1299struct thread *
paule04ab742003-01-17 23:47:00 +00001300funcname_thread_execute (struct thread_master *m,
paul718e3742002-12-13 20:15:29 +00001301 int (*func)(struct thread *),
1302 void *arg,
paule04ab742003-01-17 23:47:00 +00001303 int val,
hasso8c328f12004-10-05 21:01:23 +00001304 const char* funcname)
paul718e3742002-12-13 20:15:29 +00001305{
1306 struct thread dummy;
1307
1308 memset (&dummy, 0, sizeof (struct thread));
1309
1310 dummy.type = THREAD_EVENT;
paule04ab742003-01-17 23:47:00 +00001311 dummy.add_type = THREAD_EXECUTE;
paul718e3742002-12-13 20:15:29 +00001312 dummy.master = NULL;
1313 dummy.func = func;
1314 dummy.arg = arg;
1315 dummy.u.val = val;
Jorge Boncompte [DTI2]22714f92012-05-07 16:53:14 +00001316 strip_funcname (dummy.funcname, funcname);
paul718e3742002-12-13 20:15:29 +00001317 thread_call (&dummy);
1318
paul718e3742002-12-13 20:15:29 +00001319 return NULL;
1320}