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