root/usr/src/uts/sun4/io/px/px_debug.c
/*
 * 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 */
}