From f02feed2159d1da07fe400df1f0ce6f3fc413f91 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Mon, 11 Jan 2010 15:48:13 +0000 Subject: lib: Thread scheduler should be fair and not let events starve I/O and timers * thread.c: (thread_fetch) the current scheduler will service events indefinitely, ignoring I/O and timers, so long as there are events. In other words, events can crowd out I/O and timers. In theory this shouldn't be a huge problem as events are generated only by timers and I/O, however in practice it means normal-load behaviour is not as useful a predictor of high-load behaviour as it should be. Fix this by considering all the kinds of threads, in every run of the scheduler. For any given run, we prioritise events, however across runs the scheduler should be fair. This has been observed to give more stable inter-packet times in testing of ospfd (i.e. lower std-dev). (thread_process) new heler to queue all the given threads onto the ready list --- lib/thread.c | 57 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-) (limited to 'lib/thread.c') diff --git a/lib/thread.c b/lib/thread.c index e89af541..b6b6df66 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -903,6 +903,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) @@ -911,41 +929,48 @@ thread_fetch (struct thread_master *m, struct thread *fetch) 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); -- cgit v1.2.3 From 28126202f3b419ffc8c391dd18b072d41926cbd8 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Mon, 11 Jan 2010 15:48:15 +0000 Subject: lib: Add a command to clear the thread CPU history data * (general) this can be useful when investigating thread latency problems, when you don't want to have to restart a daemon between tests. * thread.c: (cpu_record_(hash_)clear) wipe the stored thread cpu history data, according to the filter, similar to the vty print code. (clear_thread_cpu_cmd) new command to clear data. * thread.h: export new command * command.c: install it --- lib/thread.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'lib/thread.c') diff --git a/lib/thread.c b/lib/thread.c index b6b6df66..3a68d9e3 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -382,6 +382,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 -- cgit v1.2.3