1179193Sjb/*
2179193Sjb * CDDL HEADER START
3179193Sjb *
4179193Sjb * The contents of this file are subject to the terms of the
5179193Sjb * Common Development and Distribution License (the "License").
6179193Sjb * You may not use this file except in compliance with the License.
7179193Sjb *
8179193Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9179193Sjb * or http://www.opensolaris.org/os/licensing.
10179193Sjb * See the License for the specific language governing permissions
11179193Sjb * and limitations under the License.
12179193Sjb *
13179193Sjb * When distributing Covered Code, include this CDDL HEADER in each
14179193Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15179193Sjb * If applicable, add the following below this CDDL HEADER, with the
16179193Sjb * fields enclosed by brackets "[]" replaced with your own identifying
17179193Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
18179193Sjb *
19179193Sjb * CDDL HEADER END
20179193Sjb */
21179193Sjb/*
22179193Sjb * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23179193Sjb * Use is subject to license terms.
24179193Sjb */
25179193Sjb
26179193Sjb#pragma ident	"%Z%%M%	%I%	%E% SMI"
27179193Sjb
28179193Sjb#include <sys/types.h>
29179193Sjb#include <sys/param.h>
30179193Sjb#include <sys/stat.h>
31179193Sjb#include <sys/open.h>
32179193Sjb#include <sys/file.h>
33179193Sjb#include <sys/conf.h>
34179193Sjb#include <sys/modctl.h>
35179193Sjb#include <sys/cmn_err.h>
36179193Sjb#include <sys/bitmap.h>
37179193Sjb#include <sys/debug.h>
38179193Sjb#include <sys/kmem.h>
39179193Sjb#include <sys/errno.h>
40179193Sjb#include <sys/sysmacros.h>
41179193Sjb#include <sys/lockstat.h>
42179193Sjb#include <sys/atomic.h>
43179193Sjb#include <sys/dtrace.h>
44179193Sjb
45179193Sjb#include <sys/ddi.h>
46179193Sjb#include <sys/sunddi.h>
47179193Sjb
48179193Sjbtypedef struct lockstat_probe {
49179193Sjb	const char	*lsp_func;
50179193Sjb	const char	*lsp_name;
51179193Sjb	int		lsp_probe;
52179193Sjb	dtrace_id_t	lsp_id;
53179193Sjb} lockstat_probe_t;
54179193Sjb
55179193Sjblockstat_probe_t lockstat_probes[] =
56179193Sjb{
57179193Sjb	{ LS_MUTEX_ENTER,	LSA_ACQUIRE,	LS_MUTEX_ENTER_ACQUIRE },
58179193Sjb	{ LS_MUTEX_ENTER,	LSA_BLOCK,	LS_MUTEX_ENTER_BLOCK },
59179193Sjb	{ LS_MUTEX_ENTER,	LSA_SPIN,	LS_MUTEX_ENTER_SPIN },
60179193Sjb	{ LS_MUTEX_EXIT,	LSA_RELEASE,	LS_MUTEX_EXIT_RELEASE },
61179193Sjb	{ LS_MUTEX_DESTROY,	LSA_RELEASE,	LS_MUTEX_DESTROY_RELEASE },
62179193Sjb	{ LS_MUTEX_TRYENTER,	LSA_ACQUIRE,	LS_MUTEX_TRYENTER_ACQUIRE },
63179193Sjb	{ LS_LOCK_SET,		LSS_ACQUIRE,	LS_LOCK_SET_ACQUIRE },
64179193Sjb	{ LS_LOCK_SET,		LSS_SPIN,	LS_LOCK_SET_SPIN },
65179193Sjb	{ LS_LOCK_SET_SPL,	LSS_ACQUIRE,	LS_LOCK_SET_SPL_ACQUIRE },
66179193Sjb	{ LS_LOCK_SET_SPL,	LSS_SPIN,	LS_LOCK_SET_SPL_SPIN },
67179193Sjb	{ LS_LOCK_TRY,		LSS_ACQUIRE,	LS_LOCK_TRY_ACQUIRE },
68179193Sjb	{ LS_LOCK_CLEAR,	LSS_RELEASE,	LS_LOCK_CLEAR_RELEASE },
69179193Sjb	{ LS_LOCK_CLEAR_SPLX,	LSS_RELEASE,	LS_LOCK_CLEAR_SPLX_RELEASE },
70179193Sjb	{ LS_CLOCK_UNLOCK,	LSS_RELEASE,	LS_CLOCK_UNLOCK_RELEASE },
71179193Sjb	{ LS_RW_ENTER,		LSR_ACQUIRE,	LS_RW_ENTER_ACQUIRE },
72179193Sjb	{ LS_RW_ENTER,		LSR_BLOCK,	LS_RW_ENTER_BLOCK },
73179193Sjb	{ LS_RW_EXIT,		LSR_RELEASE,	LS_RW_EXIT_RELEASE },
74179193Sjb	{ LS_RW_TRYENTER,	LSR_ACQUIRE,	LS_RW_TRYENTER_ACQUIRE },
75179193Sjb	{ LS_RW_TRYUPGRADE,	LSR_UPGRADE,	LS_RW_TRYUPGRADE_UPGRADE },
76179193Sjb	{ LS_RW_DOWNGRADE,	LSR_DOWNGRADE,	LS_RW_DOWNGRADE_DOWNGRADE },
77179193Sjb	{ LS_THREAD_LOCK,	LST_SPIN,	LS_THREAD_LOCK_SPIN },
78179193Sjb	{ LS_THREAD_LOCK_HIGH,	LST_SPIN,	LS_THREAD_LOCK_HIGH_SPIN },
79179193Sjb	{ NULL }
80179193Sjb};
81179193Sjb
82179193Sjbstatic dev_info_t	*lockstat_devi;	/* saved in xxattach() for xxinfo() */
83179193Sjbstatic kmutex_t		lockstat_test;	/* for testing purposes only */
84179193Sjbstatic dtrace_provider_id_t lockstat_id;
85179193Sjb
86179193Sjb/*ARGSUSED*/
87179193Sjbstatic void
88179193Sjblockstat_enable(void *arg, dtrace_id_t id, void *parg)
89179193Sjb{
90179193Sjb	lockstat_probe_t *probe = parg;
91179193Sjb
92179193Sjb	ASSERT(!lockstat_probemap[probe->lsp_probe]);
93179193Sjb
94179193Sjb	lockstat_probemap[probe->lsp_probe] = id;
95179193Sjb	membar_producer();
96179193Sjb
97179193Sjb	lockstat_hot_patch();
98179193Sjb	membar_producer();
99179193Sjb
100179193Sjb	/*
101179193Sjb	 * Immediately generate a record for the lockstat_test mutex
102179193Sjb	 * to verify that the mutex hot-patch code worked as expected.
103179193Sjb	 */
104179193Sjb	mutex_enter(&lockstat_test);
105179193Sjb	mutex_exit(&lockstat_test);
106179193Sjb}
107179193Sjb
108179193Sjb/*ARGSUSED*/
109179193Sjbstatic void
110179193Sjblockstat_disable(void *arg, dtrace_id_t id, void *parg)
111179193Sjb{
112179193Sjb	lockstat_probe_t *probe = parg;
113179193Sjb	int i;
114179193Sjb
115179193Sjb	ASSERT(lockstat_probemap[probe->lsp_probe]);
116179193Sjb
117179193Sjb	lockstat_probemap[probe->lsp_probe] = 0;
118179193Sjb	lockstat_hot_patch();
119179193Sjb	membar_producer();
120179193Sjb
121179193Sjb	/*
122179193Sjb	 * See if we have any probes left enabled.
123179193Sjb	 */
124179193Sjb	for (i = 0; i < LS_NPROBES; i++) {
125179193Sjb		if (lockstat_probemap[i]) {
126179193Sjb			/*
127179193Sjb			 * This probe is still enabled.  We don't need to deal
128179193Sjb			 * with waiting for all threads to be out of the
129179193Sjb			 * lockstat critical sections; just return.
130179193Sjb			 */
131179193Sjb			return;
132179193Sjb		}
133179193Sjb	}
134179193Sjb
135179193Sjb	/*
136179193Sjb	 * The delay() here isn't as cheesy as you might think.  We don't
137179193Sjb	 * want to busy-loop in the kernel, so we have to give up the
138179193Sjb	 * CPU between calls to lockstat_active_threads(); that much is
139179193Sjb	 * obvious.  But the reason it's a do..while loop rather than a
140179193Sjb	 * while loop is subtle.  The memory barrier above guarantees that
141179193Sjb	 * no threads will enter the lockstat code from this point forward.
142179193Sjb	 * However, another thread could already be executing lockstat code
143179193Sjb	 * without our knowledge if the update to its t_lockstat field hasn't
144179193Sjb	 * cleared its CPU's store buffer.  Delaying for one clock tick
145179193Sjb	 * guarantees that either (1) the thread will have *ample* time to
146179193Sjb	 * complete its work, or (2) the thread will be preempted, in which
147179193Sjb	 * case it will have to grab and release a dispatcher lock, which
148179193Sjb	 * will flush that CPU's store buffer.  Either way we're covered.
149179193Sjb	 */
150179193Sjb	do {
151179193Sjb		delay(1);
152179193Sjb	} while (lockstat_active_threads());
153179193Sjb}
154179193Sjb
155179193Sjb/*ARGSUSED*/
156179193Sjbstatic int
157179193Sjblockstat_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
158179193Sjb{
159179193Sjb	return (0);
160179193Sjb}
161179193Sjb
162179193Sjb/* ARGSUSED */
163179193Sjbstatic int
164179193Sjblockstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
165179193Sjb{
166179193Sjb	int error;
167179193Sjb
168179193Sjb	switch (infocmd) {
169179193Sjb	case DDI_INFO_DEVT2DEVINFO:
170179193Sjb		*result = (void *) lockstat_devi;
171179193Sjb		error = DDI_SUCCESS;
172179193Sjb		break;
173179193Sjb	case DDI_INFO_DEVT2INSTANCE:
174179193Sjb		*result = (void *)0;
175179193Sjb		error = DDI_SUCCESS;
176179193Sjb		break;
177179193Sjb	default:
178179193Sjb		error = DDI_FAILURE;
179179193Sjb	}
180179193Sjb	return (error);
181179193Sjb}
182179193Sjb
183179193Sjb/*ARGSUSED*/
184179193Sjbstatic void
185179193Sjblockstat_provide(void *arg, const dtrace_probedesc_t *desc)
186179193Sjb{
187179193Sjb	int i = 0;
188179193Sjb
189179193Sjb	for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
190179193Sjb		lockstat_probe_t *probe = &lockstat_probes[i];
191179193Sjb
192179193Sjb		if (dtrace_probe_lookup(lockstat_id, "genunix",
193179193Sjb		    probe->lsp_func, probe->lsp_name) != 0)
194179193Sjb			continue;
195179193Sjb
196179193Sjb		ASSERT(!probe->lsp_id);
197179193Sjb		probe->lsp_id = dtrace_probe_create(lockstat_id,
198179193Sjb		    "genunix", probe->lsp_func, probe->lsp_name,
199179193Sjb		    1, probe);
200179193Sjb	}
201179193Sjb}
202179193Sjb
203179193Sjb/*ARGSUSED*/
204179193Sjbstatic void
205179193Sjblockstat_destroy(void *arg, dtrace_id_t id, void *parg)
206179193Sjb{
207179193Sjb	lockstat_probe_t *probe = parg;
208179193Sjb
209179193Sjb	ASSERT(!lockstat_probemap[probe->lsp_probe]);
210179193Sjb	probe->lsp_id = 0;
211179193Sjb}
212179193Sjb
213179193Sjbstatic dtrace_pattr_t lockstat_attr = {
214179193Sjb{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
215179193Sjb{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
216179193Sjb{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
217179193Sjb{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
218179193Sjb{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
219179193Sjb};
220179193Sjb
221179193Sjbstatic dtrace_pops_t lockstat_pops = {
222179193Sjb	lockstat_provide,
223179193Sjb	NULL,
224179193Sjb	lockstat_enable,
225179193Sjb	lockstat_disable,
226179193Sjb	NULL,
227179193Sjb	NULL,
228179193Sjb	NULL,
229179193Sjb	NULL,
230179193Sjb	NULL,
231179193Sjb	lockstat_destroy
232179193Sjb};
233179193Sjb
234179193Sjbstatic int
235179193Sjblockstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
236179193Sjb{
237179193Sjb	switch (cmd) {
238179193Sjb	case DDI_ATTACH:
239179193Sjb		break;
240179193Sjb	case DDI_RESUME:
241179193Sjb		return (DDI_SUCCESS);
242179193Sjb	default:
243179193Sjb		return (DDI_FAILURE);
244179193Sjb	}
245179193Sjb
246179193Sjb	if (ddi_create_minor_node(devi, "lockstat", S_IFCHR, 0,
247179193Sjb	    DDI_PSEUDO, 0) == DDI_FAILURE ||
248179193Sjb	    dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_KERNEL,
249179193Sjb	    NULL, &lockstat_pops, NULL, &lockstat_id) != 0) {
250179193Sjb		ddi_remove_minor_node(devi, NULL);
251179193Sjb		return (DDI_FAILURE);
252179193Sjb	}
253179193Sjb
254179193Sjb	lockstat_probe = dtrace_probe;
255179193Sjb	membar_producer();
256179193Sjb
257179193Sjb	ddi_report_dev(devi);
258179193Sjb	lockstat_devi = devi;
259179193Sjb	return (DDI_SUCCESS);
260179193Sjb}
261179193Sjb
262179193Sjbstatic int
263179193Sjblockstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
264179193Sjb{
265179193Sjb	switch (cmd) {
266179193Sjb	case DDI_DETACH:
267179193Sjb		break;
268179193Sjb	case DDI_SUSPEND:
269179193Sjb		return (DDI_SUCCESS);
270179193Sjb	default:
271179193Sjb		return (DDI_FAILURE);
272179193Sjb	}
273179193Sjb
274179193Sjb	if (dtrace_unregister(lockstat_id) != 0)
275179193Sjb		return (DDI_FAILURE);
276179193Sjb
277179193Sjb	ddi_remove_minor_node(devi, NULL);
278179193Sjb	return (DDI_SUCCESS);
279179193Sjb}
280179193Sjb
281179193Sjb/*
282179193Sjb * Configuration data structures
283179193Sjb */
284179193Sjbstatic struct cb_ops lockstat_cb_ops = {
285179193Sjb	lockstat_open,		/* open */
286179193Sjb	nodev,			/* close */
287179193Sjb	nulldev,		/* strategy */
288179193Sjb	nulldev,		/* print */
289179193Sjb	nodev,			/* dump */
290179193Sjb	nodev,			/* read */
291179193Sjb	nodev,			/* write */
292179193Sjb	nodev,			/* ioctl */
293179193Sjb	nodev,			/* devmap */
294179193Sjb	nodev,			/* mmap */
295179193Sjb	nodev,			/* segmap */
296179193Sjb	nochpoll,		/* poll */
297179193Sjb	ddi_prop_op,		/* cb_prop_op */
298179193Sjb	0,			/* streamtab */
299179193Sjb	D_MP | D_NEW		/* Driver compatibility flag */
300179193Sjb};
301179193Sjb
302179193Sjbstatic struct dev_ops lockstat_ops = {
303179193Sjb	DEVO_REV,		/* devo_rev, */
304179193Sjb	0,			/* refcnt */
305179193Sjb	lockstat_info,		/* getinfo */
306179193Sjb	nulldev,		/* identify */
307179193Sjb	nulldev,		/* probe */
308179193Sjb	lockstat_attach,	/* attach */
309179193Sjb	lockstat_detach,	/* detach */
310179193Sjb	nulldev,		/* reset */
311179193Sjb	&lockstat_cb_ops,	/* cb_ops */
312179193Sjb	NULL,			/* bus_ops */
313179193Sjb};
314179193Sjb
315179193Sjbstatic struct modldrv modldrv = {
316179193Sjb	&mod_driverops,		/* Type of module.  This one is a driver */
317179193Sjb	"Lock Statistics %I%",	/* name of module */
318179193Sjb	&lockstat_ops,		/* driver ops */
319179193Sjb};
320179193Sjb
321179193Sjbstatic struct modlinkage modlinkage = {
322179193Sjb	MODREV_1, (void *)&modldrv, NULL
323179193Sjb};
324179193Sjb
325179193Sjbint
326179193Sjb_init(void)
327179193Sjb{
328179193Sjb	return (mod_install(&modlinkage));
329179193Sjb}
330179193Sjb
331179193Sjbint
332179193Sjb_fini(void)
333179193Sjb{
334179193Sjb	return (mod_remove(&modlinkage));
335179193Sjb}
336179193Sjb
337179193Sjbint
338179193Sjb_info(struct modinfo *modinfop)
339179193Sjb{
340179193Sjb	return (mod_info(&modlinkage, modinfop));
341179193Sjb}
342