]>
git.proxmox.com Git - mirror_frr.git/blob - lib/thread.c
1 /* Thread management routine
2 * Copyright (C) 1998, 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
4 * This file is part of GNU Zebra.
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
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.
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
33 static struct hash
*cpu_record
= NULL
;
35 /* Struct timeval's tv_usec one second value. */
36 #define TIMER_SECOND_MICRO 1000000L
39 timeval_adjust (struct timeval a
)
41 while (a
.tv_usec
>= TIMER_SECOND_MICRO
)
43 a
.tv_usec
-= TIMER_SECOND_MICRO
;
49 a
.tv_usec
+= TIMER_SECOND_MICRO
;
59 if (a
.tv_sec
> TIMER_SECOND_MICRO
)
60 a
.tv_sec
= TIMER_SECOND_MICRO
;
66 timeval_subtract (struct timeval a
, struct timeval b
)
70 ret
.tv_usec
= a
.tv_usec
- b
.tv_usec
;
71 ret
.tv_sec
= a
.tv_sec
- b
.tv_sec
;
73 return timeval_adjust (ret
);
77 timeval_cmp (struct timeval a
, struct timeval b
)
79 return (a
.tv_sec
== b
.tv_sec
80 ? a
.tv_usec
- b
.tv_usec
: a
.tv_sec
- b
.tv_sec
);
84 timeval_elapsed (struct timeval a
, struct timeval b
)
86 return (((a
.tv_sec
- b
.tv_sec
) * TIMER_SECOND_MICRO
)
87 + (a
.tv_usec
- b
.tv_usec
));
91 cpu_record_hash_key (struct cpu_thread_history
*a
)
93 return (unsigned int) a
->func
;
97 cpu_record_hash_cmp (struct cpu_thread_history
*a
,
98 struct cpu_thread_history
*b
)
100 return a
->func
== b
->func
;
104 cpu_record_hash_alloc (struct cpu_thread_history
*a
)
106 struct cpu_thread_history
*new;
107 new = XCALLOC (MTYPE_THREAD_STATS
, sizeof (struct cpu_thread_history
));
109 new->funcname
= XSTRDUP(MTYPE_THREAD_FUNCNAME
, a
->funcname
);
114 vty_out_cpu_thread_history(struct vty
* vty
,
115 struct cpu_thread_history
*a
)
117 vty_out(vty
, " %7ld.%03ld %9d %8ld %10ld %c%c%c%c%c %s%s",
118 a
->total
/1000, a
->total
%1000, a
->total_calls
,
119 a
->total
/a
->total_calls
, a
->max
,
120 a
->types
& (1 << THREAD_READ
) ? 'R':' ',
121 a
->types
& (1 << THREAD_WRITE
) ? 'W':' ',
122 a
->types
& (1 << THREAD_TIMER
) ? 'T':' ',
123 a
->types
& (1 << THREAD_EVENT
) ? 'E':' ',
124 a
->types
& (1 << THREAD_EXECUTE
) ? 'X':' ',
125 a
->funcname
, VTY_NEWLINE
);
129 cpu_record_hash_print(struct hash_backet
*bucket
,
132 struct cpu_thread_history
*totals
= args
[0];
133 struct vty
*vty
= args
[1];
134 unsigned char *filter
= args
[2];
135 struct cpu_thread_history
*a
= bucket
->data
;
139 if ( !(a
->types
& *filter
) )
141 vty_out_cpu_thread_history(vty
,a
);
142 totals
->total
+= a
->total
;
143 totals
->total_calls
+= a
->total_calls
;
144 if (totals
->max
< a
->max
)
145 totals
->max
= a
->max
;
149 cpu_record_print(struct vty
*vty
, unsigned char filter
)
151 struct cpu_thread_history tmp
;
152 void *args
[3] = {&tmp
, vty
, &filter
};
154 memset(&tmp
, 0, sizeof tmp
);
155 tmp
.funcname
= "TOTAL";
159 " Runtime(ms) Invoked Avg uSecs Max uSecs Type Thread%s",
161 hash_iterate(cpu_record
,
162 (void(*)(struct hash_backet
*,void*))cpu_record_hash_print
,
165 if (tmp
.total_calls
> 0)
166 vty_out_cpu_thread_history(vty
, &tmp
);
169 DEFUN(show_thread_cpu
,
171 "show thread cpu [FILTER]",
173 "Thread information\n"
175 "Display filter (rwtex)\n")
178 unsigned char filter
= 0xff;
183 while (argv
[0][i
] != '\0')
185 switch ( argv
[0][i
] )
189 filter
|= (1 << THREAD_READ
);
193 filter
|= (1 << THREAD_WRITE
);
197 filter
|= (1 << THREAD_TIMER
);
201 filter
|= (1 << THREAD_EVENT
);
205 filter
|= (1 << THREAD_EXECUTE
);
214 vty_out(vty
, "Invalid filter \"%s\" specified, must contain at least one of 'RWTEX'%s",
215 argv
[0], VTY_NEWLINE
);
220 cpu_record_print(vty
, filter
);
224 /* List allocation and head/tail print out. */
226 thread_list_debug (struct thread_list
*list
)
228 printf ("count [%d] head [%p] tail [%p]\n",
229 list
->count
, list
->head
, list
->tail
);
232 /* Debug print for thread_master. */
234 thread_master_debug (struct thread_master
*m
)
236 printf ("-----------\n");
237 printf ("readlist : ");
238 thread_list_debug (&m
->read
);
239 printf ("writelist : ");
240 thread_list_debug (&m
->write
);
241 printf ("timerlist : ");
242 thread_list_debug (&m
->timer
);
243 printf ("eventlist : ");
244 thread_list_debug (&m
->event
);
245 printf ("unuselist : ");
246 thread_list_debug (&m
->unuse
);
247 printf ("total alloc: [%ld]\n", m
->alloc
);
248 printf ("-----------\n");
251 /* Allocate new thread master. */
252 struct thread_master
*
253 thread_master_create ()
255 if (cpu_record
== NULL
)
257 cpu_record
= hash_create_size( 1011, cpu_record_hash_key
, cpu_record_hash_cmp
);
259 return (struct thread_master
*) XCALLOC (MTYPE_THREAD_MASTER
,
260 sizeof (struct thread_master
));
263 /* Add a new thread to the list. */
265 thread_list_add (struct thread_list
*list
, struct thread
*thread
)
268 thread
->prev
= list
->tail
;
270 list
->tail
->next
= thread
;
277 /* Add a new thread just before the point. */
279 thread_list_add_before (struct thread_list
*list
,
280 struct thread
*point
,
281 struct thread
*thread
)
283 thread
->next
= point
;
284 thread
->prev
= point
->prev
;
286 point
->prev
->next
= thread
;
289 point
->prev
= thread
;
293 /* Delete a thread from the list. */
294 static struct thread
*
295 thread_list_delete (struct thread_list
*list
, struct thread
*thread
)
298 thread
->next
->prev
= thread
->prev
;
300 list
->tail
= thread
->prev
;
302 thread
->prev
->next
= thread
->next
;
304 list
->head
= thread
->next
;
305 thread
->next
= thread
->prev
= NULL
;
310 /* Move thread to unuse list. */
312 thread_add_unuse (struct thread_master
*m
, struct thread
*thread
)
315 assert (thread
->next
== NULL
);
316 assert (thread
->prev
== NULL
);
317 assert (thread
->type
== THREAD_UNUSED
);
318 thread_list_add (&m
->unuse
, thread
);
319 /* XXX: Should we deallocate funcname here? */
322 /* Free all unused thread. */
324 thread_list_free (struct thread_master
*m
, struct thread_list
*list
)
329 for (t
= list
->head
; t
; t
= next
)
332 XFREE (MTYPE_THREAD_FUNCNAME
, t
->funcname
);
333 XFREE (MTYPE_THREAD
, t
);
339 /* Stop thread scheduler. */
341 thread_master_free (struct thread_master
*m
)
343 thread_list_free (m
, &m
->read
);
344 thread_list_free (m
, &m
->write
);
345 thread_list_free (m
, &m
->timer
);
346 thread_list_free (m
, &m
->event
);
347 thread_list_free (m
, &m
->ready
);
348 thread_list_free (m
, &m
->unuse
);
350 XFREE (MTYPE_THREAD_MASTER
, m
);
353 /* Delete top of the list and return it. */
354 static struct thread
*
355 thread_trim_head (struct thread_list
*list
)
358 return thread_list_delete (list
, list
->head
);
362 /* Thread list is empty or not. */
364 thread_empty (struct thread_list
*list
)
366 return list
->head
? 0 : 1;
369 /* Return remain time in second. */
371 thread_timer_remain_second (struct thread
*thread
)
373 struct timeval timer_now
;
375 gettimeofday (&timer_now
, NULL
);
377 if (thread
->u
.sands
.tv_sec
- timer_now
.tv_sec
> 0)
378 return thread
->u
.sands
.tv_sec
- timer_now
.tv_sec
;
383 /* Trim blankspace and "()"s */
385 strip_funcname (const char *funcname
)
388 char tmp
, *ret
, *e
, *b
= buff
;
390 strncpy(buff
, funcname
, sizeof(buff
));
391 buff
[ sizeof(buff
) -1] = '\0';
392 e
= buff
+strlen(buff
) -1;
394 /* Wont work for funcname == "Word (explanation)" */
396 while (*b
== ' ' || *b
== '(')
398 while (*e
== ' ' || *e
== ')')
404 ret
= XSTRDUP (MTYPE_THREAD_FUNCNAME
, b
);
410 /* Get new thread. */
411 static struct thread
*
412 thread_get (struct thread_master
*m
, u_char type
,
413 int (*func
) (struct thread
*), void *arg
, const char* funcname
)
415 struct thread
*thread
;
419 thread
= thread_trim_head (&m
->unuse
);
420 if (thread
->funcname
)
421 XFREE(MTYPE_THREAD_FUNCNAME
, thread
->funcname
);
425 thread
= XCALLOC (MTYPE_THREAD
, sizeof (struct thread
));
429 thread
->add_type
= type
;
434 thread
->funcname
= strip_funcname(funcname
);
439 /* Add new read thread. */
441 funcname_thread_add_read (struct thread_master
*m
,
442 int (*func
) (struct thread
*), void *arg
, int fd
, const char* funcname
)
444 struct thread
*thread
;
448 if (FD_ISSET (fd
, &m
->readfd
))
450 zlog (NULL
, LOG_WARNING
, "There is already read fd [%d]", fd
);
454 thread
= thread_get (m
, THREAD_READ
, func
, arg
, funcname
);
455 FD_SET (fd
, &m
->readfd
);
457 thread_list_add (&m
->read
, thread
);
462 /* Add new write thread. */
464 funcname_thread_add_write (struct thread_master
*m
,
465 int (*func
) (struct thread
*), void *arg
, int fd
, const char* funcname
)
467 struct thread
*thread
;
471 if (FD_ISSET (fd
, &m
->writefd
))
473 zlog (NULL
, LOG_WARNING
, "There is already write fd [%d]", fd
);
477 thread
= thread_get (m
, THREAD_WRITE
, func
, arg
, funcname
);
478 FD_SET (fd
, &m
->writefd
);
480 thread_list_add (&m
->write
, thread
);
485 static struct thread
*
486 funcname_thread_add_timer_timeval (struct thread_master
*m
,
487 int (*func
) (struct thread
*),
489 struct timeval
*time_relative
,
490 const char* funcname
)
492 struct thread
*thread
;
493 struct timeval timer_now
;
494 #ifndef TIMER_NO_SORT
496 #endif /* TIMER_NO_SORT */
500 thread
= thread_get (m
, THREAD_TIMER
, func
, arg
, funcname
);
502 /* Do we need jitter here? */
503 gettimeofday (&timer_now
, NULL
);
504 timer_now
.tv_sec
+= time_relative
->tv_sec
;
505 timer_now
.tv_usec
+= time_relative
->tv_usec
;
506 timeval_adjust (timer_now
);
507 thread
->u
.sands
= timer_now
;
509 /* Sort by timeval. */
511 thread_list_add (&m
->timer
, thread
);
513 for (tt
= m
->timer
.head
; tt
; tt
= tt
->next
)
514 if (timeval_cmp (thread
->u
.sands
, tt
->u
.sands
) <= 0)
518 thread_list_add_before (&m
->timer
, tt
, thread
);
520 thread_list_add (&m
->timer
, thread
);
521 #endif /* TIMER_NO_SORT */
527 /* Add timer event thread. */
529 funcname_thread_add_timer (struct thread_master
*m
,
530 int (*func
) (struct thread
*),
531 void *arg
, long timer
, const char* funcname
)
540 return funcname_thread_add_timer_timeval (m
, func
, arg
, &trel
, funcname
);
543 /* Add timer event thread with "millisecond" resolution */
545 funcname_thread_add_timer_msec (struct thread_master
*m
,
546 int (*func
) (struct thread
*),
547 void *arg
, long timer
, const char* funcname
)
553 timer
= 1000*timer
; /* milli -> micro */
555 trel
.tv_sec
= timer
/ TIMER_SECOND_MICRO
;
556 trel
.tv_usec
= (timer
% TIMER_SECOND_MICRO
);
558 return funcname_thread_add_timer_timeval (m
, func
, arg
, &trel
, funcname
);
561 /* Add simple event thread. */
563 funcname_thread_add_event (struct thread_master
*m
,
564 int (*func
) (struct thread
*), void *arg
, int val
, const char* funcname
)
566 struct thread
*thread
;
570 thread
= thread_get (m
, THREAD_EVENT
, func
, arg
, funcname
);
572 thread_list_add (&m
->event
, thread
);
577 /* Cancel thread from scheduler. */
579 thread_cancel (struct thread
*thread
)
581 switch (thread
->type
)
584 assert (FD_ISSET (thread
->u
.fd
, &thread
->master
->readfd
));
585 FD_CLR (thread
->u
.fd
, &thread
->master
->readfd
);
586 thread_list_delete (&thread
->master
->read
, thread
);
589 assert (FD_ISSET (thread
->u
.fd
, &thread
->master
->writefd
));
590 FD_CLR (thread
->u
.fd
, &thread
->master
->writefd
);
591 thread_list_delete (&thread
->master
->write
, thread
);
594 thread_list_delete (&thread
->master
->timer
, thread
);
597 thread_list_delete (&thread
->master
->event
, thread
);
600 thread_list_delete (&thread
->master
->ready
, thread
);
605 thread
->type
= THREAD_UNUSED
;
606 thread_add_unuse (thread
->master
, thread
);
609 /* Delete all events which has argument value arg. */
611 thread_cancel_event (struct thread_master
*m
, void *arg
)
613 struct thread
*thread
;
615 thread
= m
->event
.head
;
625 thread_list_delete (&m
->event
, t
);
626 t
->type
= THREAD_UNUSED
;
627 thread_add_unuse (m
, t
);
634 thread_timer_wait (struct thread_master
*m
, struct timeval
*timer_val
)
636 struct timeval timer_now
;
637 struct timeval timer_min
;
638 struct timeval
*timer_wait
;
640 gettimeofday (&timer_now
, NULL
);
643 for (thread
= m
->timer
.head
; thread
; thread
= thread
->next
)
646 timer_wait
= &thread
->u
.sands
;
647 else if (timeval_cmp (thread
->u
.sands
, *timer_wait
) < 0)
648 timer_wait
= &thread
->u
.sands
;
653 timer_min
= *timer_wait
;
654 timer_min
= timeval_subtract (timer_min
, timer_now
);
655 if (timer_min
.tv_sec
< 0)
657 timer_min
.tv_sec
= 0;
658 timer_min
.tv_usec
= 10;
660 timer_wait
= &timer_min
;
667 *timer_val
= timer_wait
;
672 #else /* ! TIMER_NO_SORT */
674 thread_timer_wait (struct thread_master
*m
, struct timeval
*timer_val
)
676 struct timeval timer_now
;
677 struct timeval timer_min
;
681 gettimeofday (&timer_now
, NULL
);
682 timer_min
= m
->timer
.head
->u
.sands
;
683 timer_min
= timeval_subtract (timer_min
, timer_now
);
684 if (timer_min
.tv_sec
< 0)
686 timer_min
.tv_sec
= 0;
687 timer_min
.tv_usec
= 10;
689 *timer_val
= timer_min
;
694 #endif /* TIMER_NO_SORT */
697 thread_run (struct thread_master
*m
, struct thread
*thread
,
698 struct thread
*fetch
)
701 thread
->type
= THREAD_UNUSED
;
702 thread_add_unuse (m
, thread
);
707 thread_process_fd (struct thread_master
*m
, struct thread_list
*list
,
708 fd_set
*fdset
, fd_set
*mfdset
)
710 struct thread
*thread
;
714 for (thread
= list
->head
; thread
; thread
= next
)
718 if (FD_ISSET (THREAD_FD (thread
), fdset
))
720 assert (FD_ISSET (THREAD_FD (thread
), mfdset
));
721 FD_CLR(THREAD_FD (thread
), mfdset
);
722 thread_list_delete (list
, thread
);
723 thread_list_add (&m
->ready
, thread
);
724 thread
->type
= THREAD_READY
;
731 /* Fetch next ready thread. */
733 thread_fetch (struct thread_master
*m
, struct thread
*fetch
)
737 struct thread
*thread
;
741 struct timeval timer_now
;
742 struct timeval timer_val
;
743 struct timeval
*timer_wait
;
744 struct timeval timer_nowait
;
746 timer_nowait
.tv_sec
= 0;
747 timer_nowait
.tv_usec
= 0;
751 /* Signals are highest priority */
752 quagga_sigevent_process ();
754 /* Normal event are the next highest priority. */
755 if ((thread
= thread_trim_head (&m
->event
)) != NULL
)
756 return thread_run (m
, thread
, fetch
);
759 gettimeofday (&timer_now
, NULL
);
761 for (thread
= m
->timer
.head
; thread
; thread
= thread
->next
)
762 if (timeval_cmp (timer_now
, thread
->u
.sands
) >= 0)
764 thread_list_delete (&m
->timer
, thread
);
765 return thread_run (m
, thread
, fetch
);
768 /* If there are any ready threads, process top of them. */
769 if ((thread
= thread_trim_head (&m
->ready
)) != NULL
)
770 return thread_run (m
, thread
, fetch
);
772 /* Structure copy. */
774 writefd
= m
->writefd
;
775 exceptfd
= m
->exceptfd
;
777 /* Calculate select wait timer. */
778 timer_wait
= thread_timer_wait (m
, &timer_val
);
780 num
= select (FD_SETSIZE
, &readfd
, &writefd
, &exceptfd
, timer_wait
);
789 /* signal received */
790 quagga_sigevent_process ();
794 zlog_warn ("select() error: %s", safe_strerror (errno
));
798 /* Normal priority read thead. */
799 ready
= thread_process_fd (m
, &m
->read
, &readfd
, &m
->readfd
);
802 ready
= thread_process_fd (m
, &m
->write
, &writefd
, &m
->writefd
);
804 if ((thread
= thread_trim_head (&m
->ready
)) != NULL
)
805 return thread_run (m
, thread
, fetch
);
810 thread_consumed_time (RUSAGE_T
*now
, RUSAGE_T
*start
)
812 unsigned long thread_time
;
815 /* This is 'user + sys' time. */
816 thread_time
= timeval_elapsed (now
->ru_utime
, start
->ru_utime
);
817 thread_time
+= timeval_elapsed (now
->ru_stime
, start
->ru_stime
);
819 /* When rusage is not available, simple elapsed time is used. */
820 thread_time
= timeval_elapsed (*now
, *start
);
821 #endif /* HAVE_RUSAGE */
826 /* We should aim to yield after THREAD_YIELD_TIME_SLOT
829 thread_should_yield (struct thread
*thread
)
835 if (thread_consumed_time (&ru
, &thread
->ru
) > THREAD_YIELD_TIME_SLOT
)
841 /* We check thread consumed time. If the system has getrusage, we'll
842 use that to get indepth stats on the performance of the thread. If
843 not - we'll use gettimeofday for some guestimation. */
845 thread_call (struct thread
*thread
)
847 unsigned long thread_time
;
849 struct cpu_thread_history tmp
, *cpu
;
851 tmp
.func
= thread
->func
;
852 tmp
.funcname
= thread
->funcname
;
853 cpu
= hash_get(cpu_record
, &tmp
, cpu_record_hash_alloc
);
855 GETRUSAGE (&thread
->ru
);
857 (*thread
->func
) (thread
);
861 thread_time
= thread_consumed_time (&ru
, &thread
->ru
);
862 cpu
->total
+= thread_time
;
863 if (cpu
->max
< thread_time
)
864 cpu
->max
= thread_time
;
867 cpu
->types
|= (1 << thread
->add_type
);
869 #ifdef THREAD_CONSUMED_TIME_CHECK
870 if (thread_time
> 200000L)
873 * We have a CPU Hog on our hands.
874 * Whinge about it now, so we're aware this is yet another task
877 zlog_err ("CPU HOG task %s (%lx) ran for %ldms",
879 (unsigned long) thread
->func
,
880 thread_time
/ 1000L);
882 #endif /* THREAD_CONSUMED_TIME_CHECK */
887 funcname_thread_execute (struct thread_master
*m
,
888 int (*func
)(struct thread
*),
891 const char* funcname
)
895 memset (&dummy
, 0, sizeof (struct thread
));
897 dummy
.type
= THREAD_EVENT
;
898 dummy
.add_type
= THREAD_EXECUTE
;
903 dummy
.funcname
= strip_funcname (funcname
);
904 thread_call (&dummy
);
906 XFREE (MTYPE_THREAD_FUNCNAME
, dummy
.funcname
);