xref: /netbsd-src/external/cddl/osnet/dev/lockstat/lockstat.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: lockstat.c,v 1.3 2013/06/21 19:16:00 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  * Portions Copyright (c) 2008-2009 Stacey Son <sson@FreeBSD.org>
24  *
25  * $FreeBSD: src/sys/cddl/dev/lockstat/lockstat.c,v 1.2.2.1 2009/08/03 08:13:06 kensmith Exp $
26  *
27  */
28 
29 /*
30  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
31  * Use is subject to license terms.
32  */
33 
34 #include "opt_kdtrace.h"
35 
36 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/conf.h>
40 #include <sys/kernel.h>
41 #include <sys/limits.h>
42 #include <sys/lock.h>
43 #include <sys/linker.h>
44 #include <sys/module.h>
45 #include <sys/mutex.h>
46 
47 #include <sys/dtrace.h>
48 #include <sys/lockstat.h>
49 
50 #if defined(__i386__) || defined(__amd64__) || defined(__arm__)
51 #define LOCKSTAT_AFRAMES 1
52 #else
53 #error "architecture not supported"
54 #endif
55 
56 static d_open_t lockstat_open;
57 static void     lockstat_provide(void *, dtrace_probedesc_t *);
58 static void     lockstat_destroy(void *, dtrace_id_t, void *);
59 static void     lockstat_enable(void *, dtrace_id_t, void *);
60 static void     lockstat_disable(void *, dtrace_id_t, void *);
61 static void     lockstat_load(void *);
62 static int     	lockstat_unload(void);
63 
64 
65 typedef struct lockstat_probe {
66 	char		*lsp_func;
67 	char		*lsp_name;
68 	int		lsp_probe;
69 	dtrace_id_t	lsp_id;
70 #ifdef __FreeBSD__
71 	int		lsp_frame;
72 #endif
73 } lockstat_probe_t;
74 
75 #ifdef __FreeBSD__
76 lockstat_probe_t lockstat_probes[] =
77 {
78   /* Spin Locks */
79   { LS_MTX_SPIN_LOCK,	LSS_ACQUIRE,	LS_MTX_SPIN_LOCK_ACQUIRE,
80 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
81   { LS_MTX_SPIN_LOCK, 	LSS_SPIN,	LS_MTX_SPIN_LOCK_SPIN,
82 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
83   { LS_MTX_SPIN_UNLOCK,	LSS_RELEASE,	LS_MTX_SPIN_UNLOCK_RELEASE,
84 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
85   /* Adaptive Locks */
86   { LS_MTX_LOCK,	LSA_ACQUIRE,	LS_MTX_LOCK_ACQUIRE,
87 	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
88   { LS_MTX_LOCK,	LSA_BLOCK,	LS_MTX_LOCK_BLOCK,
89 	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
90   { LS_MTX_LOCK,	LSA_SPIN,	LS_MTX_LOCK_SPIN,
91 	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
92   { LS_MTX_UNLOCK,	LSA_RELEASE,	LS_MTX_UNLOCK_RELEASE,
93 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
94   { LS_MTX_TRYLOCK,	LSA_ACQUIRE,	LS_MTX_TRYLOCK_ACQUIRE,
95 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
96   /* Reader/Writer Locks */
97   { LS_RW_RLOCK,	LSR_ACQUIRE,	LS_RW_RLOCK_ACQUIRE,
98 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
99   { LS_RW_RLOCK,	LSR_BLOCK,	LS_RW_RLOCK_BLOCK,
100 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
101   { LS_RW_RLOCK,	LSR_SPIN,	LS_RW_RLOCK_SPIN,
102 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
103   { LS_RW_RUNLOCK,	LSR_RELEASE,	LS_RW_RUNLOCK_RELEASE,
104 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
105   { LS_RW_WLOCK,	LSR_ACQUIRE,	LS_RW_WLOCK_ACQUIRE,
106 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
107   { LS_RW_WLOCK,	LSR_BLOCK,	LS_RW_WLOCK_BLOCK,
108 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
109   { LS_RW_WLOCK,	LSR_SPIN,	LS_RW_WLOCK_SPIN,
110 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
111   { LS_RW_WUNLOCK,	LSR_RELEASE,	LS_RW_WUNLOCK_RELEASE,
112 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
113   { LS_RW_TRYUPGRADE,	LSR_UPGRADE,   	LS_RW_TRYUPGRADE_UPGRADE,
114 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
115   { LS_RW_DOWNGRADE,	LSR_DOWNGRADE, 	LS_RW_DOWNGRADE_DOWNGRADE,
116 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
117   /* Shared/Exclusive Locks */
118   { LS_SX_SLOCK,	LSX_ACQUIRE,	LS_SX_SLOCK_ACQUIRE,
119 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
120   { LS_SX_SLOCK,	LSX_BLOCK,	LS_SX_SLOCK_BLOCK,
121 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
122   { LS_SX_SLOCK,	LSX_SPIN,	LS_SX_SLOCK_SPIN,
123 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
124   { LS_SX_SUNLOCK,	LSX_RELEASE,	LS_SX_SUNLOCK_RELEASE,
125 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
126   { LS_SX_XLOCK,	LSX_ACQUIRE,	LS_SX_XLOCK_ACQUIRE,
127 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
128   { LS_SX_XLOCK,	LSX_BLOCK,	LS_SX_XLOCK_BLOCK,
129 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
130   { LS_SX_XLOCK,	LSX_SPIN,	LS_SX_XLOCK_SPIN,
131 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
132   { LS_SX_XUNLOCK,	LSX_RELEASE,	LS_SX_XUNLOCK_RELEASE,
133 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
134   { LS_SX_TRYUPGRADE,	LSX_UPGRADE,	LS_SX_TRYUPGRADE_UPGRADE,
135 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
136   { LS_SX_DOWNGRADE,	LSX_DOWNGRADE,	LS_SX_DOWNGRADE_DOWNGRADE,
137 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
138   /* Thread Locks */
139   { LS_THREAD_LOCK,	LST_SPIN,	LS_THREAD_LOCK_SPIN,
140 	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
141   { NULL }
142 };
143 #else
144 #error "OS not supported"
145 #endif
146 
147 
148 static struct cdevsw lockstat_cdevsw = {
149 	.d_version	= D_VERSION,
150 	.d_open		= lockstat_open,
151 	.d_name		= "lockstat",
152 };
153 
154 static struct cdev		*lockstat_cdev;
155 static dtrace_provider_id_t 	lockstat_id;
156 
157 /*ARGSUSED*/
158 static void
159 lockstat_enable(void *arg, dtrace_id_t id, void *parg)
160 {
161 	lockstat_probe_t *probe = parg;
162 
163 	ASSERT(!lockstat_probemap[probe->lsp_probe]);
164 
165 	lockstat_probemap[probe->lsp_probe] = id;
166 #ifdef DOODAD
167 	membar_producer();
168 #endif
169 
170 	lockstat_probe_func = dtrace_probe;
171 #ifdef DOODAD
172 	membar_producer();
173 
174 	lockstat_hot_patch();
175 	membar_producer();
176 #endif
177 }
178 
179 /*ARGSUSED*/
180 static void
181 lockstat_disable(void *arg, dtrace_id_t id, void *parg)
182 {
183 	lockstat_probe_t *probe = parg;
184 	int i;
185 
186 	ASSERT(lockstat_probemap[probe->lsp_probe]);
187 
188 	lockstat_probemap[probe->lsp_probe] = 0;
189 #ifdef DOODAD
190 	lockstat_hot_patch();
191 	membar_producer();
192 #endif
193 
194 	/*
195 	 * See if we have any probes left enabled.
196 	 */
197 	for (i = 0; i < LS_NPROBES; i++) {
198 		if (lockstat_probemap[i]) {
199 			/*
200 			 * This probe is still enabled.  We don't need to deal
201 			 * with waiting for all threads to be out of the
202 			 * lockstat critical sections; just return.
203 			 */
204 			return;
205 		}
206 	}
207 
208 }
209 
210 /*ARGSUSED*/
211 static int
212 lockstat_open(struct cdev *dev __unused, int oflags __unused,
213 	      int devtype __unused, struct thread *td __unused)
214 {
215 	return (0);
216 }
217 
218 /*ARGSUSED*/
219 static void
220 lockstat_provide(void *arg, dtrace_probedesc_t *desc)
221 {
222 	int i = 0;
223 
224 	for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
225 		lockstat_probe_t *probe = &lockstat_probes[i];
226 
227 		if (dtrace_probe_lookup(lockstat_id, "kernel",
228 		    probe->lsp_func, probe->lsp_name) != 0)
229 			continue;
230 
231 		ASSERT(!probe->lsp_id);
232 #ifdef __FreeBSD__
233 		probe->lsp_id = dtrace_probe_create(lockstat_id,
234 		    "kernel", probe->lsp_func, probe->lsp_name,
235 		    probe->lsp_frame, probe);
236 #else
237 		probe->lsp_id = dtrace_probe_create(lockstat_id,
238 		    "kernel", probe->lsp_func, probe->lsp_name,
239 		    LOCKSTAT_AFRAMES, probe);
240 #endif
241 	}
242 }
243 
244 /*ARGSUSED*/
245 static void
246 lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
247 {
248 	lockstat_probe_t *probe = parg;
249 
250 	ASSERT(!lockstat_probemap[probe->lsp_probe]);
251 	probe->lsp_id = 0;
252 }
253 
254 static dtrace_pattr_t lockstat_attr = {
255 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
256 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
257 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
258 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
259 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
260 };
261 
262 static dtrace_pops_t lockstat_pops = {
263 	lockstat_provide,
264 	NULL,
265 	lockstat_enable,
266 	lockstat_disable,
267 	NULL,
268 	NULL,
269 	NULL,
270 	NULL,
271 	NULL,
272 	lockstat_destroy
273 };
274 
275 static void
276 lockstat_load(void *dummy)
277 {
278 	/* Create the /dev/dtrace/lockstat entry. */
279 	lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
280 	    "dtrace/lockstat");
281 
282 	if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
283 	    NULL, &lockstat_pops, NULL, &lockstat_id) != 0)
284 	        return;
285 }
286 
287 static int
288 lockstat_unload()
289 {
290 	int error = 0;
291 
292 	if ((error = dtrace_unregister(lockstat_id)) != 0)
293 	    return (error);
294 
295 	destroy_dev(lockstat_cdev);
296 
297 	return (error);
298 }
299 
300 /* ARGSUSED */
301 static int
302 lockstat_modevent(module_t mod __unused, int type, void *data __unused)
303 {
304 	int error = 0;
305 
306 	switch (type) {
307 	case MOD_LOAD:
308 		break;
309 
310 	case MOD_UNLOAD:
311 		break;
312 
313 	case MOD_SHUTDOWN:
314 		break;
315 
316 	default:
317 		error = EOPNOTSUPP;
318 		break;
319 	}
320 	return (error);
321 }
322 
323 SYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL);
324 SYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL);
325 
326 DEV_MODULE(lockstat, lockstat_modevent, NULL);
327 MODULE_VERSION(lockstat, 1);
328 MODULE_DEPEND(lockstat, dtrace, 1, 1, 1);
329 MODULE_DEPEND(lockstat, opensolaris, 1, 1, 1);
330