diff options
Diffstat (limited to 'lib/thread.c')
-rw-r--r-- | lib/thread.c | 274 |
1 files changed, 210 insertions, 64 deletions
diff --git a/lib/thread.c b/lib/thread.c index e89af541..e083e8c7 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -20,8 +20,9 @@ */ /* #define DEBUG */ - +#include <errno.h> #include <zebra.h> +#include <sys/times.h> #include "thread.h" #include "memory.h" @@ -31,8 +32,7 @@ #include "sigevent.h" /* Recent absolute time of day */ -struct timeval recent_time; -static struct timeval last_recent_time; +static struct timeval recent_time; /* Relative time, since startup */ static struct timeval relative_time; static struct timeval relative_time_base; @@ -94,54 +94,98 @@ timeval_elapsed (struct timeval a, struct timeval b) } #ifndef HAVE_CLOCK_MONOTONIC +static unsigned long recent_clock_mark; /* Holds value of last call to times(2) */ +static unsigned long last_clock_mark; +static unsigned long clocks_per_sec, msec_scale, usec_scale; + +static unsigned long quagga_times(void) +{ +#if defined(GNU_LINUX) + unsigned long ret; + + errno = 0; + ret = times(NULL); /* Linux can handle NULL */ + /* Workaround broken syscall impl. + * A bugfix exists for the kernel, hopefully + * it will make it into 2.6.28 + */ + if (errno) + ret = (unsigned long) (-errno); + return ret; +#else + struct tms dummy; /* Only return value is used */ + + return times(&dummy); +#endif +} + static void quagga_gettimeofday_relative_adjust (void) { - struct timeval diff; - if (timeval_cmp (recent_time, last_recent_time) < 0) - { - relative_time.tv_sec++; - relative_time.tv_usec = 0; - } - else - { - diff = timeval_subtract (recent_time, last_recent_time); - relative_time.tv_sec += diff.tv_sec; - relative_time.tv_usec += diff.tv_usec; - relative_time = timeval_adjust (relative_time); - } - last_recent_time = recent_time; + unsigned long diff; + + diff = recent_clock_mark - last_clock_mark; + if (!diff) + return; + /* save mark for next calculation */ + last_clock_mark = recent_clock_mark; + + relative_time.tv_sec += diff / clocks_per_sec; /* convert to seconds */ + relative_time.tv_usec += (diff % clocks_per_sec) * usec_scale; /* convert to useconds */ + relative_time = timeval_adjust (relative_time); } #endif /* !HAVE_CLOCK_MONOTONIC */ +static int init_quagga_timers(void) +{ + int ret; + + ret = gettimeofday (&recent_time, NULL); + relative_time_base = recent_time; +#if !defined(HAVE_CLOCK_MONOTONIC) + clocks_per_sec = sysconf(_SC_CLK_TCK); + assert(clocks_per_sec != 0); + + /* Precondition: 1000 % clocks_per_sec == 0 */ + assert(1000 % clocks_per_sec == 0); + msec_scale = 1000 / clocks_per_sec; + + /* Precondition: TIMER_SECOND_MICRO % clocks_per_sec == 0 */ + assert(TIMER_SECOND_MICRO % clocks_per_sec == 0); + usec_scale = TIMER_SECOND_MICRO / clocks_per_sec; + recent_clock_mark = quagga_times(); + last_clock_mark = recent_clock_mark; +#endif + if (ret) + return ret; + timers_inited = 1; + return 0; +} + /* gettimeofday wrapper, to keep recent_time updated */ static int quagga_gettimeofday (struct timeval *tv) { int ret; - - assert (tv); - - if (!(ret = gettimeofday (&recent_time, NULL))) - { - /* init... */ - if (!timers_inited) - { - relative_time_base = last_recent_time = recent_time; - timers_inited = 1; - } - /* avoid copy if user passed recent_time pointer.. */ - if (tv != &recent_time) - *tv = recent_time; - return 0; - } - return ret; + + /* init... */ + if (!timers_inited) + ret = init_quagga_timers(); + else + ret = gettimeofday (&recent_time, NULL); + if (ret) + return ret; + + /* avoid copy if user passed recent_time pointer.. */ + if (tv != &recent_time) + *tv = recent_time; + return 0; } static int quagga_get_relative (struct timeval *tv) { - int ret; + int ret = 0; #ifdef HAVE_CLOCK_MONOTONIC { @@ -153,8 +197,12 @@ quagga_get_relative (struct timeval *tv) } } #else /* !HAVE_CLOCK_MONOTONIC */ - if (!(ret = quagga_gettimeofday (&recent_time))) - quagga_gettimeofday_relative_adjust(); + /* init... */ + if (!timers_inited) + ret = init_quagga_timers(); + + recent_clock_mark = quagga_times(); + quagga_gettimeofday_relative_adjust(); #endif /* HAVE_CLOCK_MONOTONIC */ if (tv) @@ -382,6 +430,89 @@ DEFUN(show_thread_cpu, cpu_record_print(vty, filter); return CMD_SUCCESS; } + +static void +cpu_record_hash_clear (struct hash_backet *bucket, + void *args) +{ + thread_type *filter = args; + struct cpu_thread_history *a = bucket->data; + + a = bucket->data; + if ( !(a->types & *filter) ) + return; + + hash_release (cpu_record, bucket->data); +} + +static void +cpu_record_clear (thread_type filter) +{ + thread_type *tmp = &filter; + hash_iterate (cpu_record, + (void (*) (struct hash_backet*,void*)) cpu_record_hash_clear, + tmp); +} + +DEFUN(clear_thread_cpu, + clear_thread_cpu_cmd, + "clear thread cpu [FILTER]", + "Clear stored data\n" + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtexb)\n") +{ + int i = 0; + thread_type filter = (thread_type) -1U; + + if (argc > 0) + { + filter = 0; + while (argv[0][i] != '\0') + { + switch ( argv[0][i] ) + { + case 'r': + case 'R': + filter |= (1 << THREAD_READ); + break; + case 'w': + case 'W': + filter |= (1 << THREAD_WRITE); + break; + case 't': + case 'T': + filter |= (1 << THREAD_TIMER); + break; + case 'e': + case 'E': + filter |= (1 << THREAD_EVENT); + break; + case 'x': + case 'X': + filter |= (1 << THREAD_EXECUTE); + break; + case 'b': + case 'B': + filter |= (1 << THREAD_BACKGROUND); + break; + default: + break; + } + ++i; + } + if (filter == 0) + { + vty_out(vty, "Invalid filter \"%s\" specified," + " must contain at least one of 'RWTEXB'%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + + cpu_record_clear (filter); + return CMD_SUCCESS; +} /* List allocation and head/tail print out. */ static void @@ -903,6 +1034,24 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow) return ready; } +/* process a list en masse, e.g. for event thread lists */ +static unsigned int +thread_process (struct thread_list *list) +{ + struct thread *thread; + unsigned int ready = 0; + + for (thread = list->head; thread; thread = thread->next) + { + thread_list_delete (list, thread); + thread->type = THREAD_READY; + thread_list_add (&thread->master->ready, thread); + ready++; + } + return ready; +} + + /* Fetch next ready thread. */ struct thread * thread_fetch (struct thread_master *m, struct thread *fetch) @@ -910,44 +1059,49 @@ thread_fetch (struct thread_master *m, struct thread *fetch) struct thread *thread; fd_set readfd; fd_set writefd; - fd_set exceptfd; - struct timeval timer_val; + struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 }; struct timeval timer_val_bg; - struct timeval *timer_wait; + struct timeval *timer_wait = &timer_val; struct timeval *timer_wait_bg; while (1) { int num = 0; - /* Signals are highest priority */ + /* Signals pre-empt everything */ quagga_sigevent_process (); - /* Normal event are the next highest priority. */ - if ((thread = thread_trim_head (&m->event)) != NULL) - return thread_run (m, thread, fetch); - - /* If there are any ready threads from previous scheduler runs, - * process top of them. + /* Drain the ready queue of already scheduled jobs, before scheduling + * more. */ if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); + /* To be fair to all kinds of threads, and avoid starvation, we + * need to be careful to consider all thread types for scheduling + * in each quanta. I.e. we should not return early from here on. + */ + + /* Normal event are the next highest priority. */ + thread_process (&m->event); + /* Structure copy. */ readfd = m->readfd; writefd = m->writefd; - exceptfd = m->exceptfd; /* Calculate select wait timer if nothing else to do */ - quagga_get_relative (NULL); - timer_wait = thread_timer_wait (&m->timer, &timer_val); - timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); - - if (timer_wait_bg && - (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) - timer_wait = timer_wait_bg; + if (m->ready.count == 0) + { + quagga_get_relative (NULL); + timer_wait = thread_timer_wait (&m->timer, &timer_val); + timer_wait_bg = thread_timer_wait (&m->background, &timer_val_bg); + + if (timer_wait_bg && + (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) + timer_wait = timer_wait_bg; + } - num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); + num = select (FD_SETSIZE, &readfd, &writefd, NULL, timer_wait); /* Signals should get quick treatment */ if (num < 0) @@ -1028,14 +1182,6 @@ thread_getrusage (RUSAGE_T *r) getrusage(RUSAGE_SELF, &(r->cpu)); #endif r->real = relative_time; - -#ifdef HAVE_CLOCK_MONOTONIC - /* quagga_get_relative() only updates recent_time if gettimeofday - * based, not when using CLOCK_MONOTONIC. As we export recent_time - * and guarantee to update it before threads are run... - */ - quagga_gettimeofday(&recent_time); -#endif /* HAVE_CLOCK_MONOTONIC */ } /* We check thread consumed time. If the system has getrusage, we'll |