1 /* 2 * linux/drivers/scsi/esas2r/esas2r_log.c 3 * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers 4 * 5 * Copyright (c) 2001-2013 ATTO Technology, Inc. 6 * (mailto:linuxdrivers@attotech.com) 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 2 11 * of the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * NO WARRANTY 19 * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR 20 * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT 21 * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 22 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is 23 * solely responsible for determining the appropriateness of using and 24 * distributing the Program and assumes all risks associated with its 25 * exercise of rights under this Agreement, including but not limited to 26 * the risks and costs of program errors, damage to or loss of data, 27 * programs or equipment, and unavailability or interruption of operations. 28 * 29 * DISCLAIMER OF LIABILITY 30 * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY 31 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND 33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 35 * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 36 * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES 37 * 38 * You should have received a copy of the GNU General Public License 39 * along with this program; if not, write to the Free Software 40 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 41 * USA. 42 */ 43 44 #include "esas2r.h" 45 46 /* 47 * this module within the driver is tasked with providing logging functionality. 48 * the event_log_level module parameter controls the level of messages that are 49 * written to the system log. the default level of messages that are written 50 * are critical and warning messages. if other types of messages are desired, 51 * one simply needs to load the module with the correct value for the 52 * event_log_level module parameter. for example: 53 * 54 * insmod <module> event_log_level=1 55 * 56 * will load the module and only critical events will be written by this module 57 * to the system log. if critical, warning, and information-level messages are 58 * desired, the correct value for the event_log_level module parameter 59 * would be as follows: 60 * 61 * insmod <module> event_log_level=3 62 */ 63 64 #define EVENT_LOG_BUFF_SIZE 1024 65 66 static long event_log_level = ESAS2R_LOG_DFLT; 67 68 module_param(event_log_level, long, S_IRUGO | S_IRUSR); 69 MODULE_PARM_DESC(event_log_level, 70 "Specifies the level of events to report to the system log. Critical and warning level events are logged by default."); 71 72 /* A shared buffer to use for formatting messages. */ 73 static char event_buffer[EVENT_LOG_BUFF_SIZE]; 74 75 /* A lock to protect the shared buffer used for formatting messages. */ 76 static DEFINE_SPINLOCK(event_buffer_lock); 77 78 /* 79 * translates an esas2r-defined logging event level to a kernel logging level. 80 * 81 * @param [in] level the esas2r-defined logging event level to translate 82 * 83 * @return the corresponding kernel logging level. 84 */ 85 static const char *translate_esas2r_event_level_to_kernel(const long level) 86 { 87 switch (level) { 88 case ESAS2R_LOG_CRIT: 89 return KERN_CRIT; 90 91 case ESAS2R_LOG_WARN: 92 return KERN_WARNING; 93 94 case ESAS2R_LOG_INFO: 95 return KERN_INFO; 96 97 case ESAS2R_LOG_DEBG: 98 case ESAS2R_LOG_TRCE: 99 default: 100 return KERN_DEBUG; 101 } 102 } 103 104 #pragma GCC diagnostic push 105 #ifndef __clang__ 106 #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" 107 #endif 108 109 /* 110 * the master logging function. this function will format the message as 111 * outlined by the formatting string, the input device information and the 112 * substitution arguments and output the resulting string to the system log. 113 * 114 * @param [in] level the event log level of the message 115 * @param [in] dev the device information 116 * @param [in] format the formatting string for the message 117 * @param [in] args the substition arguments to the formatting string 118 * 119 * @return 0 on success, or -1 if an error occurred. 120 */ 121 static int esas2r_log_master(const long level, 122 const struct device *dev, 123 const char *format, 124 va_list args) 125 { 126 if (level <= event_log_level) { 127 unsigned long flags = 0; 128 int retval = 0; 129 char *buffer = event_buffer; 130 size_t buflen = EVENT_LOG_BUFF_SIZE; 131 const char *fmt_nodev = "%s%s: "; 132 const char *fmt_dev = "%s%s [%s, %s, %s]"; 133 const char *slevel = 134 translate_esas2r_event_level_to_kernel(level); 135 136 spin_lock_irqsave(&event_buffer_lock, flags); 137 138 memset(buffer, 0, buflen); 139 140 /* 141 * format the level onto the beginning of the string and do 142 * some pointer arithmetic to move the pointer to the point 143 * where the actual message can be inserted. 144 */ 145 146 if (dev == NULL) { 147 snprintf(buffer, buflen, fmt_nodev, slevel, 148 ESAS2R_DRVR_NAME); 149 } else { 150 snprintf(buffer, buflen, fmt_dev, slevel, 151 ESAS2R_DRVR_NAME, 152 (dev->driver ? dev->driver->name : "unknown"), 153 (dev->bus ? dev->bus->name : "unknown"), 154 dev_name(dev)); 155 } 156 157 buffer += strlen(event_buffer); 158 buflen -= strlen(event_buffer); 159 160 retval = vsnprintf(buffer, buflen, format, args); 161 if (retval < 0) { 162 spin_unlock_irqrestore(&event_buffer_lock, flags); 163 return -1; 164 } 165 166 /* 167 * Put a line break at the end of the formatted string so that 168 * we don't wind up with run-on messages. 169 */ 170 printk("%s\n", event_buffer); 171 172 spin_unlock_irqrestore(&event_buffer_lock, flags); 173 } 174 175 return 0; 176 } 177 178 #pragma GCC diagnostic pop 179 180 /* 181 * formats and logs a message to the system log. 182 * 183 * @param [in] level the event level of the message 184 * @param [in] format the formating string for the message 185 * @param [in] ... the substitution arguments to the formatting string 186 * 187 * @return 0 on success, or -1 if an error occurred. 188 */ 189 int esas2r_log(const long level, const char *format, ...) 190 { 191 int retval = 0; 192 va_list args; 193 194 va_start(args, format); 195 196 retval = esas2r_log_master(level, NULL, format, args); 197 198 va_end(args); 199 200 return retval; 201 } 202 203 /* 204 * formats and logs a message to the system log. this message will include 205 * device information. 206 * 207 * @param [in] level the event level of the message 208 * @param [in] dev the device information 209 * @param [in] format the formatting string for the message 210 * @param [in] ... the substitution arguments to the formatting string 211 * 212 * @return 0 on success, or -1 if an error occurred. 213 */ 214 int esas2r_log_dev(const long level, 215 const struct device *dev, 216 const char *format, 217 ...) 218 { 219 int retval = 0; 220 va_list args; 221 222 va_start(args, format); 223 224 retval = esas2r_log_master(level, dev, format, args); 225 226 va_end(args); 227 228 return retval; 229 } 230 231 /* 232 * formats and logs a message to the system log. this message will include 233 * device information. 234 * 235 * @param [in] level the event level of the message 236 * @param [in] buf 237 * @param [in] len 238 * 239 * @return 0 on success, or -1 if an error occurred. 240 */ 241 int esas2r_log_hexdump(const long level, 242 const void *buf, 243 size_t len) 244 { 245 if (level <= event_log_level) { 246 print_hex_dump(translate_esas2r_event_level_to_kernel(level), 247 "", DUMP_PREFIX_OFFSET, 16, 1, buf, 248 len, true); 249 } 250 251 return 1; 252 } 253