freebsd-skq/sys/cddl/dev/lockstat/lockstat.c
Attilio Rao 54366c0bd7 - For kernel compiled only with KDTRACE_HOOKS and not any lock debugging
option, unbreak the lock tracing release semantic by embedding
  calls to LOCKSTAT_PROFILE_RELEASE_LOCK() direclty in the inlined
  version of the releasing functions for mutex, rwlock and sxlock.
  Failing to do so skips the lockstat_probe_func invokation for
  unlocking.
- As part of the LOCKSTAT support is inlined in mutex operation, for
  kernel compiled without lock debugging options, potentially every
  consumer must be compiled including opt_kdtrace.h.
  Fix this by moving KDTRACE_HOOKS into opt_global.h and remove the
  dependency by opt_kdtrace.h for all files, as now only KDTRACE_FRAMES
  is linked there and it is only used as a compile-time stub [0].

[0] immediately shows some new bug as DTRACE-derived support for debug
in sfxge is broken and it was never really tested.  As it was not
including correctly opt_kdtrace.h before it was never enabled so it
was kept broken for a while.  Fix this by using a protection stub,
leaving sfxge driver authors the responsibility for fixing it
appropriately [1].

Sponsored by:	EMC / Isilon storage division
Discussed with:	rstone
[0] Reported by:	rstone
[1] Discussed with:	philip
2013-11-25 07:38:45 +00:00

327 lines
8.6 KiB
C

/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Portions Copyright (c) 2008-2009 Stacey Son <sson@FreeBSD.org>
*
* $FreeBSD$
*
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/dtrace.h>
#include <sys/lockstat.h>
#if defined(__i386__) || defined(__amd64__) || \
defined(__mips__) || defined(__powerpc__)
#define LOCKSTAT_AFRAMES 1
#else
#error "architecture not supported"
#endif
static d_open_t lockstat_open;
static void lockstat_provide(void *, dtrace_probedesc_t *);
static void lockstat_destroy(void *, dtrace_id_t, void *);
static void lockstat_enable(void *, dtrace_id_t, void *);
static void lockstat_disable(void *, dtrace_id_t, void *);
static void lockstat_load(void *);
static int lockstat_unload(void);
typedef struct lockstat_probe {
char *lsp_func;
char *lsp_name;
int lsp_probe;
dtrace_id_t lsp_id;
#ifdef __FreeBSD__
int lsp_frame;
#endif
} lockstat_probe_t;
#ifdef __FreeBSD__
lockstat_probe_t lockstat_probes[] =
{
/* Spin Locks */
{ LS_MTX_SPIN_LOCK, LSS_ACQUIRE, LS_MTX_SPIN_LOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_MTX_SPIN_LOCK, LSS_SPIN, LS_MTX_SPIN_LOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_MTX_SPIN_UNLOCK, LSS_RELEASE, LS_MTX_SPIN_UNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
/* Adaptive Locks */
{ LS_MTX_LOCK, LSA_ACQUIRE, LS_MTX_LOCK_ACQUIRE,
DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
{ LS_MTX_LOCK, LSA_BLOCK, LS_MTX_LOCK_BLOCK,
DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
{ LS_MTX_LOCK, LSA_SPIN, LS_MTX_LOCK_SPIN,
DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
{ LS_MTX_UNLOCK, LSA_RELEASE, LS_MTX_UNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_MTX_TRYLOCK, LSA_ACQUIRE, LS_MTX_TRYLOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
/* Reader/Writer Locks */
{ LS_RW_RLOCK, LSR_ACQUIRE, LS_RW_RLOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_RLOCK, LSR_BLOCK, LS_RW_RLOCK_BLOCK,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_RLOCK, LSR_SPIN, LS_RW_RLOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_RUNLOCK, LSR_RELEASE, LS_RW_RUNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_WLOCK, LSR_ACQUIRE, LS_RW_WLOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_WLOCK, LSR_BLOCK, LS_RW_WLOCK_BLOCK,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_WLOCK, LSR_SPIN, LS_RW_WLOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_WUNLOCK, LSR_RELEASE, LS_RW_WUNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
/* Shared/Exclusive Locks */
{ LS_SX_SLOCK, LSX_ACQUIRE, LS_SX_SLOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_SLOCK, LSX_BLOCK, LS_SX_SLOCK_BLOCK,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_SLOCK, LSX_SPIN, LS_SX_SLOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_SUNLOCK, LSX_RELEASE, LS_SX_SUNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_XLOCK, LSX_ACQUIRE, LS_SX_XLOCK_ACQUIRE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_XLOCK, LSX_BLOCK, LS_SX_XLOCK_BLOCK,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_XLOCK, LSX_SPIN, LS_SX_XLOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_XUNLOCK, LSX_RELEASE, LS_SX_XUNLOCK_RELEASE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_TRYUPGRADE, LSX_UPGRADE, LS_SX_TRYUPGRADE_UPGRADE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ LS_SX_DOWNGRADE, LSX_DOWNGRADE, LS_SX_DOWNGRADE_DOWNGRADE,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
/* Thread Locks */
{ LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN,
DTRACE_IDNONE, LOCKSTAT_AFRAMES },
{ NULL }
};
#else
#error "OS not supported"
#endif
static struct cdevsw lockstat_cdevsw = {
.d_version = D_VERSION,
.d_open = lockstat_open,
.d_name = "lockstat",
};
static struct cdev *lockstat_cdev;
static dtrace_provider_id_t lockstat_id;
/*ARGSUSED*/
static void
lockstat_enable(void *arg, dtrace_id_t id, void *parg)
{
lockstat_probe_t *probe = parg;
ASSERT(!lockstat_probemap[probe->lsp_probe]);
lockstat_probemap[probe->lsp_probe] = id;
#ifdef DOODAD
membar_producer();
#endif
lockstat_probe_func = dtrace_probe;
#ifdef DOODAD
membar_producer();
lockstat_hot_patch();
membar_producer();
#endif
}
/*ARGSUSED*/
static void
lockstat_disable(void *arg, dtrace_id_t id, void *parg)
{
lockstat_probe_t *probe = parg;
int i;
ASSERT(lockstat_probemap[probe->lsp_probe]);
lockstat_probemap[probe->lsp_probe] = 0;
#ifdef DOODAD
lockstat_hot_patch();
membar_producer();
#endif
/*
* See if we have any probes left enabled.
*/
for (i = 0; i < LS_NPROBES; i++) {
if (lockstat_probemap[i]) {
/*
* This probe is still enabled. We don't need to deal
* with waiting for all threads to be out of the
* lockstat critical sections; just return.
*/
return;
}
}
}
/*ARGSUSED*/
static int
lockstat_open(struct cdev *dev __unused, int oflags __unused,
int devtype __unused, struct thread *td __unused)
{
return (0);
}
/*ARGSUSED*/
static void
lockstat_provide(void *arg, dtrace_probedesc_t *desc)
{
int i = 0;
for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
lockstat_probe_t *probe = &lockstat_probes[i];
if (dtrace_probe_lookup(lockstat_id, "kernel",
probe->lsp_func, probe->lsp_name) != 0)
continue;
ASSERT(!probe->lsp_id);
#ifdef __FreeBSD__
probe->lsp_id = dtrace_probe_create(lockstat_id,
"kernel", probe->lsp_func, probe->lsp_name,
probe->lsp_frame, probe);
#else
probe->lsp_id = dtrace_probe_create(lockstat_id,
"kernel", probe->lsp_func, probe->lsp_name,
LOCKSTAT_AFRAMES, probe);
#endif
}
}
/*ARGSUSED*/
static void
lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
{
lockstat_probe_t *probe = parg;
ASSERT(!lockstat_probemap[probe->lsp_probe]);
probe->lsp_id = 0;
}
static dtrace_pattr_t lockstat_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
};
static dtrace_pops_t lockstat_pops = {
lockstat_provide,
NULL,
lockstat_enable,
lockstat_disable,
NULL,
NULL,
NULL,
NULL,
NULL,
lockstat_destroy
};
static void
lockstat_load(void *dummy)
{
/* Create the /dev/dtrace/lockstat entry. */
lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/lockstat");
if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
NULL, &lockstat_pops, NULL, &lockstat_id) != 0)
return;
}
static int
lockstat_unload()
{
int error = 0;
if ((error = dtrace_unregister(lockstat_id)) != 0)
return (error);
destroy_dev(lockstat_cdev);
return (error);
}
/* ARGSUSED */
static int
lockstat_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
SYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL);
SYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL);
DEV_MODULE(lockstat, lockstat_modevent, NULL);
MODULE_VERSION(lockstat, 1);
MODULE_DEPEND(lockstat, dtrace, 1, 1, 1);
MODULE_DEPEND(lockstat, opensolaris, 1, 1, 1);