aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--UPDATING6
-rw-r--r--sys/conf/options2
-rw-r--r--sys/kern/kern_lock.c24
-rw-r--r--sys/kern/kern_mutex.c276
-rw-r--r--sys/kern/kern_rwlock.c22
-rw-r--r--sys/kern/kern_sx.c22
-rw-r--r--sys/kern/subr_lock.c291
-rw-r--r--sys/kern/subr_witness.c2
-rw-r--r--sys/sun4v/include/cpufunc.h3
-rw-r--r--sys/sys/_lock.h24
-rw-r--r--sys/sys/_mutex.h19
-rw-r--r--sys/sys/lock.h3
-rw-r--r--sys/sys/lock_profile.h170
-rw-r--r--sys/sys/lockmgr.h15
-rw-r--r--sys/sys/mutex.h1
15 files changed, 599 insertions, 281 deletions
diff --git a/UPDATING b/UPDATING
index 13c6c68b0ced..428234bced9a 100644
--- a/UPDATING
+++ b/UPDATING
@@ -20,6 +20,12 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 7.x IS SLOW:
in userland, and various verbose features in the kernel. Many
developers choose to disable these features on build machines
to maximize performance.
+20061110:
+ The MUTEX_PROFILING option has been renamed to LOCK_PROFILING.
+ The lockmgr object layout has been changed as a result of having
+ a lock_object embedded in it. As a consequence all file system
+ kernel modules must be re-compiled. The mutex profiling man page
+ has not yet been updated to reflect this change.
20061026:
KSE in the kernel has now been made optional and turned on by
diff --git a/sys/conf/options b/sys/conf/options
index e0681ea40709..ebfe0930b832 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -545,7 +545,7 @@ INVARIANTS opt_global.h
MCLSHIFT opt_global.h
MUTEX_DEBUG opt_global.h
MUTEX_NOINLINE opt_global.h
-MUTEX_PROFILING opt_global.h
+LOCK_PROFILING opt_global.h
MSIZE opt_global.h
REGRESSION opt_global.h
RESTARTABLE_PANICS opt_global.h
diff --git a/sys/kern/kern_lock.c b/sys/kern/kern_lock.c
index 32b43b863b2d..96936c567200 100644
--- a/sys/kern/kern_lock.c
+++ b/sys/kern/kern_lock.c
@@ -44,6 +44,7 @@
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
+#include "opt_global.h"
#include <sys/param.h>
#include <sys/kdb.h>
@@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/systm.h>
+#include <sys/lock_profile.h>
#ifdef DEBUG_LOCKS
#include <sys/stack.h>
#endif
@@ -148,15 +150,14 @@ acquire(struct lock **lkpp, int extflags, int wanted)
* accepted shared locks and shared-to-exclusive upgrades to go away.
*/
int
-lockmgr(lkp, flags, interlkp, td)
- struct lock *lkp;
- u_int flags;
- struct mtx *interlkp;
- struct thread *td;
+_lockmgr(struct lock *lkp, int flags, struct mtx *interlkp,
+ struct thread *td, char *file, int line)
+
{
int error;
struct thread *thr;
int extflags, lockflags;
+ uint64_t waitstart;
error = 0;
if (td == NULL)
@@ -164,6 +165,7 @@ lockmgr(lkp, flags, interlkp, td)
else
thr = td;
+ lock_profile_waitstart(&waitstart);
if ((flags & LK_INTERNAL) == 0)
mtx_lock(lkp->lk_interlock);
CTR6(KTR_LOCK,
@@ -219,6 +221,9 @@ lockmgr(lkp, flags, interlkp, td)
if (error)
break;
sharelock(td, lkp, 1);
+ if (lkp->lk_sharecount == 1)
+ lock_profile_obtain_lock_success(&lkp->lk_object, waitstart, file, line);
+
#if defined(DEBUG_LOCKS)
stack_save(&lkp->lk_stack);
#endif
@@ -229,6 +234,8 @@ lockmgr(lkp, flags, interlkp, td)
* An alternative would be to fail with EDEADLK.
*/
sharelock(td, lkp, 1);
+ if (lkp->lk_sharecount == 1)
+ lock_profile_obtain_lock_success(&lkp->lk_object, waitstart, file, line);
/* FALLTHROUGH downgrade */
case LK_DOWNGRADE:
@@ -272,6 +279,8 @@ lockmgr(lkp, flags, interlkp, td)
if (lkp->lk_sharecount <= 0)
panic("lockmgr: upgrade without shared");
shareunlock(td, lkp, 1);
+ if (lkp->lk_sharecount == 0)
+ lock_profile_release_lock(&lkp->lk_object);
/*
* If we are just polling, check to see if we will block.
*/
@@ -302,6 +311,7 @@ lockmgr(lkp, flags, interlkp, td)
lkp->lk_lockholder = thr;
lkp->lk_exclusivecount = 1;
COUNT(td, 1);
+ lock_profile_obtain_lock_success(&lkp->lk_object, waitstart, file, line);
#if defined(DEBUG_LOCKS)
stack_save(&lkp->lk_stack);
#endif
@@ -361,6 +371,7 @@ lockmgr(lkp, flags, interlkp, td)
panic("lockmgr: non-zero exclusive count");
lkp->lk_exclusivecount = 1;
COUNT(td, 1);
+ lock_profile_obtain_lock_success(&lkp->lk_object, waitstart, file, line);
#if defined(DEBUG_LOCKS)
stack_save(&lkp->lk_stack);
#endif
@@ -380,6 +391,7 @@ lockmgr(lkp, flags, interlkp, td)
lkp->lk_flags &= ~LK_HAVE_EXCL;
lkp->lk_lockholder = LK_NOPROC;
lkp->lk_exclusivecount = 0;
+ lock_profile_release_lock(&lkp->lk_object);
} else {
lkp->lk_exclusivecount--;
}
@@ -509,6 +521,7 @@ lockinit(lkp, prio, wmesg, timo, flags)
#ifdef DEBUG_LOCKS
stack_zero(&lkp->lk_stack);
#endif
+ lock_profile_object_init(&lkp->lk_object, wmesg);
}
/*
@@ -520,6 +533,7 @@ lockdestroy(lkp)
{
CTR2(KTR_LOCK, "lockdestroy(): lkp == %p (lk_wmesg == \"%s\")",
lkp, lkp->lk_wmesg);
+ lock_profile_object_destroy(&lkp->lk_object);
}
/*
diff --git a/sys/kern/kern_mutex.c b/sys/kern/kern_mutex.c
index 4d9afb660b0f..b4ec84390950 100644
--- a/sys/kern/kern_mutex.c
+++ b/sys/kern/kern_mutex.c
@@ -38,7 +38,7 @@ __FBSDID("$FreeBSD$");
#include "opt_adaptive_mutexes.h"
#include "opt_ddb.h"
-#include "opt_mprof.h"
+#include "opt_global.h"
#include "opt_mutex_wake_all.h"
#include "opt_sched.h"
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/turnstile.h>
#include <sys/vmmeter.h>
+#include <sys/lock_profile.h>
#include <machine/atomic.h>
#include <machine/bus.h>
@@ -115,158 +116,6 @@ struct lock_class lock_class_mtx_spin = {
struct mtx sched_lock;
struct mtx Giant;
-#ifdef MUTEX_PROFILING
-SYSCTL_NODE(_debug, OID_AUTO, mutex, CTLFLAG_RD, NULL, "mutex debugging");
-SYSCTL_NODE(_debug_mutex, OID_AUTO, prof, CTLFLAG_RD, NULL, "mutex profiling");
-static int mutex_prof_enable = 0;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, enable, CTLFLAG_RW,
- &mutex_prof_enable, 0, "Enable tracing of mutex holdtime");
-
-struct mutex_prof {
- const char *name;
- const char *file;
- int line;
- uintmax_t cnt_max;
- uintmax_t cnt_tot;
- uintmax_t cnt_cur;
- uintmax_t cnt_contest_holding;
- uintmax_t cnt_contest_locking;
- struct mutex_prof *next;
-};
-
-/*
- * mprof_buf is a static pool of profiling records to avoid possible
- * reentrance of the memory allocation functions.
- *
- * Note: NUM_MPROF_BUFFERS must be smaller than MPROF_HASH_SIZE.
- */
-#ifdef MPROF_BUFFERS
-#define NUM_MPROF_BUFFERS MPROF_BUFFERS
-#else
-#define NUM_MPROF_BUFFERS 1000
-#endif
-static struct mutex_prof mprof_buf[NUM_MPROF_BUFFERS];
-static int first_free_mprof_buf;
-#ifndef MPROF_HASH_SIZE
-#define MPROF_HASH_SIZE 1009
-#endif
-#if NUM_MPROF_BUFFERS >= MPROF_HASH_SIZE
-#error MPROF_BUFFERS must be larger than MPROF_HASH_SIZE
-#endif
-static struct mutex_prof *mprof_hash[MPROF_HASH_SIZE];
-/* SWAG: sbuf size = avg stat. line size * number of locks */
-#define MPROF_SBUF_SIZE 256 * 400
-
-static int mutex_prof_acquisitions;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, acquisitions, CTLFLAG_RD,
- &mutex_prof_acquisitions, 0, "Number of mutex acquistions recorded");
-static int mutex_prof_records;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, records, CTLFLAG_RD,
- &mutex_prof_records, 0, "Number of profiling records");
-static int mutex_prof_maxrecords = NUM_MPROF_BUFFERS;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, maxrecords, CTLFLAG_RD,
- &mutex_prof_maxrecords, 0, "Maximum number of profiling records");
-static int mutex_prof_rejected;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, rejected, CTLFLAG_RD,
- &mutex_prof_rejected, 0, "Number of rejected profiling records");
-static int mutex_prof_hashsize = MPROF_HASH_SIZE;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, hashsize, CTLFLAG_RD,
- &mutex_prof_hashsize, 0, "Hash size");
-static int mutex_prof_collisions = 0;
-SYSCTL_INT(_debug_mutex_prof, OID_AUTO, collisions, CTLFLAG_RD,
- &mutex_prof_collisions, 0, "Number of hash collisions");
-
-/*
- * mprof_mtx protects the profiling buffers and the hash.
- */
-static struct mtx mprof_mtx;
-MTX_SYSINIT(mprof, &mprof_mtx, "mutex profiling lock", MTX_SPIN | MTX_QUIET);
-
-static u_int64_t
-nanoseconds(void)
-{
- struct timespec tv;
-
- nanotime(&tv);
- return (tv.tv_sec * (u_int64_t)1000000000 + tv.tv_nsec);
-}
-
-static int
-dump_mutex_prof_stats(SYSCTL_HANDLER_ARGS)
-{
- struct sbuf *sb;
- int error, i;
- static int multiplier = 1;
-
- if (first_free_mprof_buf == 0)
- return (SYSCTL_OUT(req, "No locking recorded",
- sizeof("No locking recorded")));
-
-retry_sbufops:
- sb = sbuf_new(NULL, NULL, MPROF_SBUF_SIZE * multiplier, SBUF_FIXEDLEN);
- sbuf_printf(sb, "\n%6s %12s %11s %5s %12s %12s %s\n",
- "max", "total", "count", "avg", "cnt_hold", "cnt_lock", "name");
- /*
- * XXX this spinlock seems to be by far the largest perpetrator
- * of spinlock latency (1.6 msec on an Athlon1600 was recorded
- * even before I pessimized it further by moving the average
- * computation here).
- */
- mtx_lock_spin(&mprof_mtx);
- for (i = 0; i < first_free_mprof_buf; ++i) {
- sbuf_printf(sb, "%6ju %12ju %11ju %5ju %12ju %12ju %s:%d (%s)\n",
- mprof_buf[i].cnt_max / 1000,
- mprof_buf[i].cnt_tot / 1000,
- mprof_buf[i].cnt_cur,
- mprof_buf[i].cnt_cur == 0 ? (uintmax_t)0 :
- mprof_buf[i].cnt_tot / (mprof_buf[i].cnt_cur * 1000),
- mprof_buf[i].cnt_contest_holding,
- mprof_buf[i].cnt_contest_locking,
- mprof_buf[i].file, mprof_buf[i].line, mprof_buf[i].name);
- if (sbuf_overflowed(sb)) {
- mtx_unlock_spin(&mprof_mtx);
- sbuf_delete(sb);
- multiplier++;
- goto retry_sbufops;
- }
- }
- mtx_unlock_spin(&mprof_mtx);
- sbuf_finish(sb);
- error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
- sbuf_delete(sb);
- return (error);
-}
-SYSCTL_PROC(_debug_mutex_prof, OID_AUTO, stats, CTLTYPE_STRING | CTLFLAG_RD,
- NULL, 0, dump_mutex_prof_stats, "A", "Mutex profiling statistics");
-
-static int
-reset_mutex_prof_stats(SYSCTL_HANDLER_ARGS)
-{
- int error, v;
-
- if (first_free_mprof_buf == 0)
- return (0);
-
- v = 0;
- error = sysctl_handle_int(oidp, &v, 0, req);
- if (error)
- return (error);
- if (req->newptr == NULL)
- return (error);
- if (v == 0)
- return (0);
-
- mtx_lock_spin(&mprof_mtx);
- bzero(mprof_buf, sizeof(*mprof_buf) * first_free_mprof_buf);
- bzero(mprof_hash, sizeof(struct mtx *) * MPROF_HASH_SIZE);
- first_free_mprof_buf = 0;
- mtx_unlock_spin(&mprof_mtx);
- return (0);
-}
-SYSCTL_PROC(_debug_mutex_prof, OID_AUTO, reset, CTLTYPE_INT | CTLFLAG_RW,
- NULL, 0, reset_mutex_prof_stats, "I", "Reset mutex profiling statistics");
-#endif
-
/*
* Function versions of the inlined __mtx_* macros. These are used by
* modules and can also be called from assembly language if needed.
@@ -274,6 +123,7 @@ SYSCTL_PROC(_debug_mutex_prof, OID_AUTO, reset, CTLTYPE_INT | CTLFLAG_RW,
void
_mtx_lock_flags(struct mtx *m, int opts, const char *file, int line)
{
+ uint64_t waittime;
MPASS(curthread != NULL);
KASSERT(m->mtx_lock != MTX_DESTROYED,
@@ -283,20 +133,14 @@ _mtx_lock_flags(struct mtx *m, int opts, const char *file, int line)
file, line));
WITNESS_CHECKORDER(&m->mtx_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE,
file, line);
+
+ lock_profile_waitstart(&waittime);
_get_sleep_lock(m, curthread, opts, file, line);
LOCK_LOG_LOCK("LOCK", &m->mtx_object, opts, m->mtx_recurse, file,
line);
WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE, file, line);
curthread->td_locks++;
-#ifdef MUTEX_PROFILING
- /* don't reset the timer when/if recursing */
- if (m->mtx_acqtime == 0) {
- m->mtx_filename = file;
- m->mtx_lineno = line;
- m->mtx_acqtime = mutex_prof_enable ? nanoseconds() : 0;
- ++mutex_prof_acquisitions;
- }
-#endif
+ lock_profile_obtain_lock_success(&m->mtx_object, waittime, file, line);
}
void
@@ -314,76 +158,16 @@ _mtx_unlock_flags(struct mtx *m, int opts, const char *file, int line)
LOCK_LOG_LOCK("UNLOCK", &m->mtx_object, opts, m->mtx_recurse, file,
line);
mtx_assert(m, MA_OWNED);
-#ifdef MUTEX_PROFILING
- if (m->mtx_acqtime != 0) {
- static const char *unknown = "(unknown)";
- struct mutex_prof *mpp;
- u_int64_t acqtime, now;
- const char *p, *q;
- volatile u_int hash;
- now = nanoseconds();
- acqtime = m->mtx_acqtime;
- m->mtx_acqtime = 0;
- if (now <= acqtime)
- goto out;
- for (p = m->mtx_filename;
- p != NULL && strncmp(p, "../", 3) == 0; p += 3)
- /* nothing */ ;
- if (p == NULL || *p == '\0')
- p = unknown;
- for (hash = m->mtx_lineno, q = p; *q != '\0'; ++q)
- hash = (hash * 2 + *q) % MPROF_HASH_SIZE;
- mtx_lock_spin(&mprof_mtx);
- for (mpp = mprof_hash[hash]; mpp != NULL; mpp = mpp->next)
- if (mpp->line == m->mtx_lineno &&
- strcmp(mpp->file, p) == 0)
- break;
- if (mpp == NULL) {
- /* Just exit if we cannot get a trace buffer */
- if (first_free_mprof_buf >= NUM_MPROF_BUFFERS) {
- ++mutex_prof_rejected;
- goto unlock;
- }
- mpp = &mprof_buf[first_free_mprof_buf++];
- mpp->name = mtx_name(m);
- mpp->file = p;
- mpp->line = m->mtx_lineno;
- mpp->next = mprof_hash[hash];
- if (mprof_hash[hash] != NULL)
- ++mutex_prof_collisions;
- mprof_hash[hash] = mpp;
- ++mutex_prof_records;
- }
- /*
- * Record if the mutex has been held longer now than ever
- * before.
- */
- if (now - acqtime > mpp->cnt_max)
- mpp->cnt_max = now - acqtime;
- mpp->cnt_tot += now - acqtime;
- mpp->cnt_cur++;
- /*
- * There's a small race, really we should cmpxchg
- * 0 with the current value, but that would bill
- * the contention to the wrong lock instance if
- * it followed this also.
- */
- mpp->cnt_contest_holding += m->mtx_contest_holding;
- m->mtx_contest_holding = 0;
- mpp->cnt_contest_locking += m->mtx_contest_locking;
- m->mtx_contest_locking = 0;
-unlock:
- mtx_unlock_spin(&mprof_mtx);
- }
-out:
-#endif
+ lock_profile_release_lock(&m->mtx_object);
_rel_sleep_lock(m, curthread, opts, file, line);
}
void
_mtx_lock_spin_flags(struct mtx *m, int opts, const char *file, int line)
{
+
+ uint64_t waittime;
MPASS(curthread != NULL);
KASSERT(m->mtx_lock != MTX_DESTROYED,
@@ -393,10 +177,12 @@ _mtx_lock_spin_flags(struct mtx *m, int opts, const char *file, int line)
m->mtx_object.lo_name, file, line));
WITNESS_CHECKORDER(&m->mtx_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE,
file, line);
+ lock_profile_waitstart(&waittime);
_get_spin_lock(m, curthread, opts, file, line);
LOCK_LOG_LOCK("LOCK", &m->mtx_object, opts, m->mtx_recurse, file,
line);
WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE, file, line);
+ lock_profile_obtain_lock_success(&m->mtx_object, waittime, file, line);
}
void
@@ -413,6 +199,7 @@ _mtx_unlock_spin_flags(struct mtx *m, int opts, const char *file, int line)
LOCK_LOG_LOCK("UNLOCK", &m->mtx_object, opts, m->mtx_recurse, file,
line);
mtx_assert(m, MA_OWNED);
+ lock_profile_release_lock(&m->mtx_object);
_rel_spin_lock(m);
}
@@ -425,6 +212,7 @@ int
_mtx_trylock(struct mtx *m, int opts, const char *file, int line)
{
int rval;
+ uint64_t waittime = 0;
MPASS(curthread != NULL);
KASSERT(m->mtx_lock != MTX_DESTROYED,
@@ -445,6 +233,8 @@ _mtx_trylock(struct mtx *m, int opts, const char *file, int line)
WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE | LOP_TRYLOCK,
file, line);
curthread->td_locks++;
+ lock_profile_obtain_lock_success(&m->mtx_object, waittime, file, line);
+
}
return (rval);
@@ -463,13 +253,11 @@ _mtx_lock_sleep(struct mtx *m, uintptr_t tid, int opts, const char *file,
#if defined(SMP) && !defined(NO_ADAPTIVE_MUTEXES)
volatile struct thread *owner;
#endif
- uintptr_t v;
#ifdef KTR
int cont_logged = 0;
#endif
-#ifdef MUTEX_PROFILING
+ uintptr_t v;
int contested;
-#endif
if (mtx_owned(m)) {
KASSERT((m->mtx_object.lo_flags & LO_RECURSABLE) != 0,
@@ -487,14 +275,8 @@ _mtx_lock_sleep(struct mtx *m, uintptr_t tid, int opts, const char *file,
"_mtx_lock_sleep: %s contested (lock=%p) at %s:%d",
m->mtx_object.lo_name, (void *)m->mtx_lock, file, line);
-#ifdef MUTEX_PROFILING
- contested = 0;
-#endif
while (!_obtain_lock(m, tid)) {
-#ifdef MUTEX_PROFILING
- contested = 1;
- atomic_add_int(&m->mtx_contest_holding, 1);
-#endif
+ lock_profile_obtain_lock_failed(&m->mtx_object, &contested);
turnstile_lock(&m->mtx_object);
v = m->mtx_lock;
@@ -585,10 +367,10 @@ _mtx_lock_sleep(struct mtx *m, uintptr_t tid, int opts, const char *file,
m->mtx_object.lo_name, (void *)tid, file, line);
}
#endif
-#ifdef MUTEX_PROFILING
+#ifdef LOCK_PROFILING
if (contested)
- m->mtx_contest_locking++;
- m->mtx_contest_holding = 0;
+ m->mtx_object.lo_profile_obj.lpo_contest_locking++;
+ m->mtx_object.lo_profile_obj.lpo_contest_holding = 0;
#endif
return;
}
@@ -605,12 +387,13 @@ _mtx_lock_spin(struct mtx *m, uintptr_t tid, int opts, const char *file,
int line)
{
struct thread *td;
- int i = 0;
+ int contested, i = 0;
if (LOCK_LOG_TEST(&m->mtx_object, opts))
CTR1(KTR_LOCK, "_mtx_lock_spin: %p spinning", m);
while (!_obtain_lock(m, tid)) {
+ lock_profile_obtain_lock_failed(&m->mtx_object, &contested);
/* Give interrupts a chance while we spin. */
spinlock_exit();
@@ -845,7 +628,7 @@ mtx_init(struct mtx *m, const char *name, const char *type, int opts)
int flags;
MPASS((opts & ~(MTX_SPIN | MTX_QUIET | MTX_RECURSE |
- MTX_NOWITNESS | MTX_DUPOK)) == 0);
+ MTX_NOWITNESS | MTX_DUPOK | MTX_NOPROFILE)) == 0);
#ifdef MUTEX_DEBUG
/* Diagnostic and error correction */
@@ -866,18 +649,14 @@ mtx_init(struct mtx *m, const char *name, const char *type, int opts)
flags |= LO_WITNESS;
if (opts & MTX_DUPOK)
flags |= LO_DUPOK;
+ if (opts & MTX_NOPROFILE)
+ flags |= LO_NOPROFILE;
/* Initialize mutex. */
m->mtx_lock = MTX_UNOWNED;
m->mtx_recurse = 0;
-#ifdef MUTEX_PROFILING
- m->mtx_acqtime = 0;
- m->mtx_filename = NULL;
- m->mtx_lineno = 0;
- m->mtx_contest_holding = 0;
- m->mtx_contest_locking = 0;
-#endif
+ lock_profile_object_init(&m->mtx_object, name);
lock_init(&m->mtx_object, class, name, type, flags);
}
@@ -908,6 +687,7 @@ mtx_destroy(struct mtx *m)
}
m->mtx_lock = MTX_DESTROYED;
+ lock_profile_object_destroy(&m->mtx_object);
lock_destroy(&m->mtx_object);
}
@@ -931,6 +711,8 @@ mutex_init(void)
mtx_init(&proc0.p_mtx, "process lock", NULL, MTX_DEF | MTX_DUPOK);
mtx_init(&devmtx, "cdev", NULL, MTX_DEF);
mtx_lock(&Giant);
+
+ lock_profile_init();
}
#ifdef DDB
diff --git a/sys/kern/kern_rwlock.c b/sys/kern/kern_rwlock.c
index 424afdc1e6ae..96ed3f0aaeb8 100644
--- a/sys/kern/kern_rwlock.c
+++ b/sys/kern/kern_rwlock.c
@@ -44,7 +44,7 @@ __FBSDID("$FreeBSD$");
#include <sys/rwlock.h>
#include <sys/systm.h>
#include <sys/turnstile.h>
-
+#include <sys/lock_profile.h>
#include <machine/cpu.h>
#ifdef DDB
@@ -86,6 +86,7 @@ rw_init(struct rwlock *rw, const char *name)
rw->rw_lock = RW_UNLOCKED;
+ lock_profile_object_init(&rw->rw_object, name);
lock_init(&rw->rw_object, &lock_class_rw, name, NULL, LO_WITNESS |
LO_RECURSABLE | LO_UPGRADABLE);
}
@@ -95,6 +96,7 @@ rw_destroy(struct rwlock *rw)
{
KASSERT(rw->rw_lock == RW_UNLOCKED, ("rw lock not unlocked"));
+ lock_profile_object_destroy(&rw->rw_object);
lock_destroy(&rw->rw_object);
}
@@ -109,6 +111,7 @@ rw_sysinit(void *arg)
void
_rw_wlock(struct rwlock *rw, const char *file, int line)
{
+ uint64_t waitstart;
MPASS(curthread != NULL);
KASSERT(rw_wowner(rw) != curthread,
@@ -116,7 +119,9 @@ _rw_wlock(struct rwlock *rw, const char *file, int line)
rw->rw_object.lo_name, file, line));
WITNESS_CHECKORDER(&rw->rw_object, LOP_NEWORDER | LOP_EXCLUSIVE, file,
line);
+ lock_profile_waitstart(&waitstart);
__rw_wlock(rw, curthread, file, line);
+ lock_profile_obtain_lock_success(&rw->rw_object, waitstart, file, line);
LOCK_LOG_LOCK("WLOCK", &rw->rw_object, 0, 0, file, line);
WITNESS_LOCK(&rw->rw_object, LOP_EXCLUSIVE, file, line);
curthread->td_locks++;
@@ -131,6 +136,7 @@ _rw_wunlock(struct rwlock *rw, const char *file, int line)
curthread->td_locks--;
WITNESS_UNLOCK(&rw->rw_object, LOP_EXCLUSIVE, file, line);
LOCK_LOG_LOCK("WUNLOCK", &rw->rw_object, 0, 0, file, line);
+ lock_profile_release_lock(&rw->rw_object);
__rw_wunlock(rw, curthread, file, line);
}
@@ -140,6 +146,8 @@ _rw_rlock(struct rwlock *rw, const char *file, int line)
#ifdef SMP
volatile struct thread *owner;
#endif
+ uint64_t waitstart;
+ int contested;
uintptr_t x;
KASSERT(rw_wowner(rw) != curthread,
@@ -158,6 +166,7 @@ _rw_rlock(struct rwlock *rw, const char *file, int line)
* be blocked on the writer, and the writer would be blocked
* waiting for the reader to release its original read lock.
*/
+ lock_profile_waitstart(&waitstart);
for (;;) {
/*
* Handle the easy case. If no other thread has a write
@@ -180,6 +189,7 @@ _rw_rlock(struct rwlock *rw, const char *file, int line)
MPASS((x & RW_LOCK_READ_WAITERS) == 0);
if (atomic_cmpset_acq_ptr(&rw->rw_lock, x,
x + RW_ONE_READER)) {
+ lock_profile_obtain_lock_success(&rw->rw_object, waitstart, file, line);
if (LOCK_LOG_TEST(&rw->rw_object, 0))
CTR4(KTR_LOCK,
"%s: %p succeed %p -> %p", __func__,
@@ -188,6 +198,7 @@ _rw_rlock(struct rwlock *rw, const char *file, int line)
break;
}
cpu_spinwait();
+ lock_profile_obtain_lock_failed(&rw->rw_object, &contested);
continue;
}
@@ -236,6 +247,7 @@ _rw_rlock(struct rwlock *rw, const char *file, int line)
*/
owner = (struct thread *)RW_OWNER(x);
if (TD_IS_RUNNING(owner)) {
+ lock_profile_obtain_lock_failed(&rw->rw_object, &contested);
turnstile_release(&rw->rw_object);
if (LOCK_LOG_TEST(&rw->rw_object, 0))
CTR3(KTR_LOCK, "%s: spinning on %p held by %p",
@@ -301,7 +313,9 @@ _rw_runlock(struct rwlock *rw, const char *file, int line)
break;
}
continue;
- }
+ } else
+ lock_profile_release_lock(&rw->rw_object);
+
/*
* We should never have read waiters while at least one
@@ -397,6 +411,7 @@ _rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line)
#ifdef SMP
volatile struct thread *owner;
#endif
+ int contested;
uintptr_t v;
if (LOCK_LOG_TEST(&rw->rw_object, 0))
@@ -438,6 +453,7 @@ _rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line)
}
turnstile_release(&rw->rw_object);
cpu_spinwait();
+ lock_profile_obtain_lock_failed(&rw->rw_object, &contested);
continue;
}
@@ -451,6 +467,7 @@ _rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line)
v | RW_LOCK_WRITE_WAITERS)) {
turnstile_release(&rw->rw_object);
cpu_spinwait();
+ lock_profile_obtain_lock_failed(&rw->rw_object, &contested);
continue;
}
if (LOCK_LOG_TEST(&rw->rw_object, 0))
@@ -466,6 +483,7 @@ _rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line)
*/
owner = (struct thread *)RW_OWNER(v);
if (!(v & RW_LOCK_READ) && TD_IS_RUNNING(owner)) {
+ lock_profile_obtain_lock_failed(&rw->rw_object, &contested);
turnstile_release(&rw->rw_object);
if (LOCK_LOG_TEST(&rw->rw_object, 0))
CTR3(KTR_LOCK, "%s: spinning on %p held by %p",
diff --git a/sys/kern/kern_sx.c b/sys/kern/kern_sx.c
index fdd8bc92c43d..23fdb72455e7 100644
--- a/sys/kern/kern_sx.c
+++ b/sys/kern/kern_sx.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sx.h>
+#include <sys/lock_profile.h>
#ifdef DDB
#include <ddb/ddb.h>
@@ -85,6 +86,7 @@ sx_init(struct sx *sx, const char *description)
cv_init(&sx->sx_excl_cv, description);
sx->sx_excl_wcnt = 0;
sx->sx_xholder = NULL;
+ lock_profile_object_init(&sx->sx_object, description);
lock_init(&sx->sx_object, &lock_class_sx, description, NULL,
LO_WITNESS | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE);
}
@@ -100,13 +102,16 @@ sx_destroy(struct sx *sx)
sx->sx_lock = NULL;
cv_destroy(&sx->sx_shrd_cv);
cv_destroy(&sx->sx_excl_cv);
-
+
+ lock_profile_object_destroy(&sx->sx_object);
lock_destroy(&sx->sx_object);
}
void
_sx_slock(struct sx *sx, const char *file, int line)
{
+ uint64_t waittime = 0;
+ int contested;
mtx_lock(sx->sx_lock);
KASSERT(sx->sx_xholder != curthread,
@@ -117,8 +122,11 @@ _sx_slock(struct sx *sx, const char *file, int line)
/*
* Loop in case we lose the race for lock acquisition.
*/
+ if (sx->sx_cnt < 0)
+ lock_profile_waitstart(&waittime);
while (sx->sx_cnt < 0) {
sx->sx_shrd_wcnt++;
+ lock_profile_obtain_lock_failed(&sx->sx_object, &contested);
cv_wait(&sx->sx_shrd_cv, sx->sx_lock);
sx->sx_shrd_wcnt--;
}
@@ -126,6 +134,9 @@ _sx_slock(struct sx *sx, const char *file, int line)
/* Acquire a shared lock. */
sx->sx_cnt++;
+ if (sx->sx_cnt == 1)
+ lock_profile_obtain_lock_success(&sx->sx_object, waittime, file, line);
+
LOCK_LOG_LOCK("SLOCK", &sx->sx_object, 0, 0, file, line);
WITNESS_LOCK(&sx->sx_object, 0, file, line);
curthread->td_locks++;
@@ -155,6 +166,8 @@ _sx_try_slock(struct sx *sx, const char *file, int line)
void
_sx_xlock(struct sx *sx, const char *file, int line)
{
+ int contested;
+ uint64_t waittime = 0;
mtx_lock(sx->sx_lock);
@@ -171,9 +184,12 @@ _sx_xlock(struct sx *sx, const char *file, int line)
WITNESS_CHECKORDER(&sx->sx_object, LOP_NEWORDER | LOP_EXCLUSIVE, file,
line);
+ if (sx->sx_cnt)
+ lock_profile_waitstart(&waittime);
/* Loop in case we lose the race for lock acquisition. */
while (sx->sx_cnt != 0) {
sx->sx_excl_wcnt++;
+ lock_profile_obtain_lock_failed(&sx->sx_object, &contested);
cv_wait(&sx->sx_excl_cv, sx->sx_lock);
sx->sx_excl_wcnt--;
}
@@ -184,6 +200,7 @@ _sx_xlock(struct sx *sx, const char *file, int line)
sx->sx_cnt--;
sx->sx_xholder = curthread;
+ lock_profile_obtain_lock_success(&sx->sx_object, waittime, file, line);
LOCK_LOG_LOCK("XLOCK", &sx->sx_object, 0, 0, file, line);
WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
curthread->td_locks++;
@@ -225,6 +242,8 @@ _sx_sunlock(struct sx *sx, const char *file, int line)
/* Release. */
sx->sx_cnt--;
+ if (sx->sx_cnt == 0)
+ lock_profile_release_lock(&sx->sx_object);
/*
* If we just released the last shared lock, wake any waiters up, giving
* exclusive lockers precedence. In order to make sure that exclusive
@@ -257,6 +276,7 @@ _sx_xunlock(struct sx *sx, const char *file, int line)
sx->sx_cnt++;
sx->sx_xholder = NULL;
+ lock_profile_release_lock(&sx->sx_object);
/*
* Wake up waiters if there are any. Give precedence to slock waiters.
*/
diff --git a/sys/kern/subr_lock.c b/sys/kern/subr_lock.c
index ecf300ac3bef..25b26ae03e08 100644
--- a/sys/kern/subr_lock.c
+++ b/sys/kern/subr_lock.c
@@ -36,12 +36,16 @@
__FBSDID("$FreeBSD$");
#include "opt_ddb.h"
+#include "opt_mprof.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ktr.h>
#include <sys/linker_set.h>
#include <sys/lock.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
+#include <sys/lock_profile.h>
#ifdef DDB
#include <ddb/ddb.h>
@@ -56,6 +60,136 @@ struct lock_class *lock_classes[LOCK_CLASS_MAX + 1] = {
&lock_class_rw,
};
+#ifdef LOCK_PROFILING
+#include <machine/cpufunc.h>
+
+SYSCTL_NODE(_debug, OID_AUTO, lock, CTLFLAG_RD, NULL, "lock debugging");
+SYSCTL_NODE(_debug_lock, OID_AUTO, prof, CTLFLAG_RD, NULL, "lock profiling");
+int lock_prof_enable = 0;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, enable, CTLFLAG_RW,
+ &lock_prof_enable, 0, "Enable lock profiling");
+
+/*
+ * lprof_buf is a static pool of profiling records to avoid possible
+ * reentrance of the memory allocation functions.
+ *
+ * Note: NUM_LPROF_BUFFERS must be smaller than LPROF_HASH_SIZE.
+ */
+struct lock_prof lprof_buf[LPROF_HASH_SIZE];
+static int allocated_lprof_buf;
+struct mtx lprof_locks[LPROF_LOCK_SIZE];
+
+
+/* SWAG: sbuf size = avg stat. line size * number of locks */
+#define LPROF_SBUF_SIZE 256 * 400
+
+static int lock_prof_acquisitions;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, acquisitions, CTLFLAG_RD,
+ &lock_prof_acquisitions, 0, "Number of mutex acquistions recorded");
+static int lock_prof_records;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, records, CTLFLAG_RD,
+ &lock_prof_records, 0, "Number of profiling records");
+static int lock_prof_maxrecords = LPROF_HASH_SIZE;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, maxrecords, CTLFLAG_RD,
+ &lock_prof_maxrecords, 0, "Maximum number of profiling records");
+static int lock_prof_rejected;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, rejected, CTLFLAG_RD,
+ &lock_prof_rejected, 0, "Number of rejected profiling records");
+static int lock_prof_hashsize = LPROF_HASH_SIZE;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, hashsize, CTLFLAG_RD,
+ &lock_prof_hashsize, 0, "Hash size");
+static int lock_prof_collisions = 0;
+SYSCTL_INT(_debug_lock_prof, OID_AUTO, collisions, CTLFLAG_RD,
+ &lock_prof_collisions, 0, "Number of hash collisions");
+
+#ifndef USE_CPU_NANOSECONDS
+static u_int64_t
+nanoseconds(void)
+{
+ struct timespec tv;
+
+ nanotime(&tv);
+ return (tv.tv_sec * (u_int64_t)1000000000 + tv.tv_nsec);
+}
+#endif
+
+static int
+dump_lock_prof_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct sbuf *sb;
+ int error, i;
+ static int multiplier = 1;
+ const char *p;
+
+ if (allocated_lprof_buf == 0)
+ return (SYSCTL_OUT(req, "No locking recorded",
+ sizeof("No locking recorded")));
+
+retry_sbufops:
+ sb = sbuf_new(NULL, NULL, LPROF_SBUF_SIZE * multiplier, SBUF_FIXEDLEN);
+ sbuf_printf(sb, "\n%6s %12s %12s %11s %5s %5s %12s %12s %s\n",
+ "max", "total", "wait_total", "count", "avg", "wait_avg", "cnt_hold", "cn\
+t_lock", "name");
+ for (i = 0; i < LPROF_HASH_SIZE; ++i) {
+ if (lprof_buf[i].name == NULL)
+ continue;
+ for (p = lprof_buf[i].file;
+ p != NULL && strncmp(p, "../", 3) == 0; p += 3)
+ /* nothing */ ;
+ sbuf_printf(sb, "%6ju %12ju %12ju %11ju %5ju %5ju %12ju %12ju %s:%d (\
+%s)\n",
+ lprof_buf[i].cnt_max / 1000,
+ lprof_buf[i].cnt_tot / 1000,
+ lprof_buf[i].cnt_wait / 1000,
+ lprof_buf[i].cnt_cur,
+ lprof_buf[i].cnt_cur == 0 ? (uintmax_t)0 :
+ lprof_buf[i].cnt_tot / (lprof_buf[i].cnt_cur * 1000),
+ lprof_buf[i].cnt_cur == 0 ? (uintmax_t)0 :
+ lprof_buf[i].cnt_wait / (lprof_buf[i].cnt_cur * 1000),
+ lprof_buf[i].cnt_contest_holding,
+ lprof_buf[i].cnt_contest_locking,
+ p, lprof_buf[i].line, lprof_buf[i].name);
+ if (sbuf_overflowed(sb)) {
+ sbuf_delete(sb);
+ multiplier++;
+ goto retry_sbufops;
+ }
+ }
+
+ sbuf_finish(sb);
+ error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
+ sbuf_delete(sb);
+ return (error);
+}
+static int
+reset_lock_prof_stats(SYSCTL_HANDLER_ARGS)
+{
+ int error, v;
+
+ if (allocated_lprof_buf == 0)
+ return (0);
+
+ v = 0;
+ error = sysctl_handle_int(oidp, &v, 0, req);
+ if (error)
+ return (error);
+ if (req->newptr == NULL)
+ return (error);
+ if (v == 0)
+ return (0);
+
+ bzero(lprof_buf, LPROF_HASH_SIZE*sizeof(*lprof_buf));
+ allocated_lprof_buf = 0;
+ return (0);
+}
+
+SYSCTL_PROC(_debug_lock_prof, OID_AUTO, stats, CTLTYPE_STRING | CTLFLAG_RD,
+ NULL, 0, dump_lock_prof_stats, "A", "Mutex profiling statistics");
+
+SYSCTL_PROC(_debug_lock_prof, OID_AUTO, reset, CTLTYPE_INT | CTLFLAG_RW,
+ NULL, 0, reset_lock_prof_stats, "I", "Reset mutex profiling statistics");
+#endif
+
void
lock_init(struct lock_object *lock, struct lock_class *class, const char *name,
const char *type, int flags)
@@ -113,3 +247,160 @@ DB_SHOW_COMMAND(lock, db_show_lock)
class->lc_ddb_show(lock);
}
#endif
+
+#ifdef LOCK_PROFILING
+void _lock_profile_obtain_lock_success(struct lock_object *lo, uint64_t waittime, con\
+st char *file, int line)
+{
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+
+ /* don't reset the timer when/if recursing */
+ if (l->lpo_acqtime == 0) {
+ l->lpo_filename = file;
+ l->lpo_lineno = line;
+ l->lpo_acqtime = nanoseconds();
+ if (waittime) {
+ if (l->lpo_acqtime > waittime)
+ l->lpo_waittime = l->lpo_acqtime - waittime;
+ }
+ }
+}
+
+void _lock_profile_update_wait(struct lock_object *lo, uint64_t waitstart)
+{
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+
+ if (lock_prof_enable && waitstart) {
+ uint64_t now, waittime;
+ struct lock_prof *mpp;
+ u_int hash;
+ const char *p = l->lpo_filename;
+ int collision = 0;
+ now = nanoseconds();
+ if (now < waitstart)
+ return;
+ waittime = now - waitstart;
+ hash = (l->lpo_namehash * 31 * 31 + (uintptr_t)p * 31 + l->lpo_lineno) & LPROF_HASH_MASK;
+
+ mpp = &lprof_buf[hash];
+ while (mpp->name != NULL) {
+ if (mpp->line == l->lpo_lineno &&
+ mpp->file == p &&
+ mpp->namehash == l->lpo_namehash)
+ break;
+ /* If the lprof_hash entry is allocated to someone else, try the next one */
+ collision = 1;
+ CTR4(KTR_SPARE1, "Hash collision, %s:%d %s(%x)", mpp->file, mpp->line, mpp->name, mpp->namehash);
+ hash = (hash + 1) & LPROF_HASH_MASK;
+ mpp = &lprof_buf[hash];
+ }
+ if (mpp->name == NULL) {
+ int buf;
+
+ buf = atomic_fetchadd_int(&allocated_lprof_buf, 1);
+ /* Just exit if we cannot get a trace buffer */
+ if (buf >= LPROF_HASH_SIZE) {
+ ++lock_prof_rejected;
+ return;
+ }
+ mpp->file = p;
+ mpp->line = l->lpo_lineno;
+ mpp->name = lo->lo_name;
+ mpp->namehash = l->lpo_namehash;
+ if (collision)
+ ++lock_prof_collisions;
+ /* We might have raced someone else but who cares, they'll try again next time */
+ ++lock_prof_records;
+ }
+ LPROF_LOCK(hash);
+ mpp->cnt_wait += waittime;
+ LPROF_UNLOCK(hash);
+ }
+}
+
+void _lock_profile_release_lock(struct lock_object *lo)
+{
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+
+ if (l->lpo_acqtime && !(lo->lo_flags & LO_NOPROFILE)) {
+ const char *unknown = "(unknown)";
+ u_int64_t acqtime, now, waittime;
+ struct lock_prof *mpp;
+ u_int hash;
+ const char *p = l->lpo_filename;
+ int collision = 0;
+
+ now = nanoseconds();
+ acqtime = l->lpo_acqtime;
+ waittime = l->lpo_waittime;
+ if (now <= acqtime)
+ return;
+ if (p == NULL || *p == '\0')
+ p = unknown;
+ hash = (l->lpo_namehash * 31 * 31 + (uintptr_t)p * 31 + l->lpo_lineno) & LPROF_HASH_MASK;
+ CTR5(KTR_SPARE1, "Hashing %s(%x) %s:%d to %d", l->lpo_name,
+ l->lpo_namehash, p, l->lpo_lineno, hash);
+ mpp = &lprof_buf[hash];
+ while (mpp->name != NULL) {
+ if (mpp->line == l->lpo_lineno &&
+ mpp->file == p &&
+ mpp->namehash == l->lpo_namehash)
+ break;
+ /* If the lprof_hash entry is allocated to someone
+ * else, try the next one
+ */
+ collision = 1;
+ CTR4(KTR_SPARE1, "Hash collision, %s:%d %s(%x)", mpp->file,
+ mpp->line, mpp->name, mpp->namehash);
+ hash = (hash + 1) & LPROF_HASH_MASK;
+ mpp = &lprof_buf[hash];
+ }
+ if (mpp->name == NULL) {
+ int buf;
+
+ buf = atomic_fetchadd_int(&allocated_lprof_buf, 1);
+ /* Just exit if we cannot get a trace buffer */
+ if (buf >= LPROF_HASH_SIZE) {
+ ++lock_prof_rejected;
+ return;
+ }
+ mpp->file = p;
+ mpp->line = l->lpo_lineno;
+ mpp->name = lo->lo_name;
+ mpp->namehash = l->lpo_namehash;
+ if (collision)
+ ++lock_prof_collisions;
+
+ /*
+ * We might have raced someone else but who cares,
+ * they'll try again next time
+ */
+ ++lock_prof_records;
+ }
+ LPROF_LOCK(hash);
+ /*
+ * Record if the mutex has been held longer now than ever
+ * before.
+ */
+ if (now - acqtime > mpp->cnt_max)
+ mpp->cnt_max = now - acqtime;
+ mpp->cnt_tot += now - acqtime;
+ mpp->cnt_wait += waittime;
+ mpp->cnt_cur++;
+ /*
+ * There's a small race, really we should cmpxchg
+ * 0 with the current value, but that would bill
+ * the contention to the wrong lock instance if
+ * it followed this also.
+ */
+ mpp->cnt_contest_holding += l->lpo_contest_holding;
+ mpp->cnt_contest_locking += l->lpo_contest_locking;
+ LPROF_UNLOCK(hash);
+
+ }
+ l->lpo_acqtime = 0;
+ l->lpo_waittime = 0;
+ l->lpo_contest_locking = 0;
+ l->lpo_contest_holding = 0;
+}
+#endif
diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c
index a99172e93c31..7b9d77d3df8b 100644
--- a/sys/kern/subr_witness.c
+++ b/sys/kern/subr_witness.c
@@ -482,7 +482,7 @@ witness_initialize(void *dummy __unused)
CTR1(KTR_WITNESS, "%s: initializing witness", __func__);
mtx_init(&w_mtx, "witness lock", NULL, MTX_SPIN | MTX_QUIET |
- MTX_NOWITNESS);
+ MTX_NOWITNESS | MTX_NOPROFILE);
for (i = 0; i < WITNESS_COUNT; i++)
witness_free(&w_data[i]);
for (i = 0; i < WITNESS_CHILDCOUNT; i++)
diff --git a/sys/sun4v/include/cpufunc.h b/sys/sun4v/include/cpufunc.h
index 01d51d5ea014..fd7fd7d021f5 100644
--- a/sys/sun4v/include/cpufunc.h
+++ b/sys/sun4v/include/cpufunc.h
@@ -249,6 +249,9 @@ void aszero(u_long asi, vm_offset_t dst, size_t len);
#include <machine/sun4v_cpufunc.h>
+#define USE_CPU_NANOSECONDS
+#define nanoseconds() rd(tick)
+
#undef LDNC_GEN
#undef STNC_GEN
diff --git a/sys/sys/_lock.h b/sys/sys/_lock.h
index dd0b0715d426..22553498fd5e 100644
--- a/sys/sys/_lock.h
+++ b/sys/sys/_lock.h
@@ -31,10 +31,34 @@
#ifndef _SYS__LOCK_H_
#define _SYS__LOCK_H_
+struct lock_profile_object {
+ /*
+ * This does not result in variant structure sizes because
+ * MUTEX_PROFILING is in opt_global.h
+ */
+ u_int64_t lpo_acqtime;
+ u_int64_t lpo_waittime;
+ const char *lpo_filename;
+ u_int lpo_namehash;
+ int lpo_lineno;
+ /*
+ * Fields relating to measuring contention on mutexes.
+ * holding must be accessed atomically since it's
+ * modified by threads that don't yet hold the mutex.
+ * locking is only modified and referenced while
+ * the mutex is held.
+ */
+ u_int lpo_contest_holding;
+ u_int lpo_contest_locking;
+};
+
struct lock_object {
const char *lo_name; /* Individual lock name. */
const char *lo_type; /* General lock type. */
u_int lo_flags;
+#ifdef LOCK_PROFILING
+ struct lock_profile_object lo_profile_obj;
+#endif
union { /* Data for witness. */
STAILQ_ENTRY(lock_object) lod_list;
struct witness *lod_witness;
diff --git a/sys/sys/_mutex.h b/sys/sys/_mutex.h
index 9b24459456bd..f80603268757 100644
--- a/sys/sys/_mutex.h
+++ b/sys/sys/_mutex.h
@@ -38,25 +38,6 @@ struct mtx {
struct lock_object mtx_object; /* Common lock properties. */
volatile uintptr_t mtx_lock; /* Owner and flags. */
volatile u_int mtx_recurse; /* Number of recursive holds. */
-
-#ifdef MUTEX_PROFILING
- /*
- * This does not result in variant structure sizes because
- * MUTEX_PROFILING is in opt_global.h
- */
- u_int64_t mtx_acqtime;
- const char *mtx_filename;
- int mtx_lineno;
- /*
- * Fields relating to measuring contention on mutexes.
- * holding must be accessed atomically since it's
- * modified by threads that don't yet hold the mutex.
- * locking is only modified and referenced while
- * the mutex is held.
- */
- u_int mtx_contest_holding;
- u_int mtx_contest_locking;
-#endif
};
#endif /* !_SYS__MUTEX_H_ */
diff --git a/sys/sys/lock.h b/sys/sys/lock.h
index b10b54abf2ae..9ca937612cff 100644
--- a/sys/sys/lock.h
+++ b/sys/sys/lock.h
@@ -69,6 +69,7 @@ struct lock_class {
#define LO_DUPOK 0x00400000 /* Don't check for duplicate acquires */
#define LO_ENROLLPEND 0x00800000 /* On the pending enroll list. */
#define LO_CLASSMASK 0x0f000000 /* Class index bitmask. */
+#define LO_NOPROFILE 0x10000000 /* Don't profile this lock */
/*
* Lock classes are statically assigned an index into the gobal lock_classes
@@ -142,7 +143,7 @@ struct lock_list_entry {
* calling conventions for this debugging code in modules so that modules can
* work with both debug and non-debug kernels.
*/
-#if defined(KLD_MODULE) || defined(WITNESS) || defined(INVARIANTS) || defined(INVARIANT_SUPPORT) || defined(KTR) || defined(MUTEX_PROFILING)
+#if defined(KLD_MODULE) || defined(WITNESS) || defined(INVARIANTS) || defined(INVARIANT_SUPPORT) || defined(KTR) || defined(LOCK_PROFILING)
#define LOCK_DEBUG 1
#else
#define LOCK_DEBUG 0
diff --git a/sys/sys/lock_profile.h b/sys/sys/lock_profile.h
new file mode 100644
index 000000000000..c4b33e79d190
--- /dev/null
+++ b/sys/sys/lock_profile.h
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2006 Kip Macy kmacy@FreeBSD.org
+ * Copyright (c) 2006 Dag-Erling Smorgrav des@des.no
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHAL THE AUTHORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#ifndef _SYS_LOCK_PROFILE_H_
+#define _SYS_LOCK_PROFILE_H_
+
+#ifdef LOCK_PROFILING
+#include <sys/stdint.h>
+#include <sys/ktr.h>
+#include <sys/mutex.h>
+
+#ifndef LPROF_HASH_SIZE
+#define LPROF_HASH_SIZE 4096
+#define LPROF_HASH_MASK (LPROF_HASH_SIZE - 1)
+#endif
+struct lock_prof {
+ const char *name;
+ const char *file;
+ u_int namehash;
+ int line;
+ uintmax_t cnt_max;
+ uintmax_t cnt_tot;
+ uintmax_t cnt_wait;
+ uintmax_t cnt_cur;
+ uintmax_t cnt_contest_holding;
+ uintmax_t cnt_contest_locking;
+};
+
+extern struct lock_prof lprof_buf[LPROF_HASH_SIZE];
+extern int allocated_lprof_buf;
+#define LPROF_SBUF_SIZE 256 * 400
+
+/* We keep a smaller pool of spin mutexes for protecting the lprof hash entries */
+#define LPROF_LOCK_SIZE 16
+#define LPROF_LOCK_MASK (LPROF_LOCK_SIZE - 1)
+#define LPROF_LHASH(hash) ((hash) & LPROF_LOCK_MASK)
+
+#define LPROF_LOCK(hash) mtx_lock_spin(&lprof_locks[LPROF_LHASH(hash)])
+#define LPROF_UNLOCK(hash) mtx_unlock_spin(&lprof_locks[LPROF_LHASH(hash)])
+
+extern struct mtx lprof_locks[LPROF_LOCK_SIZE];
+extern int lock_prof_enable;
+extern int lock_prof_records;
+extern int lock_prof_rejected;
+extern int lock_prof_collisions;
+
+void _lock_profile_obtain_lock_success(struct lock_object *lo, uint64_t waittime, const char *file, int line);
+void _lock_profile_update_wait(struct lock_object *lo, uint64_t waitstart);
+void _lock_profile_release_lock(struct lock_object *lo);
+
+static inline void lock_profile_init(void)
+{
+ int i;
+ /* Initialize the mutex profiling locks */
+ for (i = 0; i < LPROF_LOCK_SIZE; i++) {
+ mtx_init(&lprof_locks[i], "mprof lock",
+ NULL, MTX_SPIN|MTX_QUIET|MTX_NOPROFILE);
+ }
+}
+
+static inline void lock_profile_object_init(struct lock_object *lo, const char *name) {
+ const char *p;
+ u_int hash = 0;
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+
+ lo->lo_flags = 0;
+ lo->lo_name = name;
+ l->lpo_acqtime = 0;
+ l->lpo_waittime = 0;
+ l->lpo_filename = NULL;
+ l->lpo_lineno = 0;
+ l->lpo_contest_holding = 0;
+ l->lpo_contest_locking = 0;
+
+ /* Hash the mutex name to an int so we don't have to strcmp() it repeatedly */
+ for (p = name; *p != '\0'; p++)
+ hash = 31 * hash + *p;
+ l->lpo_namehash = hash;
+#if 0
+ if (opts & MTX_PROFILE)
+ l->lpo_stack = stack_create();
+#endif
+}
+
+
+static inline void
+lock_profile_object_destroy(struct lock_object *lo)
+{
+#if 0
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+ if (lo->lo_flags & LO_PROFILE)
+ stack_destroy(l->lpo_stack);
+#endif
+}
+
+static inline void lock_profile_waitstart(uint64_t *waittime)
+{
+ if (lock_prof_enable)
+ *waittime = rd(tick);
+}
+
+static inline void lock_profile_obtain_lock_failed(struct lock_object *lo, int *contested)
+{
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+ if (lock_prof_enable) {
+ *contested = 1;
+ atomic_add_int(&l->lpo_contest_holding, 1);
+ }
+}
+
+static inline void lock_profile_obtain_lock_success(struct lock_object *lo, uint64_t waittime, const char *file, int line)
+{
+ if (lock_prof_enable)
+ _lock_profile_obtain_lock_success(lo, waittime, file, line);
+}
+
+static inline void lock_profile_update_wait(struct lock_object *lo, uint64_t waitstart)
+{
+ if (lock_prof_enable)
+ _lock_profile_update_wait(lo, waitstart);
+}
+
+static inline void lock_profile_release_lock(struct lock_object *lo)
+{
+ struct lock_profile_object *l = &lo->lo_profile_obj;
+ if (lock_prof_enable || l->lpo_acqtime)
+ _lock_profile_release_lock(lo);
+}
+
+#else /* !LOCK_PROFILING */
+
+static inline void lock_profile_init(void) {;}
+static inline void lock_profile_update_wait(struct lock_object *lo, uint64_t waitstart) {;}
+static inline void lock_profile_waitstart(uint64_t *waittime) {;}
+static inline void lock_profile_release_lock(struct lock_object *lo) {;}
+static inline void lock_profile_obtain_lock_failed(struct lock_object *lo, int *contested) {;}
+static inline void lock_profile_obtain_lock_success(struct lock_object *lo, uint64_t waittime,
+ const char *file, int line) {;}
+static inline void lock_profile_object_destroy(struct lock_object *lo) {;}
+static inline void lock_profile_object_init(struct lock_object *lo, const char *name) {;}
+
+#endif /* !LOCK_PROFILING */
+
+#endif /* _SYS_LOCK_PROFILE_H_ */
diff --git a/sys/sys/lockmgr.h b/sys/sys/lockmgr.h
index 9c9b59bc8adc..e3a0ac31a3a2 100644
--- a/sys/sys/lockmgr.h
+++ b/sys/sys/lockmgr.h
@@ -40,6 +40,8 @@
#ifdef DEBUG_LOCKS
#include <sys/stack.h> /* XXX */
#endif
+#include <sys/queue.h>
+#include <sys/_lock.h>
struct mtx;
@@ -49,20 +51,23 @@ struct mtx;
* can be gained.
*/
struct lock {
+ struct lock_object lk_object; /* common lock properties */
struct mtx *lk_interlock; /* lock on remaining fields */
- u_int lk_flags; /* see below */
int lk_sharecount; /* # of accepted shared locks */
int lk_waitcount; /* # of processes sleeping for lock */
short lk_exclusivecount; /* # of recursive exclusive locks */
short lk_prio; /* priority at which to sleep */
- const char *lk_wmesg; /* resource sleeping (for tsleep) */
int lk_timo; /* maximum sleep time (for tsleep) */
struct thread *lk_lockholder; /* thread of exclusive lock holder */
struct lock *lk_newlock; /* lock taking over this lock */
+
#ifdef DEBUG_LOCKS
struct stack lk_stack;
#endif
};
+
+#define lk_flags lk_object.lo_flags
+#define lk_wmesg lk_object.lo_name
/*
* Lock request types:
* LK_SHARED - get one of many possible shared locks. If a process
@@ -197,13 +202,15 @@ void lockinit(struct lock *, int prio, const char *wmesg,
int timo, int flags);
void lockdestroy(struct lock *);
-int lockmgr(struct lock *, u_int flags,
- struct mtx *, struct thread *p);
+int _lockmgr(struct lock *, int flags,
+ struct mtx *, struct thread *p, char *file, int line);
void transferlockers(struct lock *, struct lock *);
void lockmgr_printinfo(struct lock *);
int lockstatus(struct lock *, struct thread *);
int lockcount(struct lock *);
int lockwaiters(struct lock *);
+
+#define lockmgr(lock, flags, mtx, td) _lockmgr((lock), (flags), (mtx), (td), __FILE__, __LINE__)
#ifdef DDB
int lockmgr_chain(struct thread *td, struct thread **ownerp);
#endif
diff --git a/sys/sys/mutex.h b/sys/sys/mutex.h
index 1a13e1a61a28..c5acf6c33a13 100644
--- a/sys/sys/mutex.h
+++ b/sys/sys/mutex.h
@@ -56,6 +56,7 @@
#define MTX_SPIN 0x00000001 /* Spin lock (disables interrupts) */
#define MTX_RECURSE 0x00000004 /* Option: lock allowed to recurse */
#define MTX_NOWITNESS 0x00000008 /* Don't do any witness checking. */
+#define MTX_NOPROFILE 0x00000020
/*
* Option flags passed to certain lock/unlock routines, through the use