diff options
Diffstat (limited to 'lib/thread.c')
-rw-r--r-- | lib/thread.c | 153 |
1 files changed, 140 insertions, 13 deletions
diff --git a/lib/thread.c b/lib/thread.c index cf2ec425..90aad4ba 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -228,6 +228,9 @@ recent_relative_time (void) return relative_time; } +/* Uses the address of the function (or at least ls part of same) as the hash + * key. (The function name is for display, only.) + */ static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -288,15 +291,14 @@ static void cpu_record_hash_free (void *a) { struct cpu_thread_history *hist = a; - void* funcname = miyagi(hist->funcname) ; - XFREE (MTYPE_THREAD_FUNCNAME, funcname); + XFREE (MTYPE_THREAD_FUNCNAME, hist->funcname); XFREE (MTYPE_THREAD_STATS, hist); } static inline void vty_out_cpu_thread_history(struct vty* vty, - struct cpu_thread_history *a) + const struct cpu_thread_history *a) { #ifdef HAVE_RUSAGE vty_out(vty, "%7ld.%03ld %9d %8ld %9ld %8ld %9ld", @@ -349,7 +351,9 @@ cpu_record_print(struct vty *vty, thread_type filter) void *args[3] = {&tmp, vty, &filter}; memset(&tmp, 0, sizeof tmp); - tmp.funcname = "TOTAL"; + tmp.funcname = miyagi("TOTAL"); /* NB: will not free tmp in the usual way, + in particular, will not attempt + to free this !! */ tmp.types = filter; #ifdef HAVE_RUSAGE @@ -433,6 +437,89 @@ DEFUN_CALL(show_thread_cpu, 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 thread_list_debug (struct thread_list *list) @@ -630,9 +717,16 @@ thread_get_hist(struct thread* thread, const char* funcname) struct cpu_thread_history* hist ; tmp.func = thread->func ; - tmp.funcname = funcname ; - + tmp.funcname = miyagi(funcname); /* NB: will not free tmp in the usual way, + in particular, will not attempt + to free this !! */ LOCK + + /* This looks up entry which matches the tmp just set up. + * + * If does not find one, allocates a new one -- taking a copy of the + * funcname. + */ hist = hash_get (cpu_record, &tmp, (void * (*) (void *))cpu_record_hash_alloc); UNLOCK @@ -1121,6 +1215,26 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow) } /*------------------------------------------------------------------------------ + * Move the given list of threads to the back of the THREAD_READY queue. + */ +/* 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 -- for standard thread handing. * * (This is not used when using qtimer_pile, or qnexus stuff.) @@ -1141,25 +1255,31 @@ thread_fetch (struct thread_master *m, struct thread *fetch) { 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 */ + 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); @@ -1167,6 +1287,13 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (timer_wait_bg && (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) timer_wait = timer_wait_bg; + } + else + { + timer_val.tv_sec = 0 ; + timer_val.tv_usec = 0 ; + timer_wait = &timer_val ; + } ; num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); |