1 /* $NetBSD: lockstat.c,v 1.8 2015/03/09 01:42:26 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 /* 24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: lockstat.c,v 1.8 2015/03/09 01:42:26 christos Exp $"); 30 31 #include <sys/types.h> 32 #include <sys/param.h> 33 #include <sys/conf.h> 34 #include <sys/dtrace.h> 35 #include <sys/module.h> 36 #include <sys/mutex.h> 37 #include <sys/systm.h> 38 #include <sys/xcall.h> 39 #include <sys/atomic.h> 40 41 #define NLOCKSTAT 1 42 #include <dev/lockstat.h> 43 44 #define ASSERT KASSERT 45 46 typedef struct lockstat_probe { 47 const char *lsp_func; 48 const char *lsp_name; 49 int lsp_probe; 50 dtrace_id_t lsp_id; 51 } lockstat_probe_t; 52 53 lockstat_probe_t lockstat_probes[] = { 54 { "mutex_spin", "spin", LB_SPIN_MUTEX | LB_SPIN, 0 }, 55 { "mutex_adaptive", "sleep", LB_ADAPTIVE_MUTEX | LB_SLEEP1, 0 }, 56 { "mutex_adaptive", "spin", LB_ADAPTIVE_MUTEX | LB_SPIN, 0 }, 57 { "rwlock", "sleep_writer", LB_RWLOCK | LB_SLEEP1, 0 }, 58 { "rwlock", "sleep_reader", LB_RWLOCK | LB_SLEEP2, 0 }, 59 { "rwlock", "spin", LB_RWLOCK | LB_SPIN, 0 }, 60 { "kernel", "spin", LB_KERNEL_LOCK | LB_SPIN, 0 }, 61 { "lwp", "spin", LB_NOPREEMPT | LB_SPIN, 0 }, 62 { NULL, NULL, 0, 0 }, 63 }; 64 65 static dtrace_provider_id_t lockstat_id; 66 static size_t lockstat_dtrace_count; 67 68 /*ARGSUSED*/ 69 static int 70 lockstat_enable(void *arg, dtrace_id_t id, void *parg) 71 { 72 lockstat_probe_t *probe = parg; 73 74 ASSERT(!lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); 75 76 lockstat_probemap[LS_COMPRESS(probe->lsp_probe)] = id; 77 if (lockstat_dtrace_count++ == 0) { 78 lockstat_dtrace_enabled = LB_DTRACE; 79 LOCKSTAT_ENABLED_UPDATE(); 80 } 81 82 return 0; 83 } 84 85 /*ARGSUSED*/ 86 static void 87 lockstat_disable(void *arg, dtrace_id_t id __unused, void *parg) 88 { 89 lockstat_probe_t *probe = parg; 90 91 ASSERT(lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]); 92 93 if (--lockstat_dtrace_count == 0) { 94 lockstat_dtrace_enabled = 0; 95 LOCKSTAT_ENABLED_UPDATE(); 96 } 97 98 lockstat_probemap[LS_COMPRESS(probe->lsp_probe)] = 0; 99 } 100 101 /*ARGSUSED*/ 102 static void 103 lockstat_provide(void *arg, const dtrace_probedesc_t *desc) 104 { 105 int i = 0; 106 107 for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) { 108 lockstat_probe_t *probe = &lockstat_probes[i]; 109 110 if (dtrace_probe_lookup(lockstat_id, __UNCONST("kernel"), 111 __UNCONST(probe->lsp_func), __UNCONST(probe->lsp_name)) 112 != 0) 113 continue; 114 115 ASSERT(!probe->lsp_id); 116 probe->lsp_id = dtrace_probe_create(lockstat_id, 117 __UNCONST("kernel"), __UNCONST(probe->lsp_func), 118 __UNCONST(probe->lsp_name), 1, probe); 119 } 120 } 121 122 /*ARGSUSED*/ 123 static void 124 lockstat_destroy(void *arg, dtrace_id_t id, void *parg) 125 { 126 lockstat_probe_t *probe = parg; 127 128 ASSERT(!lockstat_probemap[probe->lsp_probe]); 129 probe->lsp_id = 0; 130 } 131 132 static dtrace_pattr_t lockstat_attr = { 133 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 134 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 135 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 136 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 137 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 138 }; 139 140 static dtrace_pops_t lockstat_pops = { 141 lockstat_provide, 142 NULL, 143 lockstat_enable, 144 lockstat_disable, 145 NULL, 146 NULL, 147 NULL, 148 NULL, 149 NULL, 150 lockstat_destroy 151 }; 152 153 static void 154 lockstat_barrier_xc(void *arg0 __unused, void *arg1 __unused) 155 { 156 157 membar_consumer(); 158 } 159 160 typedef void (*dtrace_probe_func_t)(dtrace_id_t, uintptr_t, uintptr_t, 161 uintptr_t, uintptr_t, uintptr_t); 162 163 static bool 164 lockstat_cas_probe(dtrace_probe_func_t old, dtrace_probe_func_t new) 165 { 166 167 ASSERT(kernconfig_is_held()); 168 169 if (lockstat_probe_func != old) 170 return false; 171 172 lockstat_probe_func = new; 173 membar_producer(); 174 xc_wait(xc_broadcast(0, lockstat_barrier_xc, NULL, NULL)); 175 return true; 176 } 177 178 static int 179 lockstat_init(void) 180 { 181 int error; 182 bool ok; 183 184 /* Install the probe function. */ 185 ok = lockstat_cas_probe(lockstat_probe_stub, dtrace_probe); 186 if (!ok) { 187 printf("dtrace_lockstat: lockstat probe already installed\n"); 188 error = EEXIST; 189 goto fail0; 190 } 191 192 /* Everything is in place. Register a dtrace provider. */ 193 ASSERT(lockstat_id == 0); 194 error = dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER, 195 NULL, &lockstat_pops, NULL, &lockstat_id); 196 if (error) { 197 printf("dtrace_lockstat: failed to register dtrace provider" 198 ": %d\n", error); 199 goto fail1; 200 } 201 /* Success! */ 202 return 0; 203 204 fail1: ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub); 205 ASSERT(ok); 206 fail0: ASSERT(error); 207 return error; 208 } 209 210 static int 211 lockstat_fini(void) 212 { 213 int error; 214 bool ok __diagused; 215 216 error = dtrace_unregister(lockstat_id); 217 if (error) { 218 printf("dtrace_lockstat: failed to unregister dtrace provider" 219 ": %d\n", 220 error); 221 return error; 222 } 223 224 /* Unhook the probe. */ 225 ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub); 226 ASSERT(ok); 227 228 /* Success! */ 229 return 0; 230 } 231 232 static int 233 dtrace_lockstat_modcmd(modcmd_t cmd, void *data) 234 { 235 236 switch (cmd) { 237 case MODULE_CMD_INIT: 238 return lockstat_init(); 239 case MODULE_CMD_FINI: 240 return lockstat_fini(); 241 default: 242 return ENOTTY; 243 } 244 } 245 246 MODULE(MODULE_CLASS_MISC, dtrace_lockstat, "dtrace"); 247