summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_mutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_mutex.c')
-rw-r--r--sys/kern/kern_mutex.c278
1 files changed, 30 insertions, 248 deletions
diff --git a/sys/kern/kern_mutex.c b/sys/kern/kern_mutex.c
index 4d9afb6..b4ec843 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
OpenPOWER on IntegriCloud