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 2009 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 int
  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         return (0);
 106 }
 107 
 108 /*ARGSUSED*/
 109 static void
 110 lockstat_disable(void *arg, dtrace_id_t id, void *parg)
 111 {
 112         lockstat_probe_t *probe = parg;
 113         int i;
 114 
 115         ASSERT(lockstat_probemap[probe->lsp_probe]);
 116 
 117         lockstat_probemap[probe->lsp_probe] = 0;
 118         lockstat_hot_patch();
 119         membar_producer();
 120 
 121         /*
 122          * See if we have any probes left enabled.
 123          */
 124         for (i = 0; i < LS_NPROBES; i++) {
 125                 if (lockstat_probemap[i]) {
 126                         /*
 127                          * This probe is still enabled.  We don't need to deal
 128                          * with waiting for all threads to be out of the
 129                          * lockstat critical sections; just return.
 130                          */
 131                         return;
 132                 }
 133         }
 134 
 135         /*
 136          * The delay() here isn't as cheesy as you might think.  We don't
 137          * want to busy-loop in the kernel, so we have to give up the
 138          * CPU between calls to lockstat_active_threads(); that much is
 139          * obvious.  But the reason it's a do..while loop rather than a
 140          * while loop is subtle.  The memory barrier above guarantees that
 141          * no threads will enter the lockstat code from this point forward.
 142          * However, another thread could already be executing lockstat code
 143          * without our knowledge if the update to its t_lockstat field hasn't
 144          * cleared its CPU's store buffer.  Delaying for one clock tick
 145          * guarantees that either (1) the thread will have *ample* time to
 146          * complete its work, or (2) the thread will be preempted, in which
 147          * case it will have to grab and release a dispatcher lock, which
 148          * will flush that CPU's store buffer.  Either way we're covered.
 149          */
 150         do {
 151                 delay(1);
 152         } while (lockstat_active_threads());
 153 }
 154 
 155 /*ARGSUSED*/
 156 static int
 157 lockstat_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
 158 {
 159         return (0);
 160 }
 161 
 162 /* ARGSUSED */
 163 static int
 164 lockstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 165 {
 166         int error;
 167 
 168         switch (infocmd) {
 169         case DDI_INFO_DEVT2DEVINFO:
 170                 *result = (void *) lockstat_devi;
 171                 error = DDI_SUCCESS;
 172                 break;
 173         case DDI_INFO_DEVT2INSTANCE:
 174                 *result = (void *)0;
 175                 error = DDI_SUCCESS;
 176                 break;
 177         default:
 178                 error = DDI_FAILURE;
 179         }
 180         return (error);
 181 }
 182 
 183 /*ARGSUSED*/
 184 static void
 185 lockstat_provide(void *arg, const dtrace_probedesc_t *desc)
 186 {
 187         int i = 0;
 188 
 189         for (i = 0; lockstat_probes[i].lsp_func != NULL; i++) {
 190                 lockstat_probe_t *probe = &lockstat_probes[i];
 191 
 192                 if (dtrace_probe_lookup(lockstat_id, "genunix",
 193                     probe->lsp_func, probe->lsp_name) != 0)
 194                         continue;
 195 
 196                 ASSERT(!probe->lsp_id);
 197                 probe->lsp_id = dtrace_probe_create(lockstat_id,
 198                     "genunix", probe->lsp_func, probe->lsp_name,
 199                     1, probe);
 200         }
 201 }
 202 
 203 /*ARGSUSED*/
 204 static void
 205 lockstat_destroy(void *arg, dtrace_id_t id, void *parg)
 206 {
 207         lockstat_probe_t *probe = parg;
 208 
 209         ASSERT(!lockstat_probemap[probe->lsp_probe]);
 210         probe->lsp_id = 0;
 211 }
 212 
 213 static dtrace_pattr_t lockstat_attr = {
 214 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
 215 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
 216 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
 217 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
 218 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
 219 };
 220 
 221 static dtrace_pops_t lockstat_pops = {
 222         lockstat_provide,
 223         NULL,
 224         lockstat_enable,
 225         lockstat_disable,
 226         NULL,
 227         NULL,
 228         NULL,
 229         NULL,
 230         NULL,
 231         lockstat_destroy
 232 };
 233 
 234 static int
 235 lockstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 236 {
 237         switch (cmd) {
 238         case DDI_ATTACH:
 239                 break;
 240         case DDI_RESUME:
 241                 return (DDI_SUCCESS);
 242         default:
 243                 return (DDI_FAILURE);
 244         }
 245 
 246         if (ddi_create_minor_node(devi, "lockstat", S_IFCHR, 0,
 247             DDI_PSEUDO, 0) == DDI_FAILURE ||
 248             dtrace_register("lockstat", &lockstat_attr, DTRACE_PRIV_KERNEL,
 249             NULL, &lockstat_pops, NULL, &lockstat_id) != 0) {
 250                 ddi_remove_minor_node(devi, NULL);
 251                 return (DDI_FAILURE);
 252         }
 253 
 254         lockstat_probe = dtrace_probe;
 255         membar_producer();
 256 
 257         ddi_report_dev(devi);
 258         lockstat_devi = devi;
 259         return (DDI_SUCCESS);
 260 }
 261 
 262 static int
 263 lockstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 264 {
 265         switch (cmd) {
 266         case DDI_DETACH:
 267                 break;
 268         case DDI_SUSPEND:
 269                 return (DDI_SUCCESS);
 270         default:
 271                 return (DDI_FAILURE);
 272         }
 273 
 274         if (dtrace_unregister(lockstat_id) != 0)
 275                 return (DDI_FAILURE);
 276 
 277         ddi_remove_minor_node(devi, NULL);
 278         return (DDI_SUCCESS);
 279 }
 280 
 281 /*
 282  * Configuration data structures
 283  */
 284 static struct cb_ops lockstat_cb_ops = {
 285         lockstat_open,          /* open */
 286         nodev,                  /* close */
 287         nulldev,                /* strategy */
 288         nulldev,                /* print */
 289         nodev,                  /* dump */
 290         nodev,                  /* read */
 291         nodev,                  /* write */
 292         nodev,                  /* ioctl */
 293         nodev,                  /* devmap */
 294         nodev,                  /* mmap */
 295         nodev,                  /* segmap */
 296         nochpoll,               /* poll */
 297         ddi_prop_op,            /* cb_prop_op */
 298         0,                      /* streamtab */
 299         D_MP | D_NEW            /* Driver compatibility flag */
 300 };
 301 
 302 static struct dev_ops lockstat_ops = {
 303         DEVO_REV,               /* devo_rev, */
 304         0,                      /* refcnt */
 305         lockstat_info,          /* getinfo */
 306         nulldev,                /* identify */
 307         nulldev,                /* probe */
 308         lockstat_attach,        /* attach */
 309         lockstat_detach,        /* detach */
 310         nulldev,                /* reset */
 311         &lockstat_cb_ops,   /* cb_ops */
 312         NULL,                   /* bus_ops */
 313         NULL,                   /* power */
 314         ddi_quiesce_not_needed,         /* quiesce */
 315 };
 316 
 317 static struct modldrv modldrv = {
 318         &mod_driverops,             /* Type of module.  This one is a driver */
 319         "Lock Statistics",      /* name of module */
 320         &lockstat_ops,              /* driver ops */
 321 };
 322 
 323 static struct modlinkage modlinkage = {
 324         MODREV_1, { (void *)&modldrv, NULL }
 325 };
 326 
 327 int
 328 _init(void)
 329 {
 330         return (mod_install(&modlinkage));
 331 }
 332 
 333 int
 334 _fini(void)
 335 {
 336         return (mod_remove(&modlinkage));
 337 }
 338 
 339 int
 340 _info(struct modinfo *modinfop)
 341 {
 342         return (mod_info(&modlinkage, modinfop));
 343 }