/*
* 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* This file is more or less the same as the Solaris IBTL debug
* implementation. The debug functions and conf variables are
* similar. One significant change is :
* sol_ofs_supress_above_l2
* This has to be set to 0, in /etc/system to enable debug prints
* above level 2.
*/
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
#define SOL_OFS_PRINT_BUF_LEN 4096
#define SOL_OFS_DEBUG_BUF_SIZE 0x10000
#define SOL_OFS_DEBUG_EXTRA_SIZE 8
#define SOL_OFS_LOG_L5 5
#define SOL_OFS_LOG_L4 4
#define SOL_OFS_LOG_L3 3
#define SOL_OFS_LOG_L2 2
#define SOL_OFS_LOG_L1 1
#define SOL_OFS_LOG_L0 0
static kmutex_t sol_ofs_debug_mutex;
static char sol_ofs_print_buf[SOL_OFS_PRINT_BUF_LEN];
static char *sol_ofs_debug_sptr = NULL;
static char *sol_ofs_debug_eptr = NULL;
char *sol_ofs_debug_buf = NULL;
int sol_ofs_clear_debug_buf_flag = 0;
int sol_ofs_debug_buf_size = SOL_OFS_DEBUG_BUF_SIZE;
int sol_ofs_suppress_dprintf = 0;
int sol_ofs_buffer_dprintf = 1;
int sol_ofs_supress_above_l2 = 1;
int sol_ucma_errlevel = 2; /* sol_ucma driver */
int sol_uverbs_errlevel = 2; /* sol_uverbs driver */
int sol_umad_errlevel = 2; /* sol_umad driver */
int sol_rdmacm_errlevel = 2; /* rdmacm part of sol_ofs */
int sol_kverbs_errlevel = 2; /* kverbs part of sol_ofs */
/* sol_ofs module (except rdmacm and kverbs) */
int sol_ofs_module_errlevel = 2;
/* Global error levels for all OF related modules */
int sol_of_errlevel = 2;
static void
sol_ofs_clear_dbg_buf()
{
ASSERT(MUTEX_HELD(&sol_ofs_debug_mutex));
if (sol_ofs_debug_buf) {
sol_ofs_debug_sptr = sol_ofs_debug_buf;
sol_ofs_debug_eptr = sol_ofs_debug_buf +
sol_ofs_debug_buf_size - SOL_OFS_DEBUG_EXTRA_SIZE;
bzero(sol_ofs_debug_sptr, sol_ofs_debug_buf_size);
}
}
/*
* sol_ofs_dprintf_init() and sol_ofs_dprintf_fini() must be called
* from the _init of the sol_ofs module.
*/
void
sol_ofs_dprintf_init()
{
char *dbg_buf;
mutex_init(&sol_ofs_debug_mutex, NULL, MUTEX_DRIVER, NULL);
if (sol_ofs_debug_buf_size < SOL_OFS_DEBUG_EXTRA_SIZE) {
#ifdef DEBUG
cmn_err(CE_NOTE, "sol_ofs:\t debug buf size 0x%x too small, "
"setting to 0x%x", sol_ofs_debug_buf_size,
SOL_OFS_DEBUG_BUF_SIZE);
#endif
sol_ofs_debug_buf_size = SOL_OFS_DEBUG_BUF_SIZE;
}
dbg_buf = kmem_zalloc(sol_ofs_debug_buf_size, KM_SLEEP);
mutex_enter(&sol_ofs_debug_mutex);
sol_ofs_debug_buf = dbg_buf;
sol_ofs_clear_dbg_buf();
mutex_exit(&sol_ofs_debug_mutex);
}
void
sol_ofs_dprintf_fini()
{
char *dbg_buf;
mutex_enter(&sol_ofs_debug_mutex);
dbg_buf = sol_ofs_debug_buf;
sol_ofs_debug_buf = NULL;
mutex_exit(&sol_ofs_debug_mutex);
kmem_free(dbg_buf, sol_ofs_debug_buf_size);
mutex_destroy(&sol_ofs_debug_mutex);
}
static void
sol_ofs_dprintf_vlog(char *name, uint_t level, char *fmt, va_list ap)
{
char *label = (name == NULL) ? "sol_ofs_ulp" : name;
char *msg_ptr;
size_t len;
mutex_enter(&sol_ofs_debug_mutex);
/* if not using logging scheme; quit */
if (sol_ofs_suppress_dprintf || (sol_ofs_debug_buf == NULL)) {
mutex_exit(&sol_ofs_debug_mutex);
return;
}
/* if level doesn't match, we are done */
if (level > SOL_OFS_LOG_L5) {
mutex_exit(&sol_ofs_debug_mutex);
return;
}
/* If user requests to clear debug buffer, go ahead */
if (sol_ofs_clear_debug_buf_flag) {
sol_ofs_clear_dbg_buf();
sol_ofs_clear_debug_buf_flag = 0;
}
/* Skip printing to buffer, if too small */
if (sol_ofs_debug_buf_size <= 0) {
sol_ofs_buffer_dprintf = 0;
}
/* Put label and debug info into buffer */
len = snprintf((char *)sol_ofs_print_buf, SOL_OFS_DRV_NAME_LEN,
"%s:\t", label);
msg_ptr = (char *)sol_ofs_print_buf + len;
len += vsnprintf(msg_ptr, SOL_OFS_PRINT_BUF_LEN - len - 2, fmt, ap);
len = min(len, SOL_OFS_PRINT_BUF_LEN - 2);
ASSERT(len == strlen(sol_ofs_print_buf));
sol_ofs_print_buf[len++] = '\n';
sol_ofs_print_buf[len] = '\0';
/* Stuff into debug buffer */
if (sol_ofs_buffer_dprintf) {
/*
* overwrite >>>> that might be over the end of the
* buffer.
*/
*sol_ofs_debug_sptr = '\0';
if (sol_ofs_debug_sptr + len > sol_ofs_debug_eptr) {
size_t left;
left = sol_ofs_debug_eptr - sol_ofs_debug_sptr;
bcopy((caddr_t)sol_ofs_print_buf,
(caddr_t)sol_ofs_debug_sptr, left);
bcopy((caddr_t)sol_ofs_print_buf + left,
(caddr_t)sol_ofs_debug_buf, len - left);
sol_ofs_debug_sptr = sol_ofs_debug_buf + len - left;
} else {
bcopy((caddr_t)sol_ofs_print_buf,
(caddr_t)sol_ofs_debug_sptr, len);
sol_ofs_debug_sptr += len;
}
}
/*
* L5-L2 message may go to the sol_ofs_debug_buf
* L1 messages will go to the log buf in non-debug kernels and
* to console and log buf in debug kernels
* L0 messages are warnings and will go to console and log buf
*/
switch (level) {
case SOL_OFS_LOG_L5:
case SOL_OFS_LOG_L4:
case SOL_OFS_LOG_L3:
case SOL_OFS_LOG_L2:
if (!sol_ofs_buffer_dprintf) {
cmn_err(CE_CONT, "^%s", sol_ofs_print_buf);
}
break;
case SOL_OFS_LOG_L1 :
#ifdef DEBUG
cmn_err(CE_CONT, "%s", sol_ofs_print_buf);
#else
if (!sol_ofs_buffer_dprintf) {
cmn_err(CE_CONT, "^%s", sol_ofs_print_buf);
}
#endif
break;
case SOL_OFS_LOG_L0 :
/* Strip the "\n" added earlier */
if (sol_ofs_print_buf[len - 1] == '\n') {
sol_ofs_print_buf[len - 1] = '\0';
}
if (msg_ptr[len - 1] == '\n') {
msg_ptr[len - 1] = '\0';
}
cmn_err(CE_WARN, sol_ofs_print_buf);
break;
}
mutex_exit(&sol_ofs_debug_mutex);
}
/* Check individual error levels */
#define SOL_OFS_CHECK_ERR_LEVEL(level) \
if (!(uint_t)strncmp(name, "sol_ucma", 8)) { \
if (sol_ucma_errlevel < level) \
return; \
} else if (!(uint_t)strncmp(name, "sol_rdmacm", 10)) { \
if (sol_rdmacm_errlevel < level) \
return; \
} else if (!(uint_t)strncmp(name, "sol_uverbs", 10)) { \
if (sol_uverbs_errlevel < level) \
return; \
} else if (!(uint_t)strncmp(name, "sol_umad", 8)) { \
if (sol_umad_errlevel < level) \
return; \
} else if (!(uint_t)strncmp(name, "sol_ofs_mod", 12)) { \
if (sol_ofs_module_errlevel < level) \
return; \
} else if (strncmp(name, "sol_kverbs", 10) == 0) { \
if (sol_kverbs_errlevel < level) \
return; \
} else if (sol_of_errlevel < level) \
return;
void
sol_ofs_dprintf_l5(char *name, char *fmt, ...)
{
va_list ap;
if (sol_ofs_supress_above_l2)
return;
SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L5);
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L5, fmt, ap);
va_end(ap);
}
void
sol_ofs_dprintf_l4(char *name, char *fmt, ...)
{
va_list ap;
if (sol_ofs_supress_above_l2)
return;
SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L4);
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L4, fmt, ap);
va_end(ap);
}
void
sol_ofs_dprintf_l3(char *name, char *fmt, ...)
{
va_list ap;
if (sol_ofs_supress_above_l2)
return;
SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L3);
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L3, fmt, ap);
va_end(ap);
}
void
sol_ofs_dprintf_l2(char *name, char *fmt, ...)
{
va_list ap;
SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L2);
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L2, fmt, ap);
va_end(ap);
}
void
sol_ofs_dprintf_l1(char *name, char *fmt, ...)
{
va_list ap;
SOL_OFS_CHECK_ERR_LEVEL(SOL_OFS_LOG_L1);
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L1, fmt, ap);
va_end(ap);
}
void
sol_ofs_dprintf_l0(char *name, char *fmt, ...)
{
va_list ap;
if (sol_of_errlevel < SOL_OFS_LOG_L0)
return;
va_start(ap, fmt);
sol_ofs_dprintf_vlog(name, SOL_OFS_LOG_L1, fmt, ap);
va_end(ap);
}