/* * scst_debug.c * * Copyright (C) 2004 - 2018 Vladislav Bolkhovitin * Copyright (C) 2004 - 2005 Leonid Stoljar * Copyright (C) 2007 - 2018 Western Digital Corporation * * Contains helper functions for execution tracing and error reporting. * Intended to be included in main .c file. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef INSIDE_KERNEL_TREE #include #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) #include #endif #ifdef INSIDE_KERNEL_TREE #include #include #else #include "scst.h" #include "scst_debug.h" #endif #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #define TRACE_BUF_SIZE 512 static char trace_buf[TRACE_BUF_SIZE]; static DEFINE_SPINLOCK(trace_buf_lock); static inline int get_current_tid(void) { /* Code should be the same as in sys_gettid() */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) return current->pid; #else if (in_interrupt()) { /* * Unfortunately, task_pid_vnr() isn't IRQ-safe, so otherwise * it can oops. ToDo. */ return current->pid; } return task_pid_vnr(current); #endif } /* * debug_print_with_prefix() - prints a debug message * * Adds, if requested by trace_flag, debug prefix in the beginning */ int debug_print_with_prefix(unsigned long trace_flag, const char *severity, const char *prefix, const char *func, int line, const char *fmt, ...) { int i; unsigned long flags; int pid = get_current_tid(); va_list args; spin_lock_irqsave(&trace_buf_lock, flags); strlcpy(trace_buf, severity, TRACE_BUF_SIZE); i = strlen(trace_buf); if (trace_flag & TRACE_PID) i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ", pid); if (prefix != NULL) i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s: ", prefix); if (trace_flag & TRACE_FUNCTION) i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func); if (trace_flag & TRACE_LINE) i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line); i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s\n", fmt); va_start(args, fmt); vprintk(trace_buf, args); va_end(args); spin_unlock_irqrestore(&trace_buf_lock, flags); return i; } EXPORT_SYMBOL(debug_print_with_prefix); /* * debug_print_buffer() - print a buffer * * Prints in the log data from the buffer */ void debug_print_buffer(const void *data, int len) { int z, z1, i; const unsigned char *buf = (const unsigned char *) data; unsigned long flags; if (buf == NULL) return; spin_lock_irqsave(&trace_buf_lock, flags); PRINT(KERN_INFO, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F"); for (z = 0, z1 = 0, i = 0; z < len; z++) { if (z % 16 == 0) { if (z != 0) { i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " "); for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) { if ((buf[z1] >= 0x20) && (buf[z1] < 0x80)) trace_buf[i++] = buf[z1]; else trace_buf[i++] = '.'; } trace_buf[i] = '\0'; PRINT(KERN_INFO, "%s", trace_buf); i = 0; } i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%4x: ", z); } i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ", buf[z]); } i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " "); for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) { if ((buf[z1] > 0x20) && (buf[z1] < 0x80)) trace_buf[i++] = buf[z1]; else trace_buf[i++] = '.'; } trace_buf[i] = '\0'; PRINT(KERN_INFO, "%s", trace_buf); spin_unlock_irqrestore(&trace_buf_lock, flags); return; } EXPORT_SYMBOL(debug_print_buffer); /* * This function converts transport_id in a string form into internal per-CPU * static buffer. This buffer isn't anyhow protected, because it's acceptable * if the name corrupted in the debug logs because of the race for this buffer. * * Note! You can't call this function 2 or more times in a single logging * (printk) statement, because then each new call of this function will override * data written in this buffer by the previous call. You should instead split * that logging statement on smaller statements each calling * debug_transport_id_to_initiator_name() only once. */ const char *debug_transport_id_to_initiator_name(const uint8_t *transport_id) { /* * No external protection, because it's acceptable if the name * corrupted in the debug logs because of the race for this * buffer. */ #define SIZEOF_NAME_BUF 256 static char name_bufs[NR_CPUS][SIZEOF_NAME_BUF]; char *name_buf; unsigned long flags; sBUG_ON(transport_id == NULL); /* better to catch it not under lock */ spin_lock_irqsave(&trace_buf_lock, flags); name_buf = name_bufs[smp_processor_id()]; /* * To prevent external racing with us users from accidentally * missing their NULL terminator. */ memset(name_buf, 0, SIZEOF_NAME_BUF); smp_mb(); switch (transport_id[0] & 0x0f) { case SCSI_TRANSPORTID_PROTOCOLID_ISCSI: scnprintf(name_buf, SIZEOF_NAME_BUF, "%s", &transport_id[4]); break; case SCSI_TRANSPORTID_PROTOCOLID_FCP2: scnprintf(name_buf, SIZEOF_NAME_BUF, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", transport_id[8], transport_id[9], transport_id[10], transport_id[11], transport_id[12], transport_id[13], transport_id[14], transport_id[15]); break; case SCSI_TRANSPORTID_PROTOCOLID_SPI5: scnprintf(name_buf, SIZEOF_NAME_BUF, "%x:%x", be16_to_cpu((__force __be16)transport_id[2]), be16_to_cpu((__force __be16)transport_id[6])); break; case SCSI_TRANSPORTID_PROTOCOLID_SRP: scnprintf(name_buf, SIZEOF_NAME_BUF, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" ":%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", transport_id[8], transport_id[9], transport_id[10], transport_id[11], transport_id[12], transport_id[13], transport_id[14], transport_id[15], transport_id[16], transport_id[17], transport_id[18], transport_id[19], transport_id[20], transport_id[21], transport_id[22], transport_id[23]); break; case SCSI_TRANSPORTID_PROTOCOLID_SAS: scnprintf(name_buf, SIZEOF_NAME_BUF, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", transport_id[4], transport_id[5], transport_id[6], transport_id[7], transport_id[8], transport_id[9], transport_id[10], transport_id[11]); break; case SCST_TRANSPORTID_PROTOCOLID_COPY_MGR: scnprintf(name_buf, SIZEOF_NAME_BUF, "%s", &transport_id[2]); break; default: scnprintf(name_buf, SIZEOF_NAME_BUF, "(Not known protocol ID %x)", transport_id[0] & 0x0f); break; } spin_unlock_irqrestore(&trace_buf_lock, flags); return name_buf; #undef SIZEOF_NAME_BUF } #endif /* CONFIG_SCST_DEBUG || CONFIG_SCST_TRACING */