diff options
-rw-r--r-- | sys/conf/NOTES | 8 | ||||
-rw-r--r-- | sys/conf/options | 2 | ||||
-rw-r--r-- | sys/kern/subr_sleepqueue.c | 41 | ||||
-rw-r--r-- | sys/kern/subr_turnstile.c | 51 |
4 files changed, 99 insertions, 3 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 50a1143..d30e1be 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -181,6 +181,10 @@ options MUTEX_NOINLINE # SMP Debugging Options: # # MUTEX_DEBUG enables various extra assertions in the mutex code. +# SLEEPQUEUE_PROFILING enables rudimentary profiling of the hash table +# used to hold active sleep queues. +# TURNSTILE_PROFILING enables rudimentary profiling of the hash table +# used to hold active lock queues. # WITNESS enables the witness code which detects deadlocks and cycles # during locking operations. # WITNESS_DDB causes the witness code to drop into the kernel debugger if @@ -196,6 +200,10 @@ options WITNESS_SKIPSPIN # MUTEX_PROFILING(9) for details. options MUTEX_PROFILING +# Profiling for internal hash tables. +options SLEEPQUEUE_PROFILING +options TURNSTILE_PROFILING + ##################################################################### # COMPATIBILITY OPTIONS diff --git a/sys/conf/options b/sys/conf/options index 7cd8918..1c6c146 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -110,6 +110,7 @@ QUOTA SCHED_4BSD opt_sched.h SCHED_ULE opt_sched.h SHOW_BUSYBUFS +SLEEPQUEUE_PROFILING SPX_HACK SUIDDIR opt_suiddir.h MSGMNB opt_sysvipc.h @@ -134,6 +135,7 @@ SYSVMSG opt_sysvipc.h SYSVSEM opt_sysvipc.h SYSVSHM opt_sysvipc.h SW_WATCHDOG opt_watchdog.h +TURNSTILE_PROFILING TTYHOG opt_tty.h VFS_AIO WLCACHE opt_wavelan.h diff --git a/sys/kern/subr_sleepqueue.c b/sys/kern/subr_sleepqueue.c index a312ddf..79bbf2c 100644 --- a/sys/kern/subr_sleepqueue.c +++ b/sys/kern/subr_sleepqueue.c @@ -59,6 +59,8 @@ * variables. */ +#include "opt_sleepqueue_profiling.h" + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -73,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sched.h> #include <sys/signalvar.h> #include <sys/sleepqueue.h> +#include <sys/sysctl.h> /* * Constants for the hash table of sleep queue chains. These constants are @@ -119,8 +122,20 @@ struct sleepqueue { struct sleepqueue_chain { LIST_HEAD(, sleepqueue) sc_queues; /* List of sleep queues. */ struct mtx sc_lock; /* Spin lock for this chain. */ +#ifdef SLEEPQUEUE_PROFILING + u_int sc_depth; /* Length of sc_queues. */ + u_int sc_max_depth; /* Max length of sc_queues. */ +#endif }; +#ifdef SLEEPQUEUE_PROFILING +u_int sleepq_max_depth; +SYSCTL_NODE(_debug, OID_AUTO, sleepq, CTLFLAG_RD, 0, "sleepq profiling"); +SYSCTL_NODE(_debug_sleepq, OID_AUTO, chains, CTLFLAG_RD, 0, + "sleepq chain stats"); +SYSCTL_UINT(_debug_sleepq, OID_AUTO, max_depth, CTLFLAG_RD, &sleepq_max_depth, + 0, "maxmimum depth achieved of a single chain"); +#endif static struct sleepqueue_chain sleepq_chains[SC_TABLESIZE]; MALLOC_DEFINE(M_SLEEPQUEUE, "sleep queues", "sleep queues"); @@ -141,12 +156,27 @@ static void sleepq_resume_thread(struct thread *td, int pri); void init_sleepqueues(void) { +#ifdef SLEEPQUEUE_PROFILING + struct sysctl_oid *chain_oid; + char chain_name[10]; +#endif int i; for (i = 0; i < SC_TABLESIZE; i++) { LIST_INIT(&sleepq_chains[i].sc_queues); mtx_init(&sleepq_chains[i].sc_lock, "sleepq chain", NULL, MTX_SPIN); +#ifdef SLEEPQUEUE_PROFILING + snprintf(chain_name, sizeof(chain_name), "%d", i); + chain_oid = SYSCTL_ADD_NODE(NULL, + SYSCTL_STATIC_CHILDREN(_debug_sleepq_chains), OID_AUTO, + chain_name, CTLFLAG_RD, NULL, "sleepq chain stats"); + SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(chain_oid), OID_AUTO, + "depth", CTLFLAG_RD, &sleepq_chains[i].sc_depth, 0, NULL); + SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(chain_oid), OID_AUTO, + "max_depth", CTLFLAG_RD, &sleepq_chains[i].sc_max_depth, 0, + NULL); +#endif } thread0.td_sleepqueue = sleepq_alloc(); } @@ -230,6 +260,14 @@ sleepq_add(struct sleepqueue *sq, void *wchan, struct mtx *lock, /* If the passed in sleep queue is NULL, use this thread's queue. */ if (sq == NULL) { +#ifdef SLEEPQUEUE_PROFILING + sc->sc_depth++; + if (sc->sc_depth > sc->sc_max_depth) { + sc->sc_max_depth = sc->sc_depth; + if (sc->sc_max_depth > sleepq_max_depth) + sleepq_max_depth = sc->sc_max_depth; + } +#endif sq = td->td_sleepqueue; LIST_INSERT_HEAD(&sc->sc_queues, sq, sq_hash); KASSERT(TAILQ_EMPTY(&sq->sq_blocked), @@ -555,6 +593,9 @@ sleepq_remove_thread(struct sleepqueue *sq, struct thread *td) #ifdef INVARIANTS sq->sq_wchan = NULL; #endif +#ifdef SLEEPQUEUE_PROFILING + sc->sc_depth--; +#endif } else td->td_sleepqueue = LIST_FIRST(&sq->sq_free); LIST_REMOVE(td->td_sleepqueue, sq_hash); diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c index 8994a1e..5ed4393 100644 --- a/sys/kern/subr_turnstile.c +++ b/sys/kern/subr_turnstile.c @@ -56,6 +56,8 @@ * it from the hash table. */ +#include "opt_turnstile_profiling.h" + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -69,8 +71,9 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/queue.h> #include <sys/resourcevar.h> -#include <sys/turnstile.h> #include <sys/sched.h> +#include <sys/sysctl.h> +#include <sys/turnstile.h> /* * Constants for the hash table of turnstile chains. TC_SHIFT is a magic @@ -116,8 +119,20 @@ struct turnstile { struct turnstile_chain { LIST_HEAD(, turnstile) tc_turnstiles; /* List of turnstiles. */ struct mtx tc_lock; /* Spin lock for this chain. */ +#ifdef TURNSTILE_PROFILING + u_int tc_depth; /* Length of tc_queues. */ + u_int tc_max_depth; /* Max length of tc_queues. */ +#endif }; +#ifdef TURNSTILE_PROFILING +u_int turnstile_max_depth; +SYSCTL_NODE(_debug, OID_AUTO, turnstile, CTLFLAG_RD, 0, "turnstile profiling"); +SYSCTL_NODE(_debug_turnstile, OID_AUTO, chains, CTLFLAG_RD, 0, + "turnstile chain stats"); +SYSCTL_UINT(_debug_turnstile, OID_AUTO, max_depth, CTLFLAG_RD, + &turnstile_max_depth, 0, "maxmimum depth achieved of a single chain"); +#endif static struct mtx td_contested_lock; static struct turnstile_chain turnstile_chains[TC_TABLESIZE]; @@ -292,12 +307,28 @@ propagate_priority(struct thread *td) void init_turnstiles(void) { +#ifdef TURNSTILE_PROFILING + struct sysctl_oid *chain_oid; + char chain_name[10]; +#endif int i; for (i = 0; i < TC_TABLESIZE; i++) { LIST_INIT(&turnstile_chains[i].tc_turnstiles); mtx_init(&turnstile_chains[i].tc_lock, "turnstile chain", NULL, MTX_SPIN); +#ifdef TURNSTILE_PROFILING + snprintf(chain_name, sizeof(chain_name), "%d", i); + chain_oid = SYSCTL_ADD_NODE(NULL, + SYSCTL_STATIC_CHILDREN(_debug_turnstile_chains), OID_AUTO, + chain_name, CTLFLAG_RD, NULL, "turnstile chain stats"); + SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(chain_oid), OID_AUTO, + "depth", CTLFLAG_RD, &turnstile_chains[i].tc_depth, 0, + NULL); + SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(chain_oid), OID_AUTO, + "max_depth", CTLFLAG_RD, &turnstile_chains[i].tc_max_depth, + 0, NULL); +#endif } mtx_init(&td_contested_lock, "td_contested", NULL, MTX_SPIN); thread0.td_turnstile = NULL; @@ -438,6 +469,14 @@ turnstile_wait(struct turnstile *ts, struct lock_object *lock, /* If the passed in turnstile is NULL, use this thread's turnstile. */ if (ts == NULL) { +#ifdef TURNSTILE_PROFILING + tc->tc_depth++; + if (tc->tc_depth > tc->tc_max_depth) { + tc->tc_max_depth = tc->tc_depth; + if (tc->tc_max_depth > turnstile_max_depth) + turnstile_max_depth = tc->tc_max_depth; + } +#endif ts = td->td_turnstile; LIST_INSERT_HEAD(&tc->tc_turnstiles, ts, ts_hash); KASSERT(TAILQ_EMPTY(&ts->ts_pending), @@ -551,9 +590,12 @@ turnstile_signal(struct turnstile *ts) * turnstile from the free list and give it to the thread. */ empty = TAILQ_EMPTY(&ts->ts_blocked); - if (empty) + if (empty) { MPASS(LIST_EMPTY(&ts->ts_free)); - else +#ifdef TURNSTILE_PROFILING + tc->tc_depth--; +#endif + } else ts = LIST_FIRST(&ts->ts_free); MPASS(ts != NULL); LIST_REMOVE(ts, ts_hash); @@ -594,6 +636,9 @@ turnstile_broadcast(struct turnstile *ts) if (LIST_EMPTY(&ts->ts_free)) { MPASS(TAILQ_NEXT(td, td_lockq) == NULL); ts1 = ts; +#ifdef TURNSTILE_PROFILING + tc->tc_depth--; +#endif } else ts1 = LIST_FIRST(&ts->ts_free); MPASS(ts1 != NULL); |