#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/time.h>
#include <sys/ib/clients/rdsv3/rdsv3_debug.h>
#define RDSV3_DEBUG_SIZE_EXTRA_ALLOC 8
#define RDSV3_MIN_DEBUG_BUF_SIZE 0x1000
#define RDSV3_FUNCNAME_LEN 40
#define RDSV3_PRINTBUF_LEN 4096
#ifdef DEBUG
#define RDSV3_DEBUG_BUF_SIZE 0x200000
#else
#define RDSV3_DEBUG_BUF_SIZE 0x2000
#endif
#define RDSV3_PRINT_BUF_LEN 4096
static int rdsv3_suppress_dprintf;
static int rdsv3_buffer_dprintf = 1;
static int rdsv3_debug_buf_size = RDSV3_DEBUG_BUF_SIZE;
static int rdsv3_allow_intr_msgs = 0;
char *rdsv3_debug_buf = NULL;
char *rdsv3_buf_sptr, *rdsv3_buf_eptr;
int rdsv3_clear_debug_buf_flag = 0;
uint_t rdsv3dbglvl = RDSV3_LOG_L4;
static kmutex_t rdsv3_debug_mutex;
static char rdsv3_print_buf[RDSV3_PRINT_BUF_LEN];
static void rdsv3_clear_print_buf();
void
rdsv3_logging_initialization()
{
boolean_t flag = B_FALSE;
mutex_init(&rdsv3_debug_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_enter(&rdsv3_debug_mutex);
if (rdsv3_debug_buf_size <= RDSV3_DEBUG_SIZE_EXTRA_ALLOC) {
rdsv3_debug_buf_size = RDSV3_MIN_DEBUG_BUF_SIZE;
flag = B_TRUE;
}
rdsv3_debug_buf_size = max(RDSV3_MIN_DEBUG_BUF_SIZE,
rdsv3_debug_buf_size);
rdsv3_debug_buf = (char *)kmem_alloc(rdsv3_debug_buf_size, KM_SLEEP);
rdsv3_clear_print_buf();
mutex_exit(&rdsv3_debug_mutex);
if (flag == B_TRUE) {
RDSV3_DPRINTF2("RDS", "rdsv3_debug_buf_size was too small, "
"adjusted to %x", rdsv3_debug_buf_size);
}
}
void
rdsv3_logging_destroy()
{
mutex_enter(&rdsv3_debug_mutex);
if (rdsv3_debug_buf) {
kmem_free(rdsv3_debug_buf, rdsv3_debug_buf_size);
rdsv3_debug_buf = NULL;
}
mutex_exit(&rdsv3_debug_mutex);
mutex_destroy(&rdsv3_debug_mutex);
}
static void
rdsv3_clear_print_buf()
{
ASSERT(MUTEX_HELD(&rdsv3_debug_mutex));
if (rdsv3_debug_buf) {
rdsv3_buf_sptr = rdsv3_debug_buf;
rdsv3_buf_eptr = rdsv3_debug_buf + rdsv3_debug_buf_size -
RDSV3_DEBUG_SIZE_EXTRA_ALLOC;
bzero(rdsv3_debug_buf, rdsv3_debug_buf_size);
}
}
static void
rdsv3_vlog(char *name, uint_t level, char *fmt, va_list ap)
{
char *label = (name == NULL) ? "rds" : name;
char *msg_ptr;
size_t len;
mutex_enter(&rdsv3_debug_mutex);
if (rdsv3_suppress_dprintf || (rdsv3_debug_buf == NULL)) {
mutex_exit(&rdsv3_debug_mutex);
return;
}
if (rdsv3_clear_debug_buf_flag != 0) {
rdsv3_clear_print_buf();
rdsv3_clear_debug_buf_flag = 0;
}
len = snprintf(rdsv3_print_buf, RDSV3_FUNCNAME_LEN, "%s:\t", label);
msg_ptr = rdsv3_print_buf + len;
len += vsnprintf(msg_ptr, RDSV3_PRINT_BUF_LEN - len - 2, fmt, ap);
len = min(len, RDSV3_PRINT_BUF_LEN - 2);
ASSERT(len == strlen(rdsv3_print_buf));
rdsv3_print_buf[len++] = '\n';
rdsv3_print_buf[len] = '\0';
if (rdsv3_buffer_dprintf) {
*rdsv3_buf_sptr = '\0';
if (rdsv3_buf_sptr + len > rdsv3_buf_eptr) {
size_t left = (uintptr_t)rdsv3_buf_eptr -
(uintptr_t)rdsv3_buf_sptr;
bcopy((caddr_t)rdsv3_print_buf,
(caddr_t)rdsv3_buf_sptr, left);
bcopy((caddr_t)rdsv3_print_buf + left,
(caddr_t)rdsv3_debug_buf, len - left);
rdsv3_buf_sptr = rdsv3_debug_buf + len - left;
} else {
bcopy((caddr_t)rdsv3_print_buf, rdsv3_buf_sptr, len);
rdsv3_buf_sptr += len;
}
(void) sprintf(rdsv3_buf_sptr, ">>>>");
}
switch (level) {
case RDSV3_LOG_LINTR:
case RDSV3_LOG_L5:
case RDSV3_LOG_L4:
case RDSV3_LOG_L3:
case RDSV3_LOG_L2:
if (!rdsv3_buffer_dprintf) {
cmn_err(CE_CONT, "^%s", rdsv3_print_buf);
}
break;
case RDSV3_LOG_L1:
if (!rdsv3_buffer_dprintf) {
cmn_err(CE_CONT, "^%s", rdsv3_print_buf);
} else {
cmn_err(CE_CONT, "!%s", rdsv3_print_buf);
}
break;
case RDSV3_LOG_L0:
if (rdsv3_print_buf[len - 1] == '\n') {
rdsv3_print_buf[len - 1] = '\0';
}
if (msg_ptr[len - 1] == '\n') {
msg_ptr[len - 1] = '\0';
}
cmn_err(CE_CONT, "^%s", rdsv3_print_buf);
break;
}
mutex_exit(&rdsv3_debug_mutex);
}
void
rdsv3_dprintf_intr(char *name, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_LINTR, fmt, ap);
va_end(ap);
}
#define RDSV3_CHECK_ERR_LEVEL(level) \
if (rdsv3dbglvl < level) \
return; \
void
rdsv3_dprintf5(char *name, char *fmt, ...)
{
va_list ap;
RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L5);
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L5, fmt, ap);
va_end(ap);
}
void
rdsv3_dprintf4(char *name, char *fmt, ...)
{
va_list ap;
RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L4);
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L4, fmt, ap);
va_end(ap);
}
void
rdsv3_dprintf3(char *name, char *fmt, ...)
{
va_list ap;
RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L3);
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L3, fmt, ap);
va_end(ap);
}
void
rdsv3_dprintf2(char *name, char *fmt, ...)
{
va_list ap;
RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L2);
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L2, fmt, ap);
va_end(ap);
}
void
rdsv3_dprintf1(char *name, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L1, fmt, ap);
va_end(ap);
}
void
rdsv3_dprintf0(char *name, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
rdsv3_vlog(name, RDSV3_LOG_L0, fmt, ap);
va_end(ap);
}
void
rdsv3_trace(char *name, uint8_t lvl, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
rdsv3_vlog(name, lvl, fmt, ap);
va_end(ap);
}
#define DEFAULT_RATELIMIT_INTERVAL 5
#define DEFAULT_RATELIMIT_BURST 10
struct ratelimit_state {
clock_t interval;
int burst;
int printed;
int missed;
hrtime_t begin;
kmutex_t lock;
};
#define DEFINE_RATELIMIT_STATE(name, interval, burst) \
static struct ratelimit_state name = {interval, burst, }
DEFINE_RATELIMIT_STATE(rdsv3_printk_ratelimit_state,
DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
int
rdsv3_printk_ratelimit(void)
{
struct ratelimit_state *rs = &rdsv3_printk_ratelimit_state;
hrtime_t current = gethrtime();
int rtn = 0;
if (rs->interval) {
return (1);
}
mutex_enter(&rs->lock);
if (!rs->begin) {
rs->begin = current;
}
if (current < rs->begin + TICK_TO_NSEC(rs->interval)) {
if (rs->missed) {
RDSV3_DPRINTF0("rdsv3_printk_ratelimit: ",
"%d callbacks suppressed\n", rs->missed);
rs->begin = 0;
rs->printed = 0;
rs->missed = 0;
}
}
if (rs->burst && rs->burst > rs->printed) {
rs->printed++;
rtn = 1;
} else {
rs->missed++;
}
mutex_exit(&rs->lock);
return (rtn);
}