Home | History | Annotate | Download | only in dtrace
      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 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <sys/types.h>
     28 #include <sys/param.h>
     29 #include <sys/stat.h>
     30 #include <sys/open.h>
     31 #include <sys/file.h>
     32 #include <sys/conf.h>
     33 #include <sys/modctl.h>
     34 #include <sys/cmn_err.h>
     35 #include <sys/bitmap.h>
     36 #include <sys/debug.h>
     37 #include <sys/kmem.h>
     38 #include <sys/errno.h>
     39 #include <sys/sysmacros.h>
     40 #include <sys/lockstat.h>
     41 #include <sys/atomic.h>
     42 #include <sys/dtrace.h>
     43 
     44 #include <sys/ddi.h>
     45 #include <sys/sunddi.h>
     46 
     47 typedef struct lockstat_probe {
     48 	const char	*lsp_func;
     49 	const char	*lsp_name;
     50 	int		lsp_probe;
     51 	dtrace_id_t	lsp_id;
     52 } lockstat_probe_t;
     53 
     54 lockstat_probe_t lockstat_probes[] =
     55 {
     56 	{ LS_MUTEX_ENTER,	LSA_ACQUIRE,	LS_MUTEX_ENTER_ACQUIRE },
     57 	{ LS_MUTEX_ENTER,	LSA_BLOCK,	LS_MUTEX_ENTER_BLOCK },
     58 	{ LS_MUTEX_ENTER,	LSA_SPIN,	LS_MUTEX_ENTER_SPIN },
     59 	{ LS_MUTEX_EXIT,	LSA_RELEASE,	LS_MUTEX_EXIT_RELEASE },
     60 	{ LS_MUTEX_DESTROY,	LSA_RELEASE,	LS_MUTEX_DESTROY_RELEASE },
     61 	{ LS_MUTEX_TRYENTER,	LSA_ACQUIRE,	LS_MUTEX_TRYENTER_ACQUIRE },
     62 	{ LS_LOCK_SET,		LSS_ACQUIRE,	LS_LOCK_SET_ACQUIRE },
     63 	{ LS_LOCK_SET,		LSS_SPIN,	LS_LOCK_SET_SPIN },
     64 	{ LS_LOCK_SET_SPL,	LSS_ACQUIRE,	LS_LOCK_SET_SPL_ACQUIRE },
     65 	{ LS_LOCK_SET_SPL,	LSS_SPIN,	LS_LOCK_SET_SPL_SPIN },
     66 	{ LS_LOCK_TRY,		LSS_ACQUIRE,	LS_LOCK_TRY_ACQUIRE },
     67 	{ LS_LOCK_CLEAR,	LSS_RELEASE,	LS_LOCK_CLEAR_RELEASE },
     68 	{ LS_LOCK_CLEAR_SPLX,	LSS_RELEASE,	LS_LOCK_CLEAR_SPLX_RELEASE },
     69 	{ LS_CLOCK_UNLOCK,	LSS_RELEASE,	LS_CLOCK_UNLOCK_RELEASE },
     70 	{ LS_RW_ENTER,		LSR_ACQUIRE,	LS_RW_ENTER_ACQUIRE },
     71 	{ LS_RW_ENTER,		LSR_BLOCK,	LS_RW_ENTER_BLOCK },
     72 	{ LS_RW_EXIT,		LSR_RELEASE,	LS_RW_EXIT_RELEASE },
     73 	{ LS_RW_TRYENTER,	LSR_ACQUIRE,	LS_RW_TRYENTER_ACQUIRE },
     74 	{ LS_RW_TRYUPGRADE,	LSR_UPGRADE,	LS_RW_TRYUPGRADE_UPGRADE },
     75 	{ LS_RW_DOWNGRADE,	LSR_DOWNGRADE,	LS_RW_DOWNGRADE_DOWNGRADE },
     76 	{ LS_THREAD_LOCK,	LST_SPIN,	LS_THREAD_LOCK_SPIN },
     77 	{ LS_THREAD_LOCK_HIGH,	LST_SPIN,	LS_THREAD_LOCK_HIGH_SPIN },
     78 	{ NULL }
     79 };
     80 
     81 static dev_info_t	*lockstat_devi;	/* saved in xxattach() for xxinfo() */
     82 static kmutex_t		lockstat_test;	/* for testing purposes only */
     83 static dtrace_provider_id_t lockstat_id;
     84 
     85 /*ARGSUSED*/
     86 static void
     87 lockstat_enable(void *arg, dtrace_id_t id, void *parg)
     88 {
     89 	lockstat_probe_t *probe = parg;
     90 
     91 	ASSERT(!lockstat_probemap[probe->lsp_probe]);
     92 
     93 	lockstat_probemap[probe->lsp_probe] = id;
     94 	membar_producer();
     95 
     96 	lockstat_hot_patch();
     97 	membar_producer();
     98 
     99 	/*
    100 	 * Immediately generate a record for the lockstat_test mutex
    101 	 * to verify that the mutex hot-patch code worked as expected.
    102 	 */
    103 	mutex_enter(&lockstat_test);
    104 	mutex_exit(&lockstat_test);
    105 }
    106 
    107 /*ARGSUSED*/
    108 static void
    109 lockstat_disable(void *arg, dtrace_id_t id, void *parg)
    110 {
    111 	lockstat_probe_t *probe = parg;
    112 	int i;
    113 
    114 	ASSERT(lockstat_probemap[probe->lsp_probe]);
    115 
    116 	lockstat_probemap[probe->lsp_probe] = 0;
    117 	lockstat_hot_patch();
    118 	membar_producer();
    119 
    120 	/*
    121 	 * See if we have any probes left enabled.
    122 	 */
    123 	for (i = 0; i < LS_NPROBES; i++) {
    124 		if (lockstat_probemap[i]) {
    125 			/*
    126 			 * This probe is still enabled.  We don't need to deal
    127 			 * with waiting for all threads to be out of the
    128 			 * lockstat critical sections; just return.
    129 			 */
    130 			return;
    131 		}
    132 	}
    133 
    134 	/*
    135 	 * The delay() here isn't as cheesy as you might think.  We don't
    136 	 * want to busy-loop in the kernel, so we have to give up the
    137 	 * CPU between calls to lockstat_active_threads(); that much is
    138 	 * obvious.  But the reason it's a do..while loop rather than a
    139 	 * while loop is subtle.  The memory barrier above guarantees that
    140 	 * no threads will enter the lockstat code from this point forward.
    141 	 * However, another thread could already be executing lockstat code
    142 	 * without our knowledge if the update to its t_lockstat field hasn't
    143 	 * cleared its CPU's store buffer.  Delaying for one clock tick
    144 	 * guarantees that either (1) the thread will have *ample* time to
    145 	 * complete its work, or (2) the thread will be preempted, in which
    146 	 * case it will have to grab and release a dispatcher lock, which
    147 	 * will flush that CPU's store buffer.  Either way we're covered.
    148 	 */
    149 	do {
    150 		delay(1);
    151 	} while (lockstat_active_threads());
    152 }
    153 
    154 /*ARGSUSED*/
    155 static int
    156 lockstat_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
    157 {
    158 	return (0);
    159 }
    160 
    161 /* ARGSUSED */
    162 static int
    163 lockstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    164 {
    165 	int error;
    166 
    167 	switch (infocmd) {
    168 	case DDI_INFO_DEVT2DEVINFO:
    169 		*result = (void *) lockstat_devi;
    170 		error = DDI_SUCCESS;
    171 		break;
    172 	case DDI_INFO_DEVT2INSTANCE:
    173 		*result = (void *)0;
    174 		error = DDI_SUCCESS;
    175 		break;
    176 	default:
    177 		error = DDI_FAILURE;
    178 	}
    179 	return (error);
    180 }
    181 
    182 /*ARGSUSED*/
    183 static void
    184 lockstat_provide(void *arg, const dtrace_probedesc_t *desc)
    185 {
    186 	int i = 0;
    187 
    188 	for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
    189 		lockstat_probe_t *probe = &lockstat_probes[i];
    190 
    191 		if (dtrace_probe_lookup(lockstat_id, "genunix",
    192 		    probe->lsp_func, probe->lsp_name) != 0)
    193 			continue;
    194 
    195 		ASSERT(!probe->lsp_id);
    196 		probe->lsp_id = dtrace_probe_create(lockstat_id,
    197 		    "genunix", probe->lsp_func, probe->lsp_name,
    198 		    1, probe);
    199 	}
    200 }
    201 
    202 /*ARGSUSED*/
    203 static void
    204 lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
    205 {
    206 	lockstat_probe_t *probe = parg;
    207 
    208 	ASSERT(!lockstat_probemap[probe->lsp_probe]);
    209 	probe->lsp_id = 0;
    210 }
    211 
    212 static dtrace_pattr_t lockstat_attr = {
    213 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
    214 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
    215 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
    216 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
    217 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
    218 };
    219 
    220 static dtrace_pops_t lockstat_pops = {
    221 	lockstat_provide,
    222 	NULL,
    223 	lockstat_enable,
    224 	lockstat_disable,
    225 	NULL,
    226 	NULL,
    227 	NULL,
    228 	NULL,
    229 	NULL,
    230 	lockstat_destroy
    231 };
    232 
    233 static int
    234 lockstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    235 {
    236 	switch (cmd) {
    237 	case DDI_ATTACH:
    238 		break;
    239 	case DDI_RESUME:
    240 		return (DDI_SUCCESS);
    241 	default:
    242 		return (DDI_FAILURE);
    243 	}
    244 
    245 	if (ddi_create_minor_node(devi, "lockstat", S_IFCHR, 0,
    246 	    DDI_PSEUDO, 0) == DDI_FAILURE ||
    247 	    dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_KERNEL,
    248 	    NULL, &lockstat_pops, NULL, &lockstat_id) != 0) {
    249 		ddi_remove_minor_node(devi, NULL);
    250 		return (DDI_FAILURE);
    251 	}
    252 
    253 	lockstat_probe = dtrace_probe;
    254 	membar_producer();
    255 
    256 	ddi_report_dev(devi);
    257 	lockstat_devi = devi;
    258 	return (DDI_SUCCESS);
    259 }
    260 
    261 static int
    262 lockstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    263 {
    264 	switch (cmd) {
    265 	case DDI_DETACH:
    266 		break;
    267 	case DDI_SUSPEND:
    268 		return (DDI_SUCCESS);
    269 	default:
    270 		return (DDI_FAILURE);
    271 	}
    272 
    273 	if (dtrace_unregister(lockstat_id) != 0)
    274 		return (DDI_FAILURE);
    275 
    276 	ddi_remove_minor_node(devi, NULL);
    277 	return (DDI_SUCCESS);
    278 }
    279 
    280 /*
    281  * Configuration data structures
    282  */
    283 static struct cb_ops lockstat_cb_ops = {
    284 	lockstat_open,		/* open */
    285 	nodev,			/* close */
    286 	nulldev,		/* strategy */
    287 	nulldev,		/* print */
    288 	nodev,			/* dump */
    289 	nodev,			/* read */
    290 	nodev,			/* write */
    291 	nodev,			/* ioctl */
    292 	nodev,			/* devmap */
    293 	nodev,			/* mmap */
    294 	nodev,			/* segmap */
    295 	nochpoll,		/* poll */
    296 	ddi_prop_op,		/* cb_prop_op */
    297 	0,			/* streamtab */
    298 	D_MP | D_NEW		/* Driver compatibility flag */
    299 };
    300 
    301 static struct dev_ops lockstat_ops = {
    302 	DEVO_REV,		/* devo_rev, */
    303 	0,			/* refcnt */
    304 	lockstat_info,		/* getinfo */
    305 	nulldev,		/* identify */
    306 	nulldev,		/* probe */
    307 	lockstat_attach,	/* attach */
    308 	lockstat_detach,	/* detach */
    309 	nulldev,		/* reset */
    310 	&lockstat_cb_ops,	/* cb_ops */
    311 	NULL,			/* bus_ops */
    312 	NULL,			/* power */
    313 	ddi_quiesce_not_needed,		/* quiesce */
    314 };
    315 
    316 static struct modldrv modldrv = {
    317 	&mod_driverops,		/* Type of module.  This one is a driver */
    318 	"Lock Statistics",	/* name of module */
    319 	&lockstat_ops,		/* driver ops */
    320 };
    321 
    322 static struct modlinkage modlinkage = {
    323 	MODREV_1, (void *)&modldrv, NULL
    324 };
    325 
    326 int
    327 _init(void)
    328 {
    329 	return (mod_install(&modlinkage));
    330 }
    331 
    332 int
    333 _fini(void)
    334 {
    335 	return (mod_remove(&modlinkage));
    336 }
    337 
    338 int
    339 _info(struct modinfo *modinfop)
    340 {
    341 	return (mod_info(&modlinkage, modinfop));
    342 }
    343