/* * 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 * http://www.opensource.org/licenses/cddl1.txt. * 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) 2004-2012 Emulex. All rights reserved. * Use is subject to license terms. */ #define DEF_MSG_STRUCT /* Needed for emlxs_messages.h in emlxs_msg.h */ #include uint32_t emlxs_log_size = 2048; uint32_t emlxs_log_debugs = 0x7FFFFFFF; uint32_t emlxs_log_notices = 0xFFFFFFFF; uint32_t emlxs_log_warnings = 0xFFFFFFFF; uint32_t emlxs_log_errors = 0xFFFFFFFF; static uint32_t emlxs_msg_log_check(emlxs_port_t *port, emlxs_msg_t *msg); static uint32_t emlxs_msg_print_check(emlxs_port_t *port, emlxs_msg_t *msg); static void emlxs_msg_sprintf(char *buffer, emlxs_msg_entry_t *entry); uint32_t emlxs_msg_log_create(emlxs_hba_t *hba) { emlxs_msg_log_t *log = &LOG; uint32_t size = sizeof (emlxs_msg_entry_t) * emlxs_log_size; ddi_iblock_cookie_t iblock; /* Check if log is already created */ if (log->entry) { cmn_err(CE_WARN, "?%s%d: message log already created. log=%p", DRIVER_NAME, hba->ddiinst, (void *)log); return (0); } /* Clear the log */ bzero(log, sizeof (emlxs_msg_log_t)); /* Allocate the memory needed for the log file */ log->entry = (emlxs_msg_entry_t *)kmem_zalloc(size, KM_SLEEP); /* Initialize */ log->size = emlxs_log_size; log->instance = hba->ddiinst; log->start_time = emlxs_device.log_timestamp; if (!(hba->intr_flags & EMLXS_MSI_ENABLED)) { /* Get the current interrupt block cookie */ (void) ddi_get_iblock_cookie(hba->dip, (uint_t)EMLXS_INUMBER, &iblock); /* Create the log mutex lock */ mutex_init(&log->lock, NULL, MUTEX_DRIVER, (void *)iblock); } #ifdef MSI_SUPPORT else { /* Create the temporary log mutex lock */ mutex_init(&log->lock, NULL, MUTEX_DRIVER, NULL); } #endif return (1); } /* emlxs_msg_log_create() */ void emlxs_msg_lock_reinit(emlxs_hba_t *hba) { emlxs_msg_log_t *log = &LOG; /* Check if log is already destroyed */ if (!log->entry) { cmn_err(CE_WARN, "?%s%d: message log already destroyed. log=%p", DRIVER_NAME, hba->ddiinst, (void *)log); return; } /* Destroy the temporary lock */ mutex_destroy(&log->lock); /* Re-create the log mutex lock */ mutex_init(&log->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(hba->intr_arg)); return; } /* emlxs_msg_lock_reinit() */ void emlxs_msg_log_destroy(emlxs_hba_t *hba) { emlxs_msg_log_t *log = &LOG; uint32_t size; /* Check if log is already destroyed */ if (!log->entry) { cmn_err(CE_WARN, "?%s%d: message log already destroyed. log=%p", DRIVER_NAME, hba->ddiinst, (void *)log); return; } /* Destroy the lock */ mutex_destroy(&log->lock); /* Free the log buffer */ size = sizeof (emlxs_msg_entry_t) * log->size; kmem_free(log->entry, size); /* Clear the log */ bzero(log, sizeof (emlxs_msg_log_t)); return; } /* emlxs_msg_log_destroy() */ uint32_t emlxs_msg_log(emlxs_port_t *port, const uint32_t fileno, const uint32_t line, emlxs_msg_t *msg, char *buffer) { emlxs_hba_t *hba = HBA; emlxs_msg_entry_t *entry; emlxs_msg_entry_t *entry2; clock_t time; emlxs_msg_log_t *log; uint32_t last; emlxs_msg_t *msg2; /* Get the log file for this instance */ log = &LOG; /* Check if log is initialized */ if (log->entry == NULL) { return (0); } mutex_enter(&log->lock); /* Get the pointer to the last log entry */ if (log->next == 0) { last = log->size - 1; } else { last = log->next - 1; } entry = &log->entry[last]; /* Check if this matches the last message */ if ((entry->instance == log->instance) && (entry->vpi == port->vpi) && (entry->fileno == fileno) && (entry->line == line) && (entry->msg == msg) && (strcmp(entry->buffer, buffer) == 0)) { /* If the same message is being logged then increment */ log->repeat++; mutex_exit(&log->lock); return (0); } else if (log->repeat) { /* Get the pointer to the next log entry */ entry2 = &log->entry[log->next]; /* Increment and check the next entry index */ if (++(log->next) >= log->size) { log->next = 0; } switch (entry->msg->level) { case EMLXS_NOTICE: msg2 = &emlxs_notice_msg; break; case EMLXS_WARNING: msg2 = &emlxs_warning_msg; break; case EMLXS_ERROR: msg2 = &emlxs_error_msg; break; case EMLXS_PANIC: msg2 = &emlxs_panic_msg; break; case EMLXS_DEBUG: default: msg2 = &emlxs_debug_msg; break; } /* Initialize */ entry2->id = log->count++; entry2->fileno = entry->fileno; entry2->line = entry->line; entry2->msg = msg2; entry2->instance = log->instance; entry2->vpi = port->vpi; /* Save the additional info buffer */ (void) snprintf(entry2->buffer, MAX_LOG_INFO_LENGTH, "Last message repeated %d time(s).", log->repeat); /* Set the entry time stamp */ (void) drv_getparm(LBOLT, &time); entry2->time = time - log->start_time; gethrestime(&entry2->id_time); log->repeat = 0; } /* Get the pointer to the next log entry */ entry = &log->entry[log->next]; /* Increment and check the next entry index */ if (++(log->next) >= log->size) { log->next = 0; } /* Initialize */ entry->id = log->count++; entry->fileno = fileno; entry->line = line; entry->msg = msg; entry->instance = log->instance; entry->vpi = port->vpi; /* Save the additional info buffer */ (void) strncpy(entry->buffer, buffer, (MAX_LOG_INFO_LENGTH - 1)); entry->buffer[MAX_LOG_INFO_LENGTH - 1] = 0; /* Set the entry time stamp */ (void) drv_getparm(LBOLT, &time); entry->time = time - log->start_time; gethrestime(&entry->id_time); mutex_exit(&log->lock); return (0); } /* emlxs_msg_log() */ /*ARGSUSED*/ static uint32_t emlxs_msg_log_check(emlxs_port_t *port, emlxs_msg_t *msg) { switch (msg->level) { case EMLXS_DEBUG: if (msg->mask & emlxs_log_debugs) { return (1); } break; case EMLXS_NOTICE: if (msg->mask & emlxs_log_notices) { return (1); } break; case EMLXS_WARNING: if (msg->mask & emlxs_log_warnings) { return (1); } break; case EMLXS_ERROR: if (msg->mask & emlxs_log_errors) { return (1); } break; case EMLXS_PANIC: return (1); } return (0); } /* emlxs_msg_log_check() */ static uint32_t emlxs_msg_print_check(emlxs_port_t *port, emlxs_msg_t *msg) { emlxs_hba_t *hba = HBA; emlxs_config_t *cfg; uint32_t rval = 0; cfg = &CFG; switch (msg->level) { case EMLXS_DEBUG: if (msg->mask & cfg[CFG_CONSOLE_DEBUGS].current) { rval |= 2; } if (msg->mask & cfg[CFG_LOG_DEBUGS].current) { rval |= 1; } break; case EMLXS_NOTICE: if (msg->mask & cfg[CFG_CONSOLE_NOTICES].current) { rval |= 2; } if (msg->mask & cfg[CFG_LOG_NOTICES].current) { rval |= 1; } break; case EMLXS_WARNING: if (msg->mask & cfg[CFG_CONSOLE_WARNINGS].current) { rval |= 2; } if (msg->mask & cfg[CFG_LOG_WARNINGS].current) { rval |= 1; } break; case EMLXS_ERROR: if (msg->mask & cfg[CFG_CONSOLE_ERRORS].current) { rval |= 2; } if (msg->mask & cfg[CFG_LOG_ERRORS].current) { rval |= 1; } break; case EMLXS_PANIC: default: rval |= 1; } return (rval); } /* emlxs_msg_print_check() */ void emlxs_msg_printf(emlxs_port_t *port, const uint32_t fileno, const uint32_t line, emlxs_msg_t *msg, const char *fmt, ...) { emlxs_hba_t *hba = HBA; va_list valist; char va_str[256]; char msg_str[512]; char *level; int32_t cmn_level; uint32_t rval; char driver[32]; va_str[0] = 0; if (fmt) { va_start(valist, fmt); (void) vsnprintf(va_str, sizeof (va_str), fmt, valist); va_end(valist); } #ifdef FMA_SUPPORT if (msg->fm_ereport_code) { emlxs_fm_ereport(hba, msg->fm_ereport_code); } if (msg->fm_impact_code) { emlxs_fm_service_impact(hba, msg->fm_impact_code); } #endif /* FMA_SUPPORT */ /* Check if msg should be logged */ if (emlxs_msg_log_check(port, msg)) { /* Log the message */ if (emlxs_msg_log(port, fileno, line, msg, va_str)) { return; } } /* Check if msg should be printed */ if (rval = emlxs_msg_print_check(port, msg)) { cmn_level = CE_CONT; switch (msg->level) { case EMLXS_DEBUG: level = " DEBUG"; break; case EMLXS_NOTICE: level = " NOTICE"; break; case EMLXS_WARNING: level = "WARNING"; break; case EMLXS_ERROR: level = " ERROR"; break; case EMLXS_PANIC: cmn_level = CE_PANIC; level = " PANIC"; break; default: level = "UNKNOWN"; break; } if (port->vpi == 0) { (void) snprintf(driver, sizeof (driver), "%s%d", DRIVER_NAME, hba->ddiinst); } else { (void) snprintf(driver, sizeof (driver), "%s%d.%d", DRIVER_NAME, hba->ddiinst, port->vpi); } /* Generate the message string */ if (msg->buffer[0] != 0) { if (va_str[0] != 0) { (void) snprintf(msg_str, sizeof (msg_str), "[%2X.%04X]%s:%7s:%4d: %s (%s)\n", fileno, line, driver, level, msg->id, msg->buffer, va_str); } else { (void) snprintf(msg_str, sizeof (msg_str), "[%2X.%04X]%s:%7s:%4d: %s\n", fileno, line, driver, level, msg->id, msg->buffer); } } else { if (va_str[0] != 0) { (void) snprintf(msg_str, sizeof (msg_str), "[%2X.%04X]%s:%7s:%4d: (%s)\n", fileno, line, driver, level, msg->id, va_str); } else { (void) snprintf(msg_str, sizeof (msg_str), "[%2X.%04X]%s:%7s:%4d\n", fileno, line, driver, level, msg->id); } } switch (rval) { case 1: /* MESSAGE LOG ONLY */ /* Message log & console, if system booted in */ /* verbose mode (CE_CONT only) */ cmn_err(cmn_level, "?%s", msg_str); break; case 2: /* CONSOLE ONLY */ cmn_err(cmn_level, "^%s", msg_str); break; case 3: /* CONSOLE AND MESSAGE LOG */ cmn_err(cmn_level, "%s", msg_str); break; } } return; } /* emlxs_msg_printf() */ uint32_t emlxs_msg_log_get(emlxs_hba_t *hba, emlxs_log_req_t *req, emlxs_log_resp_t *resp) { emlxs_msg_log_t *log; uint32_t first; uint32_t last; uint32_t count; uint32_t index; uint32_t i; char *resp_buf; log = &LOG; mutex_enter(&log->lock); /* Check if buffer is empty */ if (log->count == 0) { /* If so, exit now */ resp->first = 0; resp->last = 0; resp->count = 0; mutex_exit(&log->lock); return (1); } /* Get current log entry ranges */ /* Get last entry id saved */ last = log->count - 1; /* Check if request is out of current range */ if (req->first > last) { /* if so, exit now */ resp->first = last; resp->last = last; resp->count = 0; mutex_exit(&log->lock); return (0); } /* Get oldest entry id and its index */ /* Check if buffer has already been filled once */ if (log->count >= log->size) { first = log->count - log->size; index = log->next; } else { /* Buffer not yet filled */ first = 0; index = 0; } /* Check if requested first message is greater than actual. */ /* If so, adjust for it. */ if (req->first > first) { /* Adjust entry index to first requested message */ index += (req->first - first); if (index >= log->size) { index -= log->size; } first = req->first; } /* Get the total number of messages available for return */ count = last - first + 1; /* Check if requested count is less than actual. If so, adjust it. */ if (req->count < count) { count = req->count; } /* Fill in the response header */ resp->count = count; resp->first = first; resp->last = last; /* Fill the response buffer */ resp_buf = (char *)resp + sizeof (emlxs_log_resp_t); for (i = 0; i < count; i++) { emlxs_msg_sprintf(resp_buf, &log->entry[index]); /* Increment the response buffer */ resp_buf += MAX_LOG_MSG_LENGTH; /* Increment index */ if (++index >= log->size) { index = 0; } } mutex_exit(&log->lock); return (1); } /* emlxs_msg_log_get() */ static void emlxs_msg_sprintf(char *buffer, emlxs_msg_entry_t *entry) { char *level; emlxs_msg_t *msg; uint32_t secs; uint32_t hsecs; char buf[256]; uint32_t buflen; char driver[32]; msg = entry->msg; hsecs = (entry->time % 100); secs = entry->time / 100; switch (msg->level) { case EMLXS_DEBUG: level = " DEBUG"; break; case EMLXS_NOTICE: level = " NOTICE"; break; case EMLXS_WARNING: level = "WARNING"; break; case EMLXS_ERROR: level = " ERROR"; break; case EMLXS_PANIC: level = " PANIC"; break; default: level = "UNKNOWN"; break; } if (entry->vpi == 0) { (void) snprintf(driver, sizeof (driver), "%s%d", DRIVER_NAME, entry->instance); } else { (void) snprintf(driver, sizeof (driver), "%s%d.%d", DRIVER_NAME, entry->instance, entry->vpi); } /* Generate the message string */ if (msg->buffer[0] != 0) { if (entry->buffer[0] != 0) { (void) snprintf(buf, sizeof (buf), "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: %s (%s)\n", secs, hsecs, entry->id, entry->fileno, entry->line, driver, level, msg->id, msg->buffer, entry->buffer); } else { (void) snprintf(buf, sizeof (buf), "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: %s\n", secs, hsecs, entry->id, entry->fileno, entry->line, driver, level, msg->id, msg->buffer); } } else { if (entry->buffer[0] != 0) { (void) snprintf(buf, sizeof (buf), "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: (%s)\n", secs, hsecs, entry->id, entry->fileno, entry->line, driver, level, msg->id, entry->buffer); } else { (void) snprintf(buf, sizeof (buf), "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d\n", secs, hsecs, entry->id, entry->fileno, entry->line, driver, level, msg->id); } } bzero(buffer, MAX_LOG_MSG_LENGTH); buflen = strlen(buf); if (buflen > (MAX_LOG_MSG_LENGTH - 1)) { (void) strncpy(buffer, buf, (MAX_LOG_MSG_LENGTH - 2)); buffer[MAX_LOG_MSG_LENGTH - 2] = '\n'; } else { (void) strncpy(buffer, buf, buflen); } return; } /* emlxs_msg_sprintf() */