xref: /netbsd-src/external/cddl/osnet/dev/lockstat/lockstat.c (revision e468962b6ac8c16ce134c9918c0cb7ca121021a9)
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