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