summaryrefslogtreecommitdiffstats
path: root/lib/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/thread.c')
-rw-r--r--lib/thread.c165
1 files changed, 146 insertions, 19 deletions
diff --git a/lib/thread.c b/lib/thread.c
index 7a9a3a60..078a09d6 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,32 +1255,45 @@ 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 */
- 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 (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;
+ 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);