1 /* $NetBSD: lockstat.c,v 1.3 2013/06/21 19:16:00 christos Exp $ */ 2 3 /* 4 * CDDL HEADER START 5 * 6 * The contents of this file are subject to the terms of the 7 * Common Development and Distribution License (the "License"). 8 * You may not use this file except in compliance with the License. 9 * 10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11 * or http://www.opensolaris.org/os/licensing. 12 * See the License for the specific language governing permissions 13 * and limitations under the License. 14 * 15 * When distributing Covered Code, include this CDDL HEADER in each 16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17 * If applicable, add the following below this CDDL HEADER, with the 18 * fields enclosed by brackets "[]" replaced with your own identifying 19 * information: Portions Copyright [yyyy] [name of copyright owner] 20 * 21 * CDDL HEADER END 22 * 23 * Portions Copyright (c) 2008-2009 Stacey Son <sson@FreeBSD.org> 24 * 25 * $FreeBSD: src/sys/cddl/dev/lockstat/lockstat.c,v 1.2.2.1 2009/08/03 08:13:06 kensmith Exp $ 26 * 27 */ 28 29 /* 30 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 31 * Use is subject to license terms. 32 */ 33 34 #include "opt_kdtrace.h" 35 36 #include <sys/cdefs.h> 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/conf.h> 40 #include <sys/kernel.h> 41 #include <sys/limits.h> 42 #include <sys/lock.h> 43 #include <sys/linker.h> 44 #include <sys/module.h> 45 #include <sys/mutex.h> 46 47 #include <sys/dtrace.h> 48 #include <sys/lockstat.h> 49 50 #if defined(__i386__) || defined(__amd64__) || defined(__arm__) 51 #define LOCKSTAT_AFRAMES 1 52 #else 53 #error "architecture not supported" 54 #endif 55 56 static d_open_t lockstat_open; 57 static void lockstat_provide(void *, dtrace_probedesc_t *); 58 static void lockstat_destroy(void *, dtrace_id_t, void *); 59 static void lockstat_enable(void *, dtrace_id_t, void *); 60 static void lockstat_disable(void *, dtrace_id_t, void *); 61 static void lockstat_load(void *); 62 static int lockstat_unload(void); 63 64 65 typedef struct lockstat_probe { 66 char *lsp_func; 67 char *lsp_name; 68 int lsp_probe; 69 dtrace_id_t lsp_id; 70 #ifdef __FreeBSD__ 71 int lsp_frame; 72 #endif 73 } lockstat_probe_t; 74 75 #ifdef __FreeBSD__ 76 lockstat_probe_t lockstat_probes[] = 77 { 78 /* Spin Locks */ 79 { LS_MTX_SPIN_LOCK, LSS_ACQUIRE, LS_MTX_SPIN_LOCK_ACQUIRE, 80 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 81 { LS_MTX_SPIN_LOCK, LSS_SPIN, LS_MTX_SPIN_LOCK_SPIN, 82 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 83 { LS_MTX_SPIN_UNLOCK, LSS_RELEASE, LS_MTX_SPIN_UNLOCK_RELEASE, 84 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 85 /* Adaptive Locks */ 86 { LS_MTX_LOCK, LSA_ACQUIRE, LS_MTX_LOCK_ACQUIRE, 87 DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 88 { LS_MTX_LOCK, LSA_BLOCK, LS_MTX_LOCK_BLOCK, 89 DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 90 { LS_MTX_LOCK, LSA_SPIN, LS_MTX_LOCK_SPIN, 91 DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) }, 92 { LS_MTX_UNLOCK, LSA_RELEASE, LS_MTX_UNLOCK_RELEASE, 93 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 94 { LS_MTX_TRYLOCK, LSA_ACQUIRE, LS_MTX_TRYLOCK_ACQUIRE, 95 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 96 /* Reader/Writer Locks */ 97 { LS_RW_RLOCK, LSR_ACQUIRE, LS_RW_RLOCK_ACQUIRE, 98 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 99 { LS_RW_RLOCK, LSR_BLOCK, LS_RW_RLOCK_BLOCK, 100 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 101 { LS_RW_RLOCK, LSR_SPIN, LS_RW_RLOCK_SPIN, 102 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 103 { LS_RW_RUNLOCK, LSR_RELEASE, LS_RW_RUNLOCK_RELEASE, 104 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 105 { LS_RW_WLOCK, LSR_ACQUIRE, LS_RW_WLOCK_ACQUIRE, 106 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 107 { LS_RW_WLOCK, LSR_BLOCK, LS_RW_WLOCK_BLOCK, 108 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 109 { LS_RW_WLOCK, LSR_SPIN, LS_RW_WLOCK_SPIN, 110 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 111 { LS_RW_WUNLOCK, LSR_RELEASE, LS_RW_WUNLOCK_RELEASE, 112 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 113 { LS_RW_TRYUPGRADE, LSR_UPGRADE, LS_RW_TRYUPGRADE_UPGRADE, 114 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 115 { LS_RW_DOWNGRADE, LSR_DOWNGRADE, LS_RW_DOWNGRADE_DOWNGRADE, 116 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 117 /* Shared/Exclusive Locks */ 118 { LS_SX_SLOCK, LSX_ACQUIRE, LS_SX_SLOCK_ACQUIRE, 119 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 120 { LS_SX_SLOCK, LSX_BLOCK, LS_SX_SLOCK_BLOCK, 121 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 122 { LS_SX_SLOCK, LSX_SPIN, LS_SX_SLOCK_SPIN, 123 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 124 { LS_SX_SUNLOCK, LSX_RELEASE, LS_SX_SUNLOCK_RELEASE, 125 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 126 { LS_SX_XLOCK, LSX_ACQUIRE, LS_SX_XLOCK_ACQUIRE, 127 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 128 { LS_SX_XLOCK, LSX_BLOCK, LS_SX_XLOCK_BLOCK, 129 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 130 { LS_SX_XLOCK, LSX_SPIN, LS_SX_XLOCK_SPIN, 131 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 132 { LS_SX_XUNLOCK, LSX_RELEASE, LS_SX_XUNLOCK_RELEASE, 133 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 134 { LS_SX_TRYUPGRADE, LSX_UPGRADE, LS_SX_TRYUPGRADE_UPGRADE, 135 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 136 { LS_SX_DOWNGRADE, LSX_DOWNGRADE, LS_SX_DOWNGRADE_DOWNGRADE, 137 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 138 /* Thread Locks */ 139 { LS_THREAD_LOCK, LST_SPIN, LS_THREAD_LOCK_SPIN, 140 DTRACE_IDNONE, LOCKSTAT_AFRAMES }, 141 { NULL } 142 }; 143 #else 144 #error "OS not supported" 145 #endif 146 147 148 static struct cdevsw lockstat_cdevsw = { 149 .d_version = D_VERSION, 150 .d_open = lockstat_open, 151 .d_name = "lockstat", 152 }; 153 154 static struct cdev *lockstat_cdev; 155 static dtrace_provider_id_t lockstat_id; 156 157 /*ARGSUSED*/ 158 static void 159 lockstat_enable(void *arg, dtrace_id_t id, void *parg) 160 { 161 lockstat_probe_t *probe = parg; 162 163 ASSERT(!lockstat_probemap[probe->lsp_probe]); 164 165 lockstat_probemap[probe->lsp_probe] = id; 166 #ifdef DOODAD 167 membar_producer(); 168 #endif 169 170 lockstat_probe_func = dtrace_probe; 171 #ifdef DOODAD 172 membar_producer(); 173 174 lockstat_hot_patch(); 175 membar_producer(); 176 #endif 177 } 178 179 /*ARGSUSED*/ 180 static void 181 lockstat_disable(void *arg, dtrace_id_t id, void *parg) 182 { 183 lockstat_probe_t *probe = parg; 184 int i; 185 186 ASSERT(lockstat_probemap[probe->lsp_probe]); 187 188 lockstat_probemap[probe->lsp_probe] = 0; 189 #ifdef DOODAD 190 lockstat_hot_patch(); 191 membar_producer(); 192 #endif 193 194 /* 195 * See if we have any probes left enabled. 196 */ 197 for (i = 0; i < LS_NPROBES; i++) { 198 if (lockstat_probemap[i]) { 199 /* 200 * This probe is still enabled. We don't need to deal 201 * with waiting for all threads to be out of the 202 * lockstat critical sections; just return. 203 */ 204 return; 205 } 206 } 207 208 } 209 210 /*ARGSUSED*/ 211 static int 212 lockstat_open(struct cdev *dev __unused, int oflags __unused, 213 int devtype __unused, struct thread *td __unused) 214 { 215 return (0); 216 } 217 218 /*ARGSUSED*/ 219 static void 220 lockstat_provide(void *arg, dtrace_probedesc_t *desc) 221 { 222 int i = 0; 223 224 for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { 225 lockstat_probe_t *probe = &lockstat_probes[i]; 226 227 if (dtrace_probe_lookup(lockstat_id, "kernel", 228 probe->lsp_func, probe->lsp_name) != 0) 229 continue; 230 231 ASSERT(!probe->lsp_id); 232 #ifdef __FreeBSD__ 233 probe->lsp_id = dtrace_probe_create(lockstat_id, 234 "kernel", probe->lsp_func, probe->lsp_name, 235 probe->lsp_frame, probe); 236 #else 237 probe->lsp_id = dtrace_probe_create(lockstat_id, 238 "kernel", probe->lsp_func, probe->lsp_name, 239 LOCKSTAT_AFRAMES, probe); 240 #endif 241 } 242 } 243 244 /*ARGSUSED*/ 245 static void 246 lockstat_destroy(void *arg, dtrace_id_t id, void *parg) 247 { 248 lockstat_probe_t *probe = parg; 249 250 ASSERT(!lockstat_probemap[probe->lsp_probe]); 251 probe->lsp_id = 0; 252 } 253 254 static dtrace_pattr_t lockstat_attr = { 255 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 256 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 257 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 258 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 259 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 260 }; 261 262 static dtrace_pops_t lockstat_pops = { 263 lockstat_provide, 264 NULL, 265 lockstat_enable, 266 lockstat_disable, 267 NULL, 268 NULL, 269 NULL, 270 NULL, 271 NULL, 272 lockstat_destroy 273 }; 274 275 static void 276 lockstat_load(void *dummy) 277 { 278 /* Create the /dev/dtrace/lockstat entry. */ 279 lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 280 "dtrace/lockstat"); 281 282 if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER, 283 NULL, &lockstat_pops, NULL, &lockstat_id) != 0) 284 return; 285 } 286 287 static int 288 lockstat_unload() 289 { 290 int error = 0; 291 292 if ((error = dtrace_unregister(lockstat_id)) != 0) 293 return (error); 294 295 destroy_dev(lockstat_cdev); 296 297 return (error); 298 } 299 300 /* ARGSUSED */ 301 static int 302 lockstat_modevent(module_t mod __unused, int type, void *data __unused) 303 { 304 int error = 0; 305 306 switch (type) { 307 case MOD_LOAD: 308 break; 309 310 case MOD_UNLOAD: 311 break; 312 313 case MOD_SHUTDOWN: 314 break; 315 316 default: 317 error = EOPNOTSUPP; 318 break; 319 } 320 return (error); 321 } 322 323 SYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL); 324 SYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL); 325 326 DEV_MODULE(lockstat, lockstat_modevent, NULL); 327 MODULE_VERSION(lockstat, 1); 328 MODULE_DEPEND(lockstat, dtrace, 1, 1, 1); 329 MODULE_DEPEND(lockstat, opensolaris, 1, 1, 1); 330