/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* PCI nexus driver general debug support
*/
#include <sys/sysmacros.h>
#include <sys/async.h>
#include <sys/sunddi.h> /* dev_info_t */
#include <sys/ddi_impldefs.h>
#include <sys/disp.h>
#include <sys/archsystm.h> /* getpil() */
#include "px_obj.h"
/*LINTLIBRARY*/
#ifdef DEBUG
uint64_t px_debug_flags = 0;
static char *px_debug_sym [] = { /* same sequence as px_debug_bit */
/* 0 */ "attach",
/* 1 */ "detach",
/* 2 */ "map",
/* 3 */ "nex-ctlops",
/* 4 */ "introps",
/* 5 */ "intx-add",
/* 6 */ "intx-rem",
/* 7 */ "intx-intr",
/* 8 */ "msiq",
/* 9 */ "msiq-intr",
/* 10 */ "msg",
/* 11 */ "msg-intr",
/* 12 */ "msix-add",
/* 13 */ "msix-rem",
/* 14 */ "msix-intr",
/* 15 */ "err",
/* 16 */ "dma-alloc",
/* 17 */ "dma-free",
/* 18 */ "dma-bind",
/* 19 */ "dma-unbind",
/* 20 */ "chk-dma-mode",
/* 21 */ "bypass-dma",
/* 22 */ "fast-dvma",
/* 23 */ "init_child",
/* 24 */ "dma-map",
/* 25 */ "dma-win",
/* 26 */ "map-win",
/* 27 */ "unmap-win",
/* 28 */ "dma-ctl",
/* 29 */ "dma-sync",
/* 30 */ NULL,
/* 31 */ NULL,
/* 32 */ "ib",
/* 33 */ "cb",
/* 34 */ "dmc",
/* 35 */ "pec",
/* 36 */ "ilu",
/* 37 */ "tlu",
/* 38 */ "lpu",
/* 39 */ "mmu",
/* 40 */ "open",
/* 41 */ "close",
/* 42 */ "ioctl",
/* 43 */ "pwr",
/* 44 */ "lib-cfg",
/* 45 */ "lib-intr",
/* 46 */ "lib-dma",
/* 47 */ "lib-msiq",
/* 48 */ "lib-msi",
/* 49 */ "lib-msg",
/* 50 */ "NULL",
/* 51 */ "NULL",
/* 52 */ "tools",
/* 53 */ "phys_acc",
/* 54 */ "hotplug",
/* LAST */ "unknown"
};
/* Tunables */
static int px_dbg_msg_size = 16; /* # of Qs. Must be ^2 */
/* Non-Tunables */
static int px_dbg_qmask = 0xFFFF; /* Mask based on Q size */
static px_dbg_msg_t *px_dbg_msgq = NULL; /* Debug Msg Queue */
static uint8_t px_dbg_reference = 0; /* Reference Counter */
static kmutex_t px_dbg_mutex; /* Mutex for dequeuing */
static uint8_t px_dbg_qtail = 0; /* Pointer to q tail */
static uint8_t px_dbg_qhead = 0; /* Pointer to q head */
static uint_t px_dbg_qsize = 0; /* # of pending messages */
static uint_t px_dbg_failed = 0; /* # of overflows */
/* Forward Declarations */
static void px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
va_list args);
static void px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt,
va_list args);
static uint_t px_dbg_drain(caddr_t arg1, caddr_t arg2);
/*
* Print function called either directly by px_dbg or through soft interrupt.
* This function cannot be called directly in threads with PIL above clock.
*/
static void
px_dbg_print(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
{
int cont = bit >> DBG_BITS;
if (cont)
goto body;
if (dip)
prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
ddi_get_instance(dip), px_debug_sym[bit]);
else
prom_printf("px: %s: ", px_debug_sym[bit]);
body:
if (args)
prom_vprintf(fmt, args);
else
prom_printf(fmt);
}
/*
* Queueing mechanism to log px_dbg messages if calling thread is running with a
* PIL above clock. It's Multithreaded safe.
*/
static void
px_dbg_queue(px_debug_bit_t bit, dev_info_t *dip, char *fmt, va_list args)
{
int instance = DIP_TO_INST(dip);
px_t *px_p = INST_TO_STATE(instance);
uint8_t q_no;
px_dbg_msg_t *msg_p;
/* Check to make sure the queue hasn't overflowed */
if (atomic_inc_uint_nv(&px_dbg_qsize) >= px_dbg_msg_size) {
px_dbg_failed++;
atomic_dec_uint(&px_dbg_qsize);
return;
}
/*
* Grab the next available queue bucket. Incrementing the tail here
* doesn't need to be protected, as it is guaranteed to not overflow.
*/
q_no = ++px_dbg_qtail & px_dbg_qmask;
msg_p = &px_dbg_msgq[q_no];
ASSERT(msg_p->active == B_FALSE);
/* Print the message in the buffer */
vsnprintf(msg_p->msg, DBG_MSG_SIZE, fmt, args);
msg_p->bit = bit;
msg_p->dip = dip;
msg_p->active = B_TRUE;
/* Trigger Soft Int */
ddi_intr_trigger_softint(px_p->px_dbg_hdl, (caddr_t)NULL);
}
/*
* Callback function for queuing px_dbg in high PIL by soft intr. This code
* assumes it will be called serially for every msg.
*/
static uint_t
px_dbg_drain(caddr_t arg1, caddr_t arg2) {
uint8_t q_no;
px_dbg_msg_t *msg_p;
uint_t ret = DDI_INTR_UNCLAIMED;
mutex_enter(&px_dbg_mutex);
while (px_dbg_qsize) {
atomic_dec_uint(&px_dbg_qsize);
if (px_dbg_failed) {
cmn_err(CE_WARN, "%d msg(s) were lost",
px_dbg_failed);
px_dbg_failed = 0;
}
q_no = ++px_dbg_qhead & px_dbg_qmask;
msg_p = &px_dbg_msgq[q_no];
if (msg_p->active) {
px_dbg_print(msg_p->bit, msg_p->dip, msg_p->msg, NULL);
msg_p->active = B_FALSE;
}
ret = DDI_INTR_CLAIMED;
}
mutex_exit(&px_dbg_mutex);
return (ret);
}
void
px_dbg(px_debug_bit_t bit, dev_info_t *dip, char *fmt, ...)
{
va_list ap;
bit &= DBG_MASK;
if (bit >= sizeof (px_debug_sym) / sizeof (char *))
return;
if (!(1ull << bit & px_debug_flags))
return;
va_start(ap, fmt);
if (getpil() > LOCK_LEVEL)
px_dbg_queue(bit, dip, fmt, ap);
else
px_dbg_print(bit, dip, fmt, ap);
va_end(ap);
}
#endif /* DEBUG */
void
px_dbg_attach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
{
#ifdef DEBUG
if (px_dbg_reference++ == 0) {
int size = px_dbg_msg_size;
/* Check if px_dbg_msg_size is ^2 */
/*
* WARNING: The bellow statement makes no sense. If size is
* not a power of 2, it will set size to zero.
*/
size = !ISP2(size) ? ((size | ~size) + 1) : size;
px_dbg_msg_size = size;
px_dbg_qmask = size - 1;
px_dbg_msgq = kmem_zalloc(sizeof (px_dbg_msg_t) * size,
KM_SLEEP);
mutex_init(&px_dbg_mutex, NULL, MUTEX_DRIVER, NULL);
}
if (ddi_intr_add_softint(dip, dbg_hdl,
DDI_INTR_SOFTPRI_MAX, px_dbg_drain, NULL) != DDI_SUCCESS) {
DBG(DBG_ATTACH, dip,
"Unable to allocate soft int for DBG printing.\n");
dbg_hdl = NULL;
}
#endif /* DEBUG */
}
/* ARGSUSED */
void
px_dbg_detach(dev_info_t *dip, ddi_softint_handle_t *dbg_hdl)
{
#ifdef DEBUG
if (dbg_hdl != NULL)
(void) ddi_intr_remove_softint(*dbg_hdl);
if (--px_dbg_reference == 0) {
if (px_dbg_msgq != NULL)
kmem_free(px_dbg_msgq,
sizeof (px_dbg_msg_t) * px_dbg_msg_size);
mutex_destroy(&px_dbg_mutex);
}
#endif /* DEBUG */
}