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