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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 #include <sys/types.h> 25 #include <sys/varargs.h> 26 #include <sys/cmn_err.h> 27 #include <sys/ddi.h> 28 #include <sys/sunddi.h> 29 #include <sys/time.h> 30 #include <sys/ib/clients/rdsv3/rdsv3_debug.h> 31 32 /* 33 * This file contains the debug defines and routines. 34 * Debugging information is collected in a circular kernel buffer. Debug 35 * messages with level lower than rdsv3dbglvl are ignored. The size of the 36 * of the debug buffer can be changed by setting 'rdsv3_debug_buf_size' in 37 * bytes in /etc/system. 38 * 39 * The debug buffer can be cleared by setting 'rdsv3_clear_debug_buf_flag = 1' 40 * on a running system. 41 */ 42 43 #define RDSV3_DEBUG_SIZE_EXTRA_ALLOC 8 44 #define RDSV3_MIN_DEBUG_BUF_SIZE 0x1000 45 #define RDSV3_FUNCNAME_LEN 40 46 #define RDSV3_PRINTBUF_LEN 4096 47 #ifdef DEBUG 48 #define RDSV3_DEBUG_BUF_SIZE 0x200000 /* 2M size */ 49 #else 50 #define RDSV3_DEBUG_BUF_SIZE 0x2000 51 #endif /* DEBUG */ 52 53 /* Max length of a debug statement */ 54 #define RDSV3_PRINT_BUF_LEN 4096 55 56 static int rdsv3_suppress_dprintf; /* Suppress debug printing */ 57 static int rdsv3_buffer_dprintf = 1; /* Use debug buffer (0 == console) */ 58 static int rdsv3_debug_buf_size = RDSV3_DEBUG_BUF_SIZE; /* Sz of Debug buf */ 59 static int rdsv3_allow_intr_msgs = 0; /* log "intr" messages */ 60 char *rdsv3_debug_buf = NULL; /* The Debug Buf */ 61 char *rdsv3_buf_sptr, *rdsv3_buf_eptr; /* debug buffer temp pointer */ 62 int rdsv3_clear_debug_buf_flag = 0; /* Clear debug buffer */ 63 uint_t rdsv3dbglvl = RDSV3_LOG_L4; 64 65 /* 66 * Print Buffer protected by mutex for debug stuff. The mutex also 67 * ensures serializing debug messages. 68 */ 69 static kmutex_t rdsv3_debug_mutex; 70 static char rdsv3_print_buf[RDSV3_PRINT_BUF_LEN]; 71 72 /* Function Prototypes */ 73 static void rdsv3_clear_print_buf(); 74 75 /* RDS logging init */ 76 void 77 rdsv3_logging_initialization() 78 { 79 boolean_t flag = B_FALSE; 80 81 mutex_init(&rdsv3_debug_mutex, NULL, MUTEX_DRIVER, NULL); 82 mutex_enter(&rdsv3_debug_mutex); 83 84 if (rdsv3_debug_buf_size <= RDSV3_DEBUG_SIZE_EXTRA_ALLOC) { 85 rdsv3_debug_buf_size = RDSV3_MIN_DEBUG_BUF_SIZE; 86 flag = B_TRUE; 87 } 88 89 /* if it is less that RDSV3_MIN_DEBUG_BUF_SIZE, adjust it */ 90 rdsv3_debug_buf_size = max(RDSV3_MIN_DEBUG_BUF_SIZE, 91 rdsv3_debug_buf_size); 92 93 rdsv3_debug_buf = (char *)kmem_alloc(rdsv3_debug_buf_size, KM_SLEEP); 94 rdsv3_clear_print_buf(); 95 mutex_exit(&rdsv3_debug_mutex); 96 97 if (flag == B_TRUE) { 98 RDSV3_DPRINTF2("RDS", "rdsv3_debug_buf_size was too small, " 99 "adjusted to %x", rdsv3_debug_buf_size); 100 } 101 } 102 103 104 /* RDS logging destroy */ 105 void 106 rdsv3_logging_destroy() 107 { 108 mutex_enter(&rdsv3_debug_mutex); 109 if (rdsv3_debug_buf) { 110 kmem_free(rdsv3_debug_buf, rdsv3_debug_buf_size); 111 rdsv3_debug_buf = NULL; 112 } 113 mutex_exit(&rdsv3_debug_mutex); 114 mutex_destroy(&rdsv3_debug_mutex); 115 } 116 117 118 /* 119 * debug, log, and console message handling 120 */ 121 122 /* 123 * clear the RDS debug buffer 124 */ 125 static void 126 rdsv3_clear_print_buf() 127 { 128 ASSERT(MUTEX_HELD(&rdsv3_debug_mutex)); 129 if (rdsv3_debug_buf) { 130 rdsv3_buf_sptr = rdsv3_debug_buf; 131 rdsv3_buf_eptr = rdsv3_debug_buf + rdsv3_debug_buf_size - 132 RDSV3_DEBUG_SIZE_EXTRA_ALLOC; 133 134 bzero(rdsv3_debug_buf, rdsv3_debug_buf_size); 135 } 136 } 137 138 139 static void 140 rdsv3_vlog(char *name, uint_t level, char *fmt, va_list ap) 141 { 142 char *label = (name == NULL) ? "rds" : name; 143 char *msg_ptr; 144 size_t len; 145 146 mutex_enter(&rdsv3_debug_mutex); 147 148 /* if not using logging scheme; quit */ 149 if (rdsv3_suppress_dprintf || (rdsv3_debug_buf == NULL)) { 150 mutex_exit(&rdsv3_debug_mutex); 151 return; 152 } 153 154 /* If user requests to clear debug buffer, go ahead */ 155 if (rdsv3_clear_debug_buf_flag != 0) { 156 rdsv3_clear_print_buf(); 157 rdsv3_clear_debug_buf_flag = 0; 158 } 159 160 /* 161 * put "label" into the buffer 162 */ 163 len = snprintf(rdsv3_print_buf, RDSV3_FUNCNAME_LEN, "%s:\t", label); 164 165 msg_ptr = rdsv3_print_buf + len; 166 len += vsnprintf(msg_ptr, RDSV3_PRINT_BUF_LEN - len - 2, fmt, ap); 167 168 len = min(len, RDSV3_PRINT_BUF_LEN - 2); 169 ASSERT(len == strlen(rdsv3_print_buf)); 170 rdsv3_print_buf[len++] = '\n'; 171 rdsv3_print_buf[len] = '\0'; 172 173 /* 174 * stuff the message in the debug buf 175 */ 176 if (rdsv3_buffer_dprintf) { 177 178 /* 179 * overwrite >>>> that might be over the end of the 180 * the buffer 181 */ 182 *rdsv3_buf_sptr = '\0'; 183 184 if (rdsv3_buf_sptr + len > rdsv3_buf_eptr) { 185 size_t left = (uintptr_t)rdsv3_buf_eptr - 186 (uintptr_t)rdsv3_buf_sptr; 187 188 bcopy((caddr_t)rdsv3_print_buf, 189 (caddr_t)rdsv3_buf_sptr, left); 190 bcopy((caddr_t)rdsv3_print_buf + left, 191 (caddr_t)rdsv3_debug_buf, len - left); 192 rdsv3_buf_sptr = rdsv3_debug_buf + len - left; 193 } else { 194 bcopy((caddr_t)rdsv3_print_buf, rdsv3_buf_sptr, len); 195 rdsv3_buf_sptr += len; 196 } 197 198 /* add marker */ 199 (void) sprintf(rdsv3_buf_sptr, ">>>>"); 200 } 201 202 /* 203 * LINTR, L5-L2 message may go to the rdsv3_debug_buf 204 * L1 messages will go to the /var/adm/messages (debug & non-debug). 205 * L0 messages will go to console (debug & non-debug). 206 */ 207 switch (level) { 208 case RDSV3_LOG_LINTR: 209 case RDSV3_LOG_L5: 210 case RDSV3_LOG_L4: 211 case RDSV3_LOG_L3: 212 case RDSV3_LOG_L2: 213 if (!rdsv3_buffer_dprintf) { 214 cmn_err(CE_CONT, "^%s", rdsv3_print_buf); 215 } 216 break; 217 case RDSV3_LOG_L1: 218 if (!rdsv3_buffer_dprintf) { 219 cmn_err(CE_CONT, "^%s", rdsv3_print_buf); 220 } else { 221 /* go to messages file */ 222 cmn_err(CE_CONT, "!%s", rdsv3_print_buf); 223 } 224 break; 225 case RDSV3_LOG_L0: 226 /* Strip the "\n" added earlier */ 227 if (rdsv3_print_buf[len - 1] == '\n') { 228 rdsv3_print_buf[len - 1] = '\0'; 229 } 230 if (msg_ptr[len - 1] == '\n') { 231 msg_ptr[len - 1] = '\0'; 232 } 233 /* go to console */ 234 cmn_err(CE_CONT, "^%s", rdsv3_print_buf); 235 break; 236 } 237 238 mutex_exit(&rdsv3_debug_mutex); 239 } 240 241 void 242 rdsv3_dprintf_intr(char *name, char *fmt, ...) 243 { 244 va_list ap; 245 246 va_start(ap, fmt); 247 rdsv3_vlog(name, RDSV3_LOG_LINTR, fmt, ap); 248 va_end(ap); 249 } 250 251 /* 252 * Check individual subsystem err levels 253 */ 254 #define RDSV3_CHECK_ERR_LEVEL(level) \ 255 if (rdsv3dbglvl < level) \ 256 return; \ 257 258 void 259 rdsv3_dprintf5(char *name, char *fmt, ...) 260 { 261 va_list ap; 262 263 RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L5); 264 265 va_start(ap, fmt); 266 rdsv3_vlog(name, RDSV3_LOG_L5, fmt, ap); 267 va_end(ap); 268 } 269 270 void 271 rdsv3_dprintf4(char *name, char *fmt, ...) 272 { 273 va_list ap; 274 275 RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L4); 276 277 va_start(ap, fmt); 278 rdsv3_vlog(name, RDSV3_LOG_L4, fmt, ap); 279 va_end(ap); 280 } 281 282 void 283 rdsv3_dprintf3(char *name, char *fmt, ...) 284 { 285 va_list ap; 286 287 RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L3); 288 289 va_start(ap, fmt); 290 rdsv3_vlog(name, RDSV3_LOG_L3, fmt, ap); 291 va_end(ap); 292 } 293 294 void 295 rdsv3_dprintf2(char *name, char *fmt, ...) 296 { 297 va_list ap; 298 299 RDSV3_CHECK_ERR_LEVEL(RDSV3_LOG_L2); 300 301 va_start(ap, fmt); 302 rdsv3_vlog(name, RDSV3_LOG_L2, fmt, ap); 303 va_end(ap); 304 } 305 306 void 307 rdsv3_dprintf1(char *name, char *fmt, ...) 308 { 309 va_list ap; 310 311 va_start(ap, fmt); 312 rdsv3_vlog(name, RDSV3_LOG_L1, fmt, ap); 313 va_end(ap); 314 } 315 316 317 /* 318 * Function: 319 * rdsv3_dprintf0 320 * Input: 321 * name - Name of the function generating the debug message 322 * fmt - The message to be displayed. 323 * Output: 324 * none 325 * Returns: 326 * none 327 * Description: 328 * A generic log function to display RDS debug messages. 329 */ 330 void 331 rdsv3_dprintf0(char *name, char *fmt, ...) 332 { 333 va_list ap; 334 335 va_start(ap, fmt); 336 rdsv3_vlog(name, RDSV3_LOG_L0, fmt, ap); 337 va_end(ap); 338 } 339 340 /* For ofed rdstrace */ 341 void 342 rdsv3_trace(char *name, uint8_t lvl, char *fmt, ...) 343 { 344 va_list ap; 345 346 va_start(ap, fmt); 347 rdsv3_vlog(name, lvl, fmt, ap); 348 va_end(ap); 349 } 350 351 #define DEFAULT_RATELIMIT_INTERVAL 5 352 #define DEFAULT_RATELIMIT_BURST 10 353 354 struct ratelimit_state { 355 clock_t interval; 356 int burst; 357 int printed; 358 int missed; 359 hrtime_t begin; 360 kmutex_t lock; 361 }; 362 363 #define DEFINE_RATELIMIT_STATE(name, interval, burst) \ 364 static struct ratelimit_state name = {interval, burst, } 365 366 DEFINE_RATELIMIT_STATE(rdsv3_printk_ratelimit_state, 367 DEFAULT_RATELIMIT_INTERVAL, 368 DEFAULT_RATELIMIT_BURST); 369 370 int 371 rdsv3_printk_ratelimit(void) 372 { 373 struct ratelimit_state *rs = &rdsv3_printk_ratelimit_state; 374 hrtime_t current = gethrtime(); 375 int rtn = 0; 376 377 if (rs->interval) { 378 return (1); 379 } 380 mutex_enter(&rs->lock); 381 if (!rs->begin) { 382 rs->begin = current; 383 } 384 if (current < rs->begin + TICK_TO_NSEC(rs->interval)) { 385 if (rs->missed) { 386 RDSV3_DPRINTF0("rdsv3_printk_ratelimit: ", 387 "%d callbacks suppressed\n", rs->missed); 388 rs->begin = 0; 389 rs->printed = 0; 390 rs->missed = 0; 391 } 392 } 393 if (rs->burst && rs->burst > rs->printed) { 394 rs->printed++; 395 rtn = 1; 396 } else { 397 rs->missed++; 398 } 399 mutex_exit(&rs->lock); 400 return (rtn); 401 } 402