1 /* $NetBSD: lockstat.c,v 1.12 2022/02/27 14:16:32 riastradh 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.12 2022/02/27 14:16:32 riastradh Exp $");
30
31 #include <sys/types.h>
32 #include <sys/param.h>
33
34 #include <sys/atomic.h>
35 #include <sys/conf.h>
36 #include <sys/dtrace.h>
37 #include <sys/module.h>
38 #include <sys/mutex.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/xcall.h>
42
43 #define NLOCKSTAT 1
44 #include <dev/lockstat.h>
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
lockstat_enable(void * arg,dtrace_id_t id,void * parg)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 if (lockstat_dtrace_count++ == 0) {
77 LOCKSTAT_ENABLED_UPDATE_BEGIN();
78 lockstat_dtrace_enabled = LB_DTRACE;
79 LOCKSTAT_ENABLED_UPDATE_END();
80 }
81 atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)],
82 id);
83
84 return 0;
85 }
86
87 /*ARGSUSED*/
88 static void
lockstat_disable(void * arg,dtrace_id_t id __unused,void * parg)89 lockstat_disable(void *arg, dtrace_id_t id __unused, void *parg)
90 {
91 lockstat_probe_t *probe = parg;
92
93 ASSERT(lockstat_probemap[LS_COMPRESS(probe->lsp_probe)]);
94
95 atomic_store_relaxed(&lockstat_probemap[LS_COMPRESS(probe->lsp_probe)],
96 0);
97 if (--lockstat_dtrace_count == 0) {
98 LOCKSTAT_ENABLED_UPDATE_BEGIN();
99 lockstat_dtrace_enabled = 0;
100 LOCKSTAT_ENABLED_UPDATE_END();
101
102 /*
103 * Wait for all lockstat dtrace probe on all CPUs to
104 * finish, now that they've been disabled.
105 */
106 xc_barrier(0);
107 }
108 }
109
110 /*ARGSUSED*/
111 static void
lockstat_provide(void * arg,dtrace_probedesc_t * desc)112 lockstat_provide(void *arg, dtrace_probedesc_t *desc)
113 {
114 int i = 0;
115
116 for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
117 lockstat_probe_t *probe = &lockstat_probes[i];
118
119 if (dtrace_probe_lookup(lockstat_id, __UNCONST("kernel"),
120 __UNCONST(probe->lsp_func), __UNCONST(probe->lsp_name))
121 != 0)
122 continue;
123
124 ASSERT(!probe->lsp_id);
125 probe->lsp_id = dtrace_probe_create(lockstat_id,
126 __UNCONST("kernel"), __UNCONST(probe->lsp_func),
127 __UNCONST(probe->lsp_name), 1, probe);
128 }
129 }
130
131 /*ARGSUSED*/
132 static void
lockstat_destroy(void * arg,dtrace_id_t id,void * parg)133 lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
134 {
135 lockstat_probe_t *probe = parg;
136
137 ASSERT(!lockstat_probemap[probe->lsp_probe]);
138 probe->lsp_id = 0;
139 }
140
141 static dtrace_pattr_t lockstat_attr = {
142 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
143 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
144 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
145 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
146 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
147 };
148
149 static dtrace_pops_t lockstat_pops = {
150 lockstat_provide,
151 NULL,
152 lockstat_enable,
153 lockstat_disable,
154 NULL,
155 NULL,
156 NULL,
157 NULL,
158 NULL,
159 lockstat_destroy
160 };
161
162 typedef void (*dtrace_probe_func_t)(dtrace_id_t, uintptr_t, uintptr_t,
163 uintptr_t, uintptr_t, uintptr_t);
164
165 static bool
lockstat_cas_probe(dtrace_probe_func_t old,dtrace_probe_func_t new)166 lockstat_cas_probe(dtrace_probe_func_t old, dtrace_probe_func_t new)
167 {
168
169 ASSERT(kernconfig_is_held());
170
171 if (lockstat_probe_func != old)
172 return false;
173
174 lockstat_probe_func = new;
175
176 /*
177 * Make sure that the probe function is initialized on all CPUs
178 * before we enable the lockstat probe by setting
179 * lockstat_probemap[...].
180 */
181 xc_barrier(0);
182
183 return true;
184 }
185
186 static int
lockstat_init(void)187 lockstat_init(void)
188 {
189 int error;
190 bool ok;
191
192 /* Install the probe function. */
193 ok = lockstat_cas_probe(lockstat_probe_stub, dtrace_probe);
194 if (!ok) {
195 printf("dtrace_lockstat: lockstat probe already installed\n");
196 error = EEXIST;
197 goto fail0;
198 }
199
200 /* Everything is in place. Register a dtrace provider. */
201 ASSERT(lockstat_id == 0);
202 error = dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
203 NULL, &lockstat_pops, NULL, &lockstat_id);
204 if (error) {
205 printf("dtrace_lockstat: failed to register dtrace provider"
206 ": %d\n", error);
207 goto fail1;
208 }
209 /* Success! */
210 return 0;
211
212 fail1: ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub);
213 ASSERT(ok);
214 fail0: ASSERT(error);
215 return error;
216 }
217
218 static int
lockstat_fini(void)219 lockstat_fini(void)
220 {
221 int error;
222 bool ok __debugused;
223
224 error = dtrace_unregister(lockstat_id);
225 if (error) {
226 printf("dtrace_lockstat: failed to unregister dtrace provider"
227 ": %d\n",
228 error);
229 return error;
230 }
231
232 /* Unhook the probe. */
233 ok = lockstat_cas_probe(dtrace_probe, lockstat_probe_stub);
234 ASSERT(ok);
235
236 /* Success! */
237 return 0;
238 }
239
240 static int
dtrace_lockstat_modcmd(modcmd_t cmd,void * data)241 dtrace_lockstat_modcmd(modcmd_t cmd, void *data)
242 {
243
244 switch (cmd) {
245 case MODULE_CMD_INIT:
246 return lockstat_init();
247 case MODULE_CMD_FINI:
248 return lockstat_fini();
249 default:
250 return ENOTTY;
251 }
252 }
253
254 MODULE(MODULE_CLASS_MISC, dtrace_lockstat, "dtrace");
255