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 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * PCI nexus driver general debug support
  28  */
  29 #include <sys/sysmacros.h>
  30 #include <sys/async.h>
  31 #include <sys/sunddi.h>           /* dev_info_t */
  32 #include <sys/ddi_impldefs.h>
  33 #include <sys/disp.h>
  34 #include <sys/archsystm.h>        /* getpil() */
  35 #include "px_obj.h"
  36 
  37 /*LINTLIBRARY*/
  38 
  39 #ifdef  DEBUG
  40 uint64_t px_debug_flags = 0;
  41 
  42 static char *px_debug_sym [] = {        /* same sequence as px_debug_bit */
  43         /*  0 */ "attach",
  44         /*  1 */ "detach",
  45         /*  2 */ "map",
  46         /*  3 */ "nex-ctlops",
  47 
  48         /*  4 */ "introps",
  49         /*  5 */ "intx-add",
  50         /*  6 */ "intx-rem",
  51         /*  7 */ "intx-intr",
  52 
  53         /*  8 */ "msiq",
  54         /*  9 */ "msiq-intr",
  55         /* 10 */ "msg",
  56         /* 11 */ "msg-intr",
  57 
  58         /* 12 */ "msix-add",
  59         /* 13 */ "msix-rem",
  60         /* 14 */ "msix-intr",
  61         /* 15 */ "err",
  62 
  63         /* 16 */ "dma-alloc",
  64         /* 17 */ "dma-free",
  65         /* 18 */ "dma-bind",
  66         /* 19 */ "dma-unbind",
  67 
  68         /* 20 */ "chk-dma-mode",
  69         /* 21 */ "bypass-dma",
  70         /* 22 */ "fast-dvma",
  71         /* 23 */ "init_child",
  72 
  73         /* 24 */ "dma-map",
  74         /* 25 */ "dma-win",
  75         /* 26 */ "map-win",
  76         /* 27 */ "unmap-win",
  77 
  78         /* 28 */ "dma-ctl",
  79         /* 29 */ "dma-sync",
  80         /* 30 */ NULL,
  81         /* 31 */ NULL,
  82 
  83         /* 32 */ "ib",
  84         /* 33 */ "cb",
  85         /* 34 */ "dmc",
  86         /* 35 */ "pec",
  87 
  88         /* 36 */ "ilu",
  89         /* 37 */ "tlu",
  90         /* 38 */ "lpu",
  91         /* 39 */ "mmu",
  92 
  93         /* 40 */ "open",
  94         /* 41 */ "close",
  95         /* 42 */ "ioctl",
  96         /* 43 */ "pwr",
  97 
  98         /* 44 */ "lib-cfg",
  99         /* 45 */ "lib-intr",
 100         /* 46 */ "lib-dma",
 101         /* 47 */ "lib-msiq",
 102 
 103         /* 48 */ "lib-msi",
 104         /* 49 */ "lib-msg",
 105         /* 50 */ "NULL",
 106         /* 51 */ "NULL",
 107 
 108         /* 52 */ "tools",
 109         /* 53 */ "phys_acc",
 110 
 111         /* 54 */ "hotplug",
 112         /* LAST */ "unknown"
 113 };
 114 
 115 /* Tunables */
 116 static int px_dbg_msg_size = 16;                /* # of Qs.  Must be ^2 */
 117 
 118 /* Non-Tunables */
 119 static int px_dbg_qmask = 0xFFFF;               /* Mask based on Q size */
 120 static px_dbg_msg_t *px_dbg_msgq = NULL;        /* Debug Msg Queue */
 121 static uint8_t px_dbg_reference = 0;            /* Reference Counter */
 122 static kmutex_t px_dbg_mutex;                   /* Mutex for dequeuing */
 123 static uint8_t px_dbg_qtail = 0;                /* Pointer to q tail */
 124 static uint8_t px_dbg_qhead = 0;                /* Pointer to q head */
 125 static uint_t px_dbg_qsize = 0;                 /* # of pending messages */
 126 static uint_t px_dbg_failed = 0;                /* # of overflows */
 127 
 128 /* Forward Declarations */
 129 static void px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
 130     va_list args);
 131 static void px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
 132     va_list args);
 133 static uint_t px_dbg_drain(caddr_t arg1, caddr_t arg2);
 134 
 135 /*
 136  * Print function called either directly by px_dbg or through soft interrupt.
 137  * This function cannot be called directly in threads with PIL above clock.
 138  */
 139 static void
 140 px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
 141 {
 142         int cont = bit >> DBG_BITS;
 143 
 144         if (cont)
 145                 goto body;
 146 
 147         if (dip)
 148                 prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
 149                     ddi_get_instance(dip), px_debug_sym[bit]);
 150         else
 151                 prom_printf("px: %s: ", px_debug_sym[bit]);
 152 body:
 153         if (args)
 154                 prom_vprintf(fmt, args);
 155         else
 156                 prom_printf(fmt);
 157 }
 158 
 159 /*
 160  * Queueing mechanism to log px_dbg messages if calling thread is running with a
 161  * PIL above clock. It's Multithreaded safe.
 162  */
 163 static void
 164 px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
 165 {
 166         int             instance = DIP_TO_INST(dip);
 167         px_t            *px_p = INST_TO_STATE(instance);
 168         uint8_t         q_no;
 169         px_dbg_msg_t    *msg_p;
 170 
 171         /* Check to make sure the queue hasn't overflowed */
 172         if (atomic_inc_uint_nv(&px_dbg_qsize) >= px_dbg_msg_size) {
 173                 px_dbg_failed++;
 174                 atomic_dec_uint(&px_dbg_qsize);
 175                 return;
 176         }
 177 
 178         /*
 179          * Grab the next available queue bucket. Incrementing the tail here
 180          * doesn't need to be protected, as it is guaranteed to not overflow.
 181          */
 182         q_no = ++px_dbg_qtail & px_dbg_qmask;
 183         msg_p = &px_dbg_msgq[q_no];
 184 
 185         ASSERT(msg_p->active == B_FALSE);
 186 
 187         /* Print the message in the buffer */
 188         vsnprintf(msg_p->msg, DBG_MSG_SIZE, fmt, args);
 189         msg_p->bit = bit;
 190         msg_p->dip = dip;
 191         msg_p->active = B_TRUE;
 192 
 193         /* Trigger Soft Int */
 194         ddi_intr_trigger_softint(px_p->px_dbg_hdl, (caddr_t)NULL);
 195 }
 196 
 197 /*
 198  * Callback function for queuing px_dbg in high PIL by soft intr.  This code
 199  * assumes it will be called serially for every msg.
 200  */
 201 static uint_t
 202 px_dbg_drain(caddr_t arg1, caddr_t arg2) {
 203         uint8_t         q_no;
 204         px_dbg_msg_t    *msg_p;
 205         uint_t          ret = DDI_INTR_UNCLAIMED;
 206 
 207         mutex_enter(&px_dbg_mutex);
 208         while (px_dbg_qsize) {
 209                 atomic_dec_uint(&px_dbg_qsize);
 210                 if (px_dbg_failed) {
 211                         cmn_err(CE_WARN, "%d msg(s) were lost",
 212                             px_dbg_failed);
 213                         px_dbg_failed = 0;
 214                 }
 215 
 216                 q_no = ++px_dbg_qhead & px_dbg_qmask;
 217                 msg_p = &px_dbg_msgq[q_no];
 218 
 219                 if (msg_p->active) {
 220                         px_dbg_print(msg_p->bit, msg_p->dip, msg_p->msg, NULL);
 221                         msg_p->active = B_FALSE;
 222                 }
 223                 ret = DDI_INTR_CLAIMED;
 224         }
 225 
 226         mutex_exit(&px_dbg_mutex);
 227         return (ret);
 228 }
 229 
 230 void
 231 px_dbg(px_debug_bit_t bit, dev_info_t *dip, char *fmt, ...)
 232 {
 233         va_list ap;
 234 
 235         bit &= DBG_MASK;
 236         if (bit >= sizeof (px_debug_sym) / sizeof (char *))
 237                 return;
 238         if (!(1ull << bit & px_debug_flags))
 239                 return;
 240 
 241         va_start(ap, fmt);
 242         if (getpil() > LOCK_LEVEL)
 243                 px_dbg_queue(bit, dip, fmt, ap);
 244         else
 245                 px_dbg_print(bit, dip, fmt, ap);
 246         va_end(ap);
 247 }
 248 #endif  /* DEBUG */
 249 
 250 void
 251 px_dbg_attach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
 252 {
 253 #ifdef  DEBUG
 254         if (px_dbg_reference++ == 0) {
 255                 int size = px_dbg_msg_size;
 256 
 257                 /* Check if px_dbg_msg_size is ^2 */
 258                 size = !ISP2(size) ? ((size | ~size) + 1) : size;
 259                 px_dbg_msg_size = size;
 260                 px_dbg_qmask = size - 1;
 261                 px_dbg_msgq = kmem_zalloc(sizeof (px_dbg_msg_t) * size,
 262                     KM_SLEEP);
 263 
 264                 mutex_init(&px_dbg_mutex, NULL, MUTEX_DRIVER, NULL);
 265         }
 266 
 267         if (ddi_intr_add_softint(dip, dbg_hdl,
 268                 DDI_INTR_SOFTPRI_MAX, px_dbg_drain, NULL) != DDI_SUCCESS) {
 269                 DBG(DBG_ATTACH, dip,
 270                     "Unable to allocate soft int for DBG printing.\n");
 271                 dbg_hdl = NULL;
 272         }
 273 #endif  /* DEBUG */
 274 }
 275 
 276 /* ARGSUSED */
 277 void
 278 px_dbg_detach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
 279 {
 280 #ifdef  DEBUG
 281         if (dbg_hdl != NULL)
 282                 (void) ddi_intr_remove_softint(*dbg_hdl);
 283 
 284         if (--px_dbg_reference == 0) {
 285                 if (px_dbg_msgq != NULL)
 286                         kmem_free(px_dbg_msgq,
 287                             sizeof (px_dbg_msg_t) * px_dbg_msg_size);
 288                 mutex_destroy(&px_dbg_mutex);
 289         }
 290 #endif  /* DEBUG */
 291 }