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