1192853Ssson/*
2192853Ssson * CDDL HEADER START
3192853Ssson *
4192853Ssson * The contents of this file are subject to the terms of the
5192853Ssson * Common Development and Distribution License (the "License").
6192853Ssson * You may not use this file except in compliance with the License.
7192853Ssson *
8192853Ssson * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9192853Ssson * or http://www.opensolaris.org/os/licensing.
10192853Ssson * See the License for the specific language governing permissions
11192853Ssson * and limitations under the License.
12192853Ssson *
13192853Ssson * When distributing Covered Code, include this CDDL HEADER in each
14192853Ssson * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15192853Ssson * If applicable, add the following below this CDDL HEADER, with the
16192853Ssson * fields enclosed by brackets "[]" replaced with your own identifying
17192853Ssson * information: Portions Copyright [yyyy] [name of copyright owner]
18192853Ssson *
19192853Ssson * CDDL HEADER END
20192853Ssson *
21192853Ssson * Portions Copyright (c) 2008-2009 Stacey Son <sson@FreeBSD.org>
22192853Ssson *
23192853Ssson * $FreeBSD: releng/10.3/sys/cddl/dev/lockstat/lockstat.c 285759 2015-07-21 17:16:37Z markj $
24192853Ssson *
25192853Ssson */
26192853Ssson
27192853Ssson/*
28192853Ssson * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
29192853Ssson * Use is subject to license terms.
30192853Ssson */
31192853Ssson
32192853Ssson#include "opt_kdtrace.h"
33192853Ssson
34192853Ssson#include <sys/cdefs.h>
35192853Ssson#include <sys/param.h>
36192853Ssson#include <sys/systm.h>
37192853Ssson#include <sys/conf.h>
38192853Ssson#include <sys/kernel.h>
39192853Ssson#include <sys/limits.h>
40192853Ssson#include <sys/lock.h>
41192853Ssson#include <sys/linker.h>
42192853Ssson#include <sys/module.h>
43192853Ssson#include <sys/mutex.h>
44192853Ssson
45192853Ssson#include <sys/dtrace.h>
46192853Ssson#include <sys/lockstat.h>
47192853Ssson
48242723Sjhibbits#if defined(__i386__) || defined(__amd64__) || \
49242723Sjhibbits	defined(__mips__) || defined(__powerpc__)
50192853Ssson#define LOCKSTAT_AFRAMES 1
51192853Ssson#else
52192853Ssson#error "architecture not supported"
53192853Ssson#endif
54192853Ssson
55192853Sssonstatic d_open_t lockstat_open;
56192853Sssonstatic void     lockstat_provide(void *, dtrace_probedesc_t *);
57192853Sssonstatic void     lockstat_destroy(void *, dtrace_id_t, void *);
58192853Sssonstatic void     lockstat_enable(void *, dtrace_id_t, void *);
59192853Sssonstatic void     lockstat_disable(void *, dtrace_id_t, void *);
60192853Sssonstatic void     lockstat_load(void *);
61192853Sssonstatic int     	lockstat_unload(void);
62192853Ssson
63192853Ssson
64192853Sssontypedef struct lockstat_probe {
65192853Ssson	char		*lsp_func;
66192853Ssson	char		*lsp_name;
67192853Ssson	int		lsp_probe;
68192853Ssson	dtrace_id_t	lsp_id;
69192853Ssson#ifdef __FreeBSD__
70192853Ssson	int		lsp_frame;
71192853Ssson#endif
72192853Ssson} lockstat_probe_t;
73192853Ssson
74192853Ssson#ifdef __FreeBSD__
75192853Sssonlockstat_probe_t lockstat_probes[] =
76192853Ssson{
77192853Ssson  /* Spin Locks */
78192853Ssson  { LS_MTX_SPIN_LOCK,	LSS_ACQUIRE,	LS_MTX_SPIN_LOCK_ACQUIRE,
79192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
80192853Ssson  { LS_MTX_SPIN_LOCK, 	LSS_SPIN,	LS_MTX_SPIN_LOCK_SPIN,
81192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
82192853Ssson  { LS_MTX_SPIN_UNLOCK,	LSS_RELEASE,	LS_MTX_SPIN_UNLOCK_RELEASE,
83192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
84192853Ssson  /* Adaptive Locks */
85192853Ssson  { LS_MTX_LOCK,	LSA_ACQUIRE,	LS_MTX_LOCK_ACQUIRE,
86192853Ssson	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
87192853Ssson  { LS_MTX_LOCK,	LSA_BLOCK,	LS_MTX_LOCK_BLOCK,
88192853Ssson	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
89192853Ssson  { LS_MTX_LOCK,	LSA_SPIN,	LS_MTX_LOCK_SPIN,
90192853Ssson	  DTRACE_IDNONE, (LOCKSTAT_AFRAMES + 1) },
91192853Ssson  { LS_MTX_UNLOCK,	LSA_RELEASE,	LS_MTX_UNLOCK_RELEASE,
92192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
93192853Ssson  { LS_MTX_TRYLOCK,	LSA_ACQUIRE,	LS_MTX_TRYLOCK_ACQUIRE,
94192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
95192853Ssson  /* Reader/Writer Locks */
96192853Ssson  { LS_RW_RLOCK,	LSR_ACQUIRE,	LS_RW_RLOCK_ACQUIRE,
97192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
98192853Ssson  { LS_RW_RLOCK,	LSR_BLOCK,	LS_RW_RLOCK_BLOCK,
99192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
100192853Ssson  { LS_RW_RLOCK,	LSR_SPIN,	LS_RW_RLOCK_SPIN,
101192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
102192853Ssson  { LS_RW_RUNLOCK,	LSR_RELEASE,	LS_RW_RUNLOCK_RELEASE,
103192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
104192853Ssson  { LS_RW_WLOCK,	LSR_ACQUIRE,	LS_RW_WLOCK_ACQUIRE,
105192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
106192853Ssson  { LS_RW_WLOCK,	LSR_BLOCK,	LS_RW_WLOCK_BLOCK,
107192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
108192853Ssson  { LS_RW_WLOCK,	LSR_SPIN,	LS_RW_WLOCK_SPIN,
109192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
110192853Ssson  { LS_RW_WUNLOCK,	LSR_RELEASE,	LS_RW_WUNLOCK_RELEASE,
111192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
112192853Ssson  { LS_RW_TRYUPGRADE,	LSR_UPGRADE,   	LS_RW_TRYUPGRADE_UPGRADE,
113192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
114192853Ssson  { LS_RW_DOWNGRADE,	LSR_DOWNGRADE, 	LS_RW_DOWNGRADE_DOWNGRADE,
115192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
116192853Ssson  /* Shared/Exclusive Locks */
117192853Ssson  { LS_SX_SLOCK,	LSX_ACQUIRE,	LS_SX_SLOCK_ACQUIRE,
118192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
119192853Ssson  { LS_SX_SLOCK,	LSX_BLOCK,	LS_SX_SLOCK_BLOCK,
120192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
121192853Ssson  { LS_SX_SLOCK,	LSX_SPIN,	LS_SX_SLOCK_SPIN,
122192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
123192853Ssson  { LS_SX_SUNLOCK,	LSX_RELEASE,	LS_SX_SUNLOCK_RELEASE,
124192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
125192853Ssson  { LS_SX_XLOCK,	LSX_ACQUIRE,	LS_SX_XLOCK_ACQUIRE,
126192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
127192853Ssson  { LS_SX_XLOCK,	LSX_BLOCK,	LS_SX_XLOCK_BLOCK,
128192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
129192853Ssson  { LS_SX_XLOCK,	LSX_SPIN,	LS_SX_XLOCK_SPIN,
130192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
131192853Ssson  { LS_SX_XUNLOCK,	LSX_RELEASE,	LS_SX_XUNLOCK_RELEASE,
132192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
133192853Ssson  { LS_SX_TRYUPGRADE,	LSX_UPGRADE,	LS_SX_TRYUPGRADE_UPGRADE,
134192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
135192853Ssson  { LS_SX_DOWNGRADE,	LSX_DOWNGRADE,	LS_SX_DOWNGRADE_DOWNGRADE,
136192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
137192853Ssson  /* Thread Locks */
138192853Ssson  { LS_THREAD_LOCK,	LST_SPIN,	LS_THREAD_LOCK_SPIN,
139192853Ssson	  DTRACE_IDNONE, LOCKSTAT_AFRAMES },
140192853Ssson  { NULL }
141192853Ssson};
142192853Ssson#else
143192853Ssson#error "OS not supported"
144192853Ssson#endif
145192853Ssson
146192853Ssson
147192853Sssonstatic struct cdevsw lockstat_cdevsw = {
148192853Ssson	.d_version	= D_VERSION,
149192853Ssson	.d_open		= lockstat_open,
150192853Ssson	.d_name		= "lockstat",
151192853Ssson};
152192853Ssson
153192853Sssonstatic struct cdev		*lockstat_cdev;
154192853Sssonstatic dtrace_provider_id_t 	lockstat_id;
155192853Ssson
156192853Ssson/*ARGSUSED*/
157192853Sssonstatic void
158192853Sssonlockstat_enable(void *arg, dtrace_id_t id, void *parg)
159192853Ssson{
160192853Ssson	lockstat_probe_t *probe = parg;
161192853Ssson
162192853Ssson	ASSERT(!lockstat_probemap[probe->lsp_probe]);
163192853Ssson
164285759Smarkj	lockstat_enabled++;
165285759Smarkj
166192853Ssson	lockstat_probemap[probe->lsp_probe] = id;
167192853Ssson#ifdef DOODAD
168192853Ssson	membar_producer();
169192853Ssson#endif
170192853Ssson
171192853Ssson	lockstat_probe_func = dtrace_probe;
172192853Ssson#ifdef DOODAD
173192853Ssson	membar_producer();
174192853Ssson
175192853Ssson	lockstat_hot_patch();
176192853Ssson	membar_producer();
177192853Ssson#endif
178192853Ssson}
179192853Ssson
180192853Ssson/*ARGSUSED*/
181192853Sssonstatic void
182192853Sssonlockstat_disable(void *arg, dtrace_id_t id, void *parg)
183192853Ssson{
184192853Ssson	lockstat_probe_t *probe = parg;
185192853Ssson	int i;
186192853Ssson
187192853Ssson	ASSERT(lockstat_probemap[probe->lsp_probe]);
188192853Ssson
189285759Smarkj	lockstat_enabled--;
190285759Smarkj
191192853Ssson	lockstat_probemap[probe->lsp_probe] = 0;
192192853Ssson#ifdef DOODAD
193192853Ssson	lockstat_hot_patch();
194192853Ssson	membar_producer();
195192853Ssson#endif
196192853Ssson
197192853Ssson	/*
198192853Ssson	 * See if we have any probes left enabled.
199192853Ssson	 */
200192853Ssson	for (i = 0; i < LS_NPROBES; i++) {
201192853Ssson		if (lockstat_probemap[i]) {
202192853Ssson			/*
203192853Ssson			 * This probe is still enabled.  We don't need to deal
204192853Ssson			 * with waiting for all threads to be out of the
205192853Ssson			 * lockstat critical sections; just return.
206192853Ssson			 */
207192853Ssson			return;
208192853Ssson		}
209192853Ssson	}
210192853Ssson
211192853Ssson}
212192853Ssson
213192853Ssson/*ARGSUSED*/
214192853Sssonstatic int
215192853Sssonlockstat_open(struct cdev *dev __unused, int oflags __unused,
216192853Ssson	      int devtype __unused, struct thread *td __unused)
217192853Ssson{
218192853Ssson	return (0);
219192853Ssson}
220192853Ssson
221192853Ssson/*ARGSUSED*/
222192853Sssonstatic void
223192853Sssonlockstat_provide(void *arg, dtrace_probedesc_t *desc)
224192853Ssson{
225192853Ssson	int i = 0;
226192853Ssson
227192853Ssson	for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
228192853Ssson		lockstat_probe_t *probe = &lockstat_probes[i];
229192853Ssson
230192853Ssson		if (dtrace_probe_lookup(lockstat_id, "kernel",
231192853Ssson		    probe->lsp_func, probe->lsp_name) != 0)
232192853Ssson			continue;
233192853Ssson
234192853Ssson		ASSERT(!probe->lsp_id);
235192853Ssson#ifdef __FreeBSD__
236192853Ssson		probe->lsp_id = dtrace_probe_create(lockstat_id,
237192853Ssson		    "kernel", probe->lsp_func, probe->lsp_name,
238192853Ssson		    probe->lsp_frame, probe);
239192853Ssson#else
240192853Ssson		probe->lsp_id = dtrace_probe_create(lockstat_id,
241192853Ssson		    "kernel", probe->lsp_func, probe->lsp_name,
242192853Ssson		    LOCKSTAT_AFRAMES, probe);
243192853Ssson#endif
244192853Ssson	}
245192853Ssson}
246192853Ssson
247192853Ssson/*ARGSUSED*/
248192853Sssonstatic void
249192853Sssonlockstat_destroy(void *arg, dtrace_id_t id, void *parg)
250192853Ssson{
251192853Ssson	lockstat_probe_t *probe = parg;
252192853Ssson
253192853Ssson	ASSERT(!lockstat_probemap[probe->lsp_probe]);
254192853Ssson	probe->lsp_id = 0;
255192853Ssson}
256192853Ssson
257192853Sssonstatic dtrace_pattr_t lockstat_attr = {
258192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
259192853Ssson{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
260192853Ssson{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
261192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
262192853Ssson{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
263192853Ssson};
264192853Ssson
265192853Sssonstatic dtrace_pops_t lockstat_pops = {
266192853Ssson	lockstat_provide,
267192853Ssson	NULL,
268192853Ssson	lockstat_enable,
269192853Ssson	lockstat_disable,
270192853Ssson	NULL,
271192853Ssson	NULL,
272192853Ssson	NULL,
273192853Ssson	NULL,
274192853Ssson	NULL,
275192853Ssson	lockstat_destroy
276192853Ssson};
277192853Ssson
278192853Sssonstatic void
279192853Sssonlockstat_load(void *dummy)
280192853Ssson{
281192853Ssson	/* Create the /dev/dtrace/lockstat entry. */
282192853Ssson	lockstat_cdev = make_dev(&lockstat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
283192853Ssson	    "dtrace/lockstat");
284192853Ssson
285192853Ssson	if (dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_USER,
286192853Ssson	    NULL, &lockstat_pops, NULL, &lockstat_id) != 0)
287192853Ssson	        return;
288192853Ssson}
289192853Ssson
290192853Sssonstatic int
291192853Sssonlockstat_unload()
292192853Ssson{
293192853Ssson	int error = 0;
294192853Ssson
295192853Ssson	if ((error = dtrace_unregister(lockstat_id)) != 0)
296192853Ssson	    return (error);
297192853Ssson
298192853Ssson	destroy_dev(lockstat_cdev);
299192853Ssson
300192853Ssson	return (error);
301192853Ssson}
302192853Ssson
303192853Ssson/* ARGSUSED */
304192853Sssonstatic int
305192853Sssonlockstat_modevent(module_t mod __unused, int type, void *data __unused)
306192853Ssson{
307192853Ssson	int error = 0;
308192853Ssson
309192853Ssson	switch (type) {
310192853Ssson	case MOD_LOAD:
311192853Ssson		break;
312192853Ssson
313192853Ssson	case MOD_UNLOAD:
314192853Ssson		break;
315192853Ssson
316192853Ssson	case MOD_SHUTDOWN:
317192853Ssson		break;
318192853Ssson
319192853Ssson	default:
320192853Ssson		error = EOPNOTSUPP;
321192853Ssson		break;
322192853Ssson	}
323192853Ssson	return (error);
324192853Ssson}
325192853Ssson
326192853SssonSYSINIT(lockstat_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_load, NULL);
327192853SssonSYSUNINIT(lockstat_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, lockstat_unload, NULL);
328192853Ssson
329192853SssonDEV_MODULE(lockstat, lockstat_modevent, NULL);
330192853SssonMODULE_VERSION(lockstat, 1);
331192853SssonMODULE_DEPEND(lockstat, dtrace, 1, 1, 1);
332192853SssonMODULE_DEPEND(lockstat, opensolaris, 1, 1, 1);
333