forked from Mirror/frr
Some small enhancements to thread and workqueue libraries in zebra:
- Allow work queues to specify the yield duration for corresponding background thread - Support using specified yield duration in thread yielding - During work queue processing, if using a single list element with a meta-queue (like done in Zebra), do not exit after each element is processed, instead update the next-node upon a WQ_REQUEUE so that the WQ processing continues and is terminated by the yield logic. - Enhance work queue debug output
This commit is contained in:
parent
5000f21c25
commit
50596be0d5
12
lib/thread.c
12
lib/thread.c
|
@ -706,6 +706,7 @@ thread_get (struct thread_master *m, u_char type,
|
||||||
thread->func = func;
|
thread->func = func;
|
||||||
thread->arg = arg;
|
thread->arg = arg;
|
||||||
thread->index = -1;
|
thread->index = -1;
|
||||||
|
thread->yield = THREAD_YIELD_TIME_SLOT; /* default */
|
||||||
|
|
||||||
strip_funcname (thread->funcname, funcname);
|
strip_funcname (thread->funcname, funcname);
|
||||||
|
|
||||||
|
@ -1198,7 +1199,8 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime)
|
||||||
return timeval_elapsed (now->real, start->real);
|
return timeval_elapsed (now->real, start->real);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We should aim to yield after THREAD_YIELD_TIME_SLOT milliseconds.
|
/* We should aim to yield after yield milliseconds, which defaults
|
||||||
|
to THREAD_YIELD_TIME_SLOT .
|
||||||
Note: we are using real (wall clock) time for this calculation.
|
Note: we are using real (wall clock) time for this calculation.
|
||||||
It could be argued that CPU time may make more sense in certain
|
It could be argued that CPU time may make more sense in certain
|
||||||
contexts. The things to consider are whether the thread may have
|
contexts. The things to consider are whether the thread may have
|
||||||
|
@ -1212,7 +1214,13 @@ thread_should_yield (struct thread *thread)
|
||||||
{
|
{
|
||||||
quagga_get_relative (NULL);
|
quagga_get_relative (NULL);
|
||||||
return (timeval_elapsed(relative_time, thread->real) >
|
return (timeval_elapsed(relative_time, thread->real) >
|
||||||
THREAD_YIELD_TIME_SLOT);
|
thread->yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_set_yield_time (struct thread *thread, unsigned long yield_time)
|
||||||
|
{
|
||||||
|
thread->yield = yield_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -85,6 +85,7 @@ struct thread
|
||||||
int index; /* used for timers to store position in queue */
|
int index; /* used for timers to store position in queue */
|
||||||
struct timeval real;
|
struct timeval real;
|
||||||
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
|
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
|
||||||
|
unsigned long yield; /* yield time in us */
|
||||||
char funcname[FUNCNAME_LEN];
|
char funcname[FUNCNAME_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -209,6 +210,8 @@ extern void thread_call (struct thread *);
|
||||||
extern unsigned long thread_timer_remain_second (struct thread *);
|
extern unsigned long thread_timer_remain_second (struct thread *);
|
||||||
extern int thread_should_yield (struct thread *);
|
extern int thread_should_yield (struct thread *);
|
||||||
extern unsigned long timeval_elapsed (struct timeval a, struct timeval b);
|
extern unsigned long timeval_elapsed (struct timeval a, struct timeval b);
|
||||||
|
/* set yield time for thread */
|
||||||
|
extern void thread_set_yield_time (struct thread *, unsigned long);
|
||||||
|
|
||||||
/* Internal libzebra exports */
|
/* Internal libzebra exports */
|
||||||
extern void thread_getrusage (RUSAGE_T *);
|
extern void thread_getrusage (RUSAGE_T *);
|
||||||
|
|
|
@ -84,6 +84,7 @@ work_queue_new (struct thread_master *m, const char *queue_name)
|
||||||
|
|
||||||
/* Default values, can be overriden by caller */
|
/* Default values, can be overriden by caller */
|
||||||
new->spec.hold = WORK_QUEUE_DEFAULT_HOLD;
|
new->spec.hold = WORK_QUEUE_DEFAULT_HOLD;
|
||||||
|
new->spec.yield = THREAD_YIELD_TIME_SLOT;
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +114,9 @@ work_queue_schedule (struct work_queue *wq, unsigned int delay)
|
||||||
{
|
{
|
||||||
wq->thread = thread_add_background (wq->master, work_queue_run,
|
wq->thread = thread_add_background (wq->master, work_queue_run,
|
||||||
wq, delay);
|
wq, delay);
|
||||||
|
/* set thread yield time, if needed */
|
||||||
|
if (wq->thread && wq->spec.yield != THREAD_YIELD_TIME_SLOT)
|
||||||
|
thread_set_yield_time (wq->thread, wq->spec.yield);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -174,27 +178,27 @@ DEFUN(show_work_queues,
|
||||||
struct work_queue *wq;
|
struct work_queue *wq;
|
||||||
|
|
||||||
vty_out (vty,
|
vty_out (vty,
|
||||||
"%c %8s %5s %8s %21s%s",
|
"%c %8s %5s %8s %8s %21s%s",
|
||||||
' ', "List","(ms) ","Q. Runs","Cycle Counts ",
|
' ', "List","(ms) ","Q. Runs","Yields","Cycle Counts ",
|
||||||
VTY_NEWLINE);
|
VTY_NEWLINE);
|
||||||
vty_out (vty,
|
vty_out (vty,
|
||||||
"%c %8s %5s %8s %7s %6s %6s %s%s",
|
"%c %8s %5s %8s %8s %7s %6s %8s %6s %s%s",
|
||||||
'P',
|
'P',
|
||||||
"Items",
|
"Items",
|
||||||
"Hold",
|
"Hold",
|
||||||
"Total",
|
"Total","Total",
|
||||||
"Best","Gran.","Avg.",
|
"Best","Gran.","Total","Avg.",
|
||||||
"Name",
|
"Name",
|
||||||
VTY_NEWLINE);
|
VTY_NEWLINE);
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS_RO ((&work_queues), node, wq))
|
for (ALL_LIST_ELEMENTS_RO ((&work_queues), node, wq))
|
||||||
{
|
{
|
||||||
vty_out (vty,"%c %8d %5d %8ld %7d %6d %6u %s%s",
|
vty_out (vty,"%c %8d %5d %8ld %8ld %7d %6d %8ld %6u %s%s",
|
||||||
(CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'),
|
(CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'),
|
||||||
listcount (wq->items),
|
listcount (wq->items),
|
||||||
wq->spec.hold,
|
wq->spec.hold,
|
||||||
wq->runs,
|
wq->runs, wq->yields,
|
||||||
wq->cycles.best, wq->cycles.granularity,
|
wq->cycles.best, wq->cycles.granularity, wq->cycles.total,
|
||||||
(wq->runs) ?
|
(wq->runs) ?
|
||||||
(unsigned int) (wq->cycles.total / wq->runs) : 0,
|
(unsigned int) (wq->cycles.total / wq->runs) : 0,
|
||||||
wq->name,
|
wq->name,
|
||||||
|
@ -250,7 +254,8 @@ work_queue_run (struct thread *thread)
|
||||||
assert (wq && wq->items);
|
assert (wq && wq->items);
|
||||||
|
|
||||||
/* calculate cycle granularity:
|
/* calculate cycle granularity:
|
||||||
* list iteration == 1 cycle
|
* list iteration == 1 run
|
||||||
|
* listnode processing == 1 cycle
|
||||||
* granularity == # cycles between checks whether we should yield.
|
* granularity == # cycles between checks whether we should yield.
|
||||||
*
|
*
|
||||||
* granularity should be > 0, and can increase slowly after each run to
|
* granularity should be > 0, and can increase slowly after each run to
|
||||||
|
@ -309,6 +314,14 @@ work_queue_run (struct thread *thread)
|
||||||
{
|
{
|
||||||
item->ran--;
|
item->ran--;
|
||||||
work_queue_item_requeue (wq, node);
|
work_queue_item_requeue (wq, node);
|
||||||
|
/* If a single node is being used with a meta-queue (e.g., zebra),
|
||||||
|
* update the next node as we don't want to exit the thread and
|
||||||
|
* reschedule it after every node. By definition, WQ_REQUEUE is
|
||||||
|
* meant to continue the processing; the yield logic will kick in
|
||||||
|
* to terminate the thread when time has exceeded.
|
||||||
|
*/
|
||||||
|
if (nnode == NULL)
|
||||||
|
nnode = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case WQ_RETRY_NOW:
|
case WQ_RETRY_NOW:
|
||||||
|
@ -346,7 +359,7 @@ stats:
|
||||||
/* we yielded, check whether granularity should be reduced */
|
/* we yielded, check whether granularity should be reduced */
|
||||||
if (yielded && (cycles < wq->cycles.granularity))
|
if (yielded && (cycles < wq->cycles.granularity))
|
||||||
{
|
{
|
||||||
wq->cycles.granularity = ((cycles > 0) ? cycles
|
wq->cycles.granularity = ((cycles > 0) ? cycles
|
||||||
: WORK_QUEUE_MIN_GRANULARITY);
|
: WORK_QUEUE_MIN_GRANULARITY);
|
||||||
}
|
}
|
||||||
/* otherwise, should granularity increase? */
|
/* otherwise, should granularity increase? */
|
||||||
|
@ -354,7 +367,7 @@ stats:
|
||||||
{
|
{
|
||||||
if (cycles > wq->cycles.best)
|
if (cycles > wq->cycles.best)
|
||||||
wq->cycles.best = cycles;
|
wq->cycles.best = cycles;
|
||||||
|
|
||||||
/* along with yielded check, provides hysteresis for granularity */
|
/* along with yielded check, provides hysteresis for granularity */
|
||||||
if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR
|
if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR
|
||||||
* WQ_HYSTERESIS_FACTOR))
|
* WQ_HYSTERESIS_FACTOR))
|
||||||
|
@ -366,6 +379,8 @@ stats:
|
||||||
|
|
||||||
wq->runs++;
|
wq->runs++;
|
||||||
wq->cycles.total += cycles;
|
wq->cycles.total += cycles;
|
||||||
|
if (yielded)
|
||||||
|
wq->yields++;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
printf ("%s: cycles %d, new: best %d, worst %d\n",
|
printf ("%s: cycles %d, new: best %d, worst %d\n",
|
||||||
|
|
|
@ -84,11 +84,14 @@ struct work_queue
|
||||||
unsigned int max_retries;
|
unsigned int max_retries;
|
||||||
|
|
||||||
unsigned int hold; /* hold time for first run, in ms */
|
unsigned int hold; /* hold time for first run, in ms */
|
||||||
|
|
||||||
|
unsigned long yield; /* yield time in us for associated thread */
|
||||||
} spec;
|
} spec;
|
||||||
|
|
||||||
/* remaining fields should be opaque to users */
|
/* remaining fields should be opaque to users */
|
||||||
struct list *items; /* queue item list */
|
struct list *items; /* queue item list */
|
||||||
unsigned long runs; /* runs count */
|
unsigned long runs; /* runs count */
|
||||||
|
unsigned long yields; /* yields count */
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned int best;
|
unsigned int best;
|
||||||
|
|
Loading…
Reference in a new issue