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