1192853Ssson/* 2192853Ssson * CDDL HEADER START 3192853Ssson * 4192853Ssson * The contents of this file are subject to the terms of the 5192853Ssson * Common Development and Distribution License (the "License"). 6192853Ssson * You may not use this file except in compliance with the License. 7192853Ssson * 8192853Ssson * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9192853Ssson * or http://www.opensolaris.org/os/licensing. 10192853Ssson * See the License for the specific language governing permissions 11192853Ssson * and limitations under the License. 12192853Ssson * 13192853Ssson * When distributing Covered Code, include this CDDL HEADER in each 14192853Ssson * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15192853Ssson * If applicable, add the following below this CDDL HEADER, with the 16192853Ssson * fields enclosed by brackets "[]" replaced with your own identifying 17192853Ssson * information: Portions Copyright [yyyy] [name of copyright owner] 18192853Ssson * 19192853Ssson * CDDL HEADER END 20192853Ssson * 21192853Ssson * Portions Copyright (c) 2008-2009 Stacey Son <sson@FreeBSD.org> 22192853Ssson * 23192853Ssson * $FreeBSD: releng/10.3/sys/cddl/dev/lockstat/lockstat.c 285759 2015-07-21 17:16:37Z markj $ 24192853Ssson * 25192853Ssson */ 26192853Ssson 27192853Ssson/* 28192853Ssson * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 29192853Ssson * Use is subject to license terms. 30192853Ssson */ 31192853Ssson 32192853Ssson#include "opt_kdtrace.h" 33192853Ssson 34192853Ssson#include <sys/cdefs.h> 35192853Ssson#include <sys/param.h> 36192853Ssson#include <sys/systm.h> 37192853Ssson#include <sys/conf.h> 38192853Ssson#include <sys/kernel.h> 39192853Ssson#include <sys/limits.h> 40192853Ssson#include <sys/lock.h> 41192853Ssson#include <sys/linker.h> 42192853Ssson#include <sys/module.h> 43192853Ssson#include <sys/mutex.h> 44192853Ssson 45192853Ssson#include <sys/dtrace.h> 46192853Ssson#include <sys/lockstat.h> 47192853Ssson 48242723Sjhibbits#if defined(__i386__) || defined(__amd64__) || \ 49242723Sjhibbits defined(__mips__) || defined(__powerpc__) 50192853Ssson#define LOCKSTAT_AFRAMES 1 51192853Ssson#else 52192853Ssson#error "architecture not supported" 53192853Ssson#endif 54192853Ssson 55192853Sssonstatic d_open_t lockstat_open; 56192853Sssonstatic void lockstat_provide(void *, dtrace_probedesc_t *); 57192853Sssonstatic void lockstat_destroy(void *, dtrace_id_t, void *); 58192853Sssonstatic void lockstat_enable(void *, dtrace_id_t, void *); 59192853Sssonstatic void lockstat_disable(void *, dtrace_id_t, void *); 60192853Sssonstatic void lockstat_load(void *); 61192853Sssonstatic int lockstat_unload(void); 62192853Ssson 63192853Ssson 64192853Sssontypedef struct lockstat_probe { 65192853Ssson char *lsp_func; 66192853Ssson char *lsp_name; 67192853Ssson int lsp_probe; 68192853Ssson dtrace_id_t lsp_id; 69192853Ssson#ifdef __FreeBSD__ 70192853Ssson int lsp_frame; 71192853Ssson#endif 72192853Ssson} lockstat_probe_t; 73192853Ssson 74192853Ssson#ifdef __FreeBSD__ 75192853Sssonlockstat_probe_t lockstat_probes[] = 76192853Ssson{ 77192853Ssson /* Spin Locks */ 78192853Ssson { LS_MTX_SPIN_LOCK, LSS_ACQUIRE, LS_MTX_SPIN_LOCK_ACQUIRE, 79192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 80192853Ssson { LS_MTX_SPIN_LOCK, LSS_SPIN, LS_MTX_SPIN_LOCK_SPIN, 81192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 82192853Ssson { LS_MTX_SPIN_UNLOCK, LSS_RELEASE, LS_MTX_SPIN_UNLOCK_RELEASE, 83192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 84192853Ssson /* Adaptive Locks */ 85192853Ssson { LS_MTX_LOCK, LSA_ACQUIRE, LS_MTX_LOCK_ACQUIRE, 86192853Ssson DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 87192853Ssson { LS_MTX_LOCK, LSA_BLOCK, LS_MTX_LOCK_BLOCK, 88192853Ssson DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 89192853Ssson { LS_MTX_LOCK, LSA_SPIN, LS_MTX_LOCK_SPIN, 90192853Ssson DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 91192853Ssson { LS_MTX_UNLOCK, LSA_RELEASE, LS_MTX_UNLOCK_RELEASE, 92192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 93192853Ssson { LS_MTX_TRYLOCK, LSA_ACQUIRE, LS_MTX_TRYLOCK_ACQUIRE, 94192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 95192853Ssson /* Reader/Writer Locks */ 96192853Ssson { LS_RW_RLOCK, LSR_ACQUIRE, LS_RW_RLOCK_ACQUIRE, 97192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 98192853Ssson { LS_RW_RLOCK, LSR_BLOCK, LS_RW_RLOCK_BLOCK, 99192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 100192853Ssson { LS_RW_RLOCK, LSR_SPIN, LS_RW_RLOCK_SPIN, 101192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 102192853Ssson { LS_RW_RUNLOCK, LSR_RELEASE, LS_RW_RUNLOCK_RELEASE, 103192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 104192853Ssson { LS_RW_WLOCK, LSR_ACQUIRE, LS_RW_WLOCK_ACQUIRE, 105192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 106192853Ssson { LS_RW_WLOCK, LSR_BLOCK, LS_RW_WLOCK_BLOCK, 107192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 108192853Ssson { LS_RW_WLOCK, LSR_SPIN, LS_RW_WLOCK_SPIN, 109192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 110192853Ssson { LS_RW_WUNLOCK, LSR_RELEASE, LS_RW_WUNLOCK_RELEASE, 111192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 112192853Ssson { LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE, 113192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 114192853Ssson { LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE, 115192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 116192853Ssson /* Shared/Exclusive Locks */ 117192853Ssson { LS_SX_SLOCK, LSX_ACQUIRE, LS_SX_SLOCK_ACQUIRE, 118192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 119192853Ssson { LS_SX_SLOCK, LSX_BLOCK, LS_SX_SLOCK_BLOCK, 120192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 121192853Ssson { LS_SX_SLOCK, LSX_SPIN, LS_SX_SLOCK_SPIN, 122192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 123192853Ssson { LS_SX_SUNLOCK, LSX_RELEASE, LS_SX_SUNLOCK_RELEASE, 124192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 125192853Ssson { LS_SX_XLOCK, LSX_ACQUIRE, LS_SX_XLOCK_ACQUIRE, 126192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 127192853Ssson { LS_SX_XLOCK, LSX_BLOCK, LS_SX_XLOCK_BLOCK, 128192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 129192853Ssson { LS_SX_XLOCK, LSX_SPIN, LS_SX_XLOCK_SPIN, 130192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 131192853Ssson { LS_SX_XUNLOCK, LSX_RELEASE, LS_SX_XUNLOCK_RELEASE, 132192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 133192853Ssson { LS_SX_TRYUPGRADE, LSX_UPGRADE, LS_SX_TRYUPGRADE_UPGRADE, 134192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 135192853Ssson { LS_SX_DOWNGRADE, LSX_DOWNGRADE, LS_SX_DOWNGRADE_DOWNGRADE, 136192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 137192853Ssson /* Thread Locks */ 138192853Ssson { LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN, 139192853Ssson DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 140192853Ssson { NULL } 141192853Ssson}; 142192853Ssson#else 143192853Ssson#error "OS not supported" 144192853Ssson#endif 145192853Ssson 146192853Ssson 147192853Sssonstatic struct cdevsw lockstat_cdevsw = { 148192853Ssson .d_version = D_VERSION, 149192853Ssson .d_open = lockstat_open, 150192853Ssson .d_name = "lockstat", 151192853Ssson}; 152192853Ssson 153192853Sssonstatic struct cdev *lockstat_cdev; 154192853Sssonstatic dtrace_provider_id_t lockstat_id; 155192853Ssson 156192853Ssson/*ARGSUSED*/ 157192853Sssonstatic void 158192853Sssonlockstat_enable(void *arg, dtrace_id_t id, void *parg) 159192853Ssson{ 160192853Ssson lockstat_probe_t *probe = parg; 161192853Ssson 162192853Ssson ASSERT(!lockstat_probemap[probe->lsp_probe]); 163192853Ssson 164285759Smarkj lockstat_enabled++; 165285759Smarkj 166192853Ssson lockstat_probemap[probe->lsp_probe] = id; 167192853Ssson#ifdef DOODAD 168192853Ssson membar_producer(); 169192853Ssson#endif 170192853Ssson 171192853Ssson lockstat_probe_func = dtrace_probe; 172192853Ssson#ifdef DOODAD 173192853Ssson membar_producer(); 174192853Ssson 175192853Ssson lockstat_hot_patch(); 176192853Ssson membar_producer(); 177192853Ssson#endif 178192853Ssson} 179192853Ssson 180192853Ssson/*ARGSUSED*/ 181192853Sssonstatic void 182192853Sssonlockstat_disable(void *arg, dtrace_id_t id, void *parg) 183192853Ssson{ 184192853Ssson lockstat_probe_t *probe = parg; 185192853Ssson int i; 186192853Ssson 187192853Ssson ASSERT(lockstat_probemap[probe->lsp_probe]); 188192853Ssson 189285759Smarkj lockstat_enabled--; 190285759Smarkj 191192853Ssson lockstat_probemap[probe->lsp_probe] = 0; 192192853Ssson#ifdef DOODAD 193192853Ssson lockstat_hot_patch(); 194192853Ssson membar_producer(); 195192853Ssson#endif 196192853Ssson 197192853Ssson /* 198192853Ssson * See if we have any probes left enabled. 199192853Ssson */ 200192853Ssson for (i = 0; i < LS_NPROBES; i++) { 201192853Ssson if (lockstat_probemap[i]) { 202192853Ssson /* 203192853Ssson * This probe is still enabled. We don't need to deal 204192853Ssson * with waiting for all threads to be out of the 205192853Ssson * lockstat critical sections; just return. 206192853Ssson */ 207192853Ssson return; 208192853Ssson } 209192853Ssson } 210192853Ssson 211192853Ssson} 212192853Ssson 213192853Ssson/*ARGSUSED*/ 214192853Sssonstatic int 215192853Sssonlockstat_open(struct cdev *dev __unused, int oflags __unused, 216192853Ssson int devtype __unused, struct thread *td __unused) 217192853Ssson{ 218192853Ssson return (0); 219192853Ssson} 220192853Ssson 221192853Ssson/*ARGSUSED*/ 222192853Sssonstatic void 223192853Sssonlockstat_provide(void *arg, dtrace_probedesc_t *desc) 224192853Ssson{ 225192853Ssson int i = 0; 226192853Ssson 227192853Ssson for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { 228192853Ssson lockstat_probe_t *probe = &lockstat_probes[i]; 229192853Ssson 230192853Ssson if (dtrace_probe_lookup(lockstat_id, "kernel", 231192853Ssson probe->lsp_func, probe->lsp_name) != 0) 232192853Ssson continue; 233192853Ssson 234192853Ssson ASSERT(!probe->lsp_id); 235192853Ssson#ifdef __FreeBSD__ 236192853Ssson probe->lsp_id = dtrace_probe_create(lockstat_id, 237192853Ssson "kernel", probe->lsp_func, probe->lsp_name, 238192853Ssson probe->lsp_frame, probe); 239192853Ssson#else 240192853Ssson probe->lsp_id = dtrace_probe_create(lockstat_id, 241192853Ssson "kernel", probe->lsp_func, probe->lsp_name, 242192853Ssson LOCKSTAT_AFRAMES, probe); 243192853Ssson#endif 244192853Ssson } 245192853Ssson} 246192853Ssson 247192853Ssson/*ARGSUSED*/ 248192853Sssonstatic void 249192853Sssonlockstat_destroy(void *arg, dtrace_id_t id, void *parg) 250192853Ssson{ 251192853Ssson lockstat_probe_t *probe = parg; 252192853Ssson 253192853Ssson ASSERT(!lockstat_probemap[probe->lsp_probe]); 254192853Ssson probe->lsp_id = 0; 255192853Ssson} 256192853Ssson 257192853Sssonstatic dtrace_pattr_t lockstat_attr = { 258192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 259192853Ssson{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 260192853Ssson{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 261192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 262192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 263192853Ssson}; 264192853Ssson 265192853Sssonstatic dtrace_pops_t lockstat_pops = { 266192853Ssson lockstat_provide, 267192853Ssson NULL, 268192853Ssson lockstat_enable, 269192853Ssson lockstat_disable, 270192853Ssson NULL, 271192853Ssson NULL, 272192853Ssson NULL, 273192853Ssson NULL, 274192853Ssson NULL, 275192853Ssson lockstat_destroy 276192853Ssson}; 277192853Ssson 278192853Sssonstatic void 279192853Sssonlockstat_load(void *dummy) 280192853Ssson{ 281192853Ssson /* Create the /dev/dtrace/lockstat entry. */ 282192853Ssson lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 283192853Ssson "dtrace/lockstat"); 284192853Ssson 285192853Ssson if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER, 286192853Ssson NULL, &lockstat_pops, NULL, &lockstat_id) != 0) 287192853Ssson return; 288192853Ssson} 289192853Ssson 290192853Sssonstatic int 291192853Sssonlockstat_unload() 292192853Ssson{ 293192853Ssson int error = 0; 294192853Ssson 295192853Ssson if ((error = dtrace_unregister(lockstat_id)) != 0) 296192853Ssson return (error); 297192853Ssson 298192853Ssson destroy_dev(lockstat_cdev); 299192853Ssson 300192853Ssson return (error); 301192853Ssson} 302192853Ssson 303192853Ssson/* ARGSUSED */ 304192853Sssonstatic int 305192853Sssonlockstat_modevent(module_t mod __unused, int type, void *data __unused) 306192853Ssson{ 307192853Ssson int error = 0; 308192853Ssson 309192853Ssson switch (type) { 310192853Ssson case MOD_LOAD: 311192853Ssson break; 312192853Ssson 313192853Ssson case MOD_UNLOAD: 314192853Ssson break; 315192853Ssson 316192853Ssson case MOD_SHUTDOWN: 317192853Ssson break; 318192853Ssson 319192853Ssson default: 320192853Ssson error = EOPNOTSUPP; 321192853Ssson break; 322192853Ssson } 323192853Ssson return (error); 324192853Ssson} 325192853Ssson 326192853SssonSYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL); 327192853SssonSYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL); 328192853Ssson 329192853SssonDEV_MODULE(lockstat, lockstat_modevent, NULL); 330192853SssonMODULE_VERSION(lockstat, 1); 331192853SssonMODULE_DEPEND(lockstat, dtrace, 1, 1, 1); 332192853SssonMODULE_DEPEND(lockstat, opensolaris, 1, 1, 1); 333