Add two new kernel options to allow rudimentary profiling of the internal
hash tables used in the sleep queue and turnstile code. Each option adds a sysctl tree under debug containing the maximum depth of any bucket in the hash table as well as a separate node for each bucket (or chain) containing the current depth and maximum depth for that bucket.
This commit is contained in:
parent
c796e27ba8
commit
ef0ebfc351
@ -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
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
@ -554,6 +592,9 @@ sleepq_remove_thread(struct sleepqueue *sq, struct thread *td)
|
||||
td->td_sleepqueue = sq;
|
||||
#ifdef INVARIANTS
|
||||
sq->sq_wchan = NULL;
|
||||
#endif
|
||||
#ifdef SLEEPQUEUE_PROFILING
|
||||
sc->sc_depth--;
|
||||
#endif
|
||||
} else
|
||||
td->td_sleepqueue = LIST_FIRST(&sq->sq_free);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user