1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * scsi_logging.c 4 * 5 * Copyright (C) 2014 SUSE Linux Products GmbH 6 * Copyright (C) 2014 Hannes Reinecke <hare@suse.de> 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/atomic.h> 11 12 #include <scsi/scsi.h> 13 #include <scsi/scsi_cmnd.h> 14 #include <scsi/scsi_device.h> 15 #include <scsi/scsi_eh.h> 16 #include <scsi/scsi_dbg.h> 17 18 static char *scsi_log_reserve_buffer(size_t *len) 19 { 20 *len = 128; 21 return kmalloc(*len, GFP_ATOMIC); 22 } 23 24 static void scsi_log_release_buffer(char *bufptr) 25 { 26 kfree(bufptr); 27 } 28 29 static inline const char *scmd_name(const struct scsi_cmnd *scmd) 30 { 31 return scmd->request->rq_disk ? 32 scmd->request->rq_disk->disk_name : NULL; 33 } 34 35 static size_t sdev_format_header(char *logbuf, size_t logbuf_len, 36 const char *name, int tag) 37 { 38 size_t off = 0; 39 40 if (name) 41 off += scnprintf(logbuf + off, logbuf_len - off, 42 "[%s] ", name); 43 44 if (WARN_ON(off >= logbuf_len)) 45 return off; 46 47 if (tag >= 0) 48 off += scnprintf(logbuf + off, logbuf_len - off, 49 "tag#%d ", tag); 50 return off; 51 } 52 53 void sdev_prefix_printk(const char *level, const struct scsi_device *sdev, 54 const char *name, const char *fmt, ...) 55 { 56 va_list args; 57 char *logbuf; 58 size_t off = 0, logbuf_len; 59 60 if (!sdev) 61 return; 62 63 logbuf = scsi_log_reserve_buffer(&logbuf_len); 64 if (!logbuf) 65 return; 66 67 if (name) 68 off += scnprintf(logbuf + off, logbuf_len - off, 69 "[%s] ", name); 70 if (!WARN_ON(off >= logbuf_len)) { 71 va_start(args, fmt); 72 off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); 73 va_end(args); 74 } 75 dev_printk(level, &sdev->sdev_gendev, "%s", logbuf); 76 scsi_log_release_buffer(logbuf); 77 } 78 EXPORT_SYMBOL(sdev_prefix_printk); 79 80 void scmd_printk(const char *level, const struct scsi_cmnd *scmd, 81 const char *fmt, ...) 82 { 83 va_list args; 84 char *logbuf; 85 size_t off = 0, logbuf_len; 86 87 if (!scmd || !scmd->cmnd) 88 return; 89 90 logbuf = scsi_log_reserve_buffer(&logbuf_len); 91 if (!logbuf) 92 return; 93 off = sdev_format_header(logbuf, logbuf_len, scmd_name(scmd), 94 scmd->request->tag); 95 if (off < logbuf_len) { 96 va_start(args, fmt); 97 off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args); 98 va_end(args); 99 } 100 dev_printk(level, &scmd->device->sdev_gendev, "%s", logbuf); 101 scsi_log_release_buffer(logbuf); 102 } 103 EXPORT_SYMBOL(scmd_printk); 104 105 static size_t scsi_format_opcode_name(char *buffer, size_t buf_len, 106 const unsigned char *cdbp) 107 { 108 int sa, cdb0; 109 const char *cdb_name = NULL, *sa_name = NULL; 110 size_t off; 111 112 cdb0 = cdbp[0]; 113 if (cdb0 == VARIABLE_LENGTH_CMD) { 114 int len = scsi_varlen_cdb_length(cdbp); 115 116 if (len < 10) { 117 off = scnprintf(buffer, buf_len, 118 "short variable length command, len=%d", 119 len); 120 return off; 121 } 122 sa = (cdbp[8] << 8) + cdbp[9]; 123 } else 124 sa = cdbp[1] & 0x1f; 125 126 if (!scsi_opcode_sa_name(cdb0, sa, &cdb_name, &sa_name)) { 127 if (cdb_name) 128 off = scnprintf(buffer, buf_len, "%s", cdb_name); 129 else { 130 off = scnprintf(buffer, buf_len, "opcode=0x%x", cdb0); 131 if (WARN_ON(off >= buf_len)) 132 return off; 133 if (cdb0 >= VENDOR_SPECIFIC_CDB) 134 off += scnprintf(buffer + off, buf_len - off, 135 " (vendor)"); 136 else if (cdb0 >= 0x60 && cdb0 < 0x7e) 137 off += scnprintf(buffer + off, buf_len - off, 138 " (reserved)"); 139 } 140 } else { 141 if (sa_name) 142 off = scnprintf(buffer, buf_len, "%s", sa_name); 143 else if (cdb_name) 144 off = scnprintf(buffer, buf_len, "%s, sa=0x%x", 145 cdb_name, sa); 146 else 147 off = scnprintf(buffer, buf_len, 148 "opcode=0x%x, sa=0x%x", cdb0, sa); 149 } 150 WARN_ON(off >= buf_len); 151 return off; 152 } 153 154 size_t __scsi_format_command(char *logbuf, size_t logbuf_len, 155 const unsigned char *cdb, size_t cdb_len) 156 { 157 int len, k; 158 size_t off; 159 160 off = scsi_format_opcode_name(logbuf, logbuf_len, cdb); 161 if (off >= logbuf_len) 162 return off; 163 len = scsi_command_size(cdb); 164 if (cdb_len < len) 165 len = cdb_len; 166 /* print out all bytes in cdb */ 167 for (k = 0; k < len; ++k) { 168 if (off > logbuf_len - 3) 169 break; 170 off += scnprintf(logbuf + off, logbuf_len - off, 171 " %02x", cdb[k]); 172 } 173 return off; 174 } 175 EXPORT_SYMBOL(__scsi_format_command); 176 177 void scsi_print_command(struct scsi_cmnd *cmd) 178 { 179 int k; 180 char *logbuf; 181 size_t off, logbuf_len; 182 183 if (!cmd->cmnd) 184 return; 185 186 logbuf = scsi_log_reserve_buffer(&logbuf_len); 187 if (!logbuf) 188 return; 189 190 off = sdev_format_header(logbuf, logbuf_len, 191 scmd_name(cmd), cmd->request->tag); 192 if (off >= logbuf_len) 193 goto out_printk; 194 off += scnprintf(logbuf + off, logbuf_len - off, "CDB: "); 195 if (WARN_ON(off >= logbuf_len)) 196 goto out_printk; 197 198 off += scsi_format_opcode_name(logbuf + off, logbuf_len - off, 199 cmd->cmnd); 200 if (off >= logbuf_len) 201 goto out_printk; 202 203 /* print out all bytes in cdb */ 204 if (cmd->cmd_len > 16) { 205 /* Print opcode in one line and use separate lines for CDB */ 206 off += scnprintf(logbuf + off, logbuf_len - off, "\n"); 207 dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf); 208 for (k = 0; k < cmd->cmd_len; k += 16) { 209 size_t linelen = min(cmd->cmd_len - k, 16); 210 211 off = sdev_format_header(logbuf, logbuf_len, 212 scmd_name(cmd), 213 cmd->request->tag); 214 if (!WARN_ON(off > logbuf_len - 58)) { 215 off += scnprintf(logbuf + off, logbuf_len - off, 216 "CDB[%02x]: ", k); 217 hex_dump_to_buffer(&cmd->cmnd[k], linelen, 218 16, 1, logbuf + off, 219 logbuf_len - off, false); 220 } 221 dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", 222 logbuf); 223 } 224 goto out; 225 } 226 if (!WARN_ON(off > logbuf_len - 49)) { 227 off += scnprintf(logbuf + off, logbuf_len - off, " "); 228 hex_dump_to_buffer(cmd->cmnd, cmd->cmd_len, 16, 1, 229 logbuf + off, logbuf_len - off, 230 false); 231 } 232 out_printk: 233 dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf); 234 out: 235 scsi_log_release_buffer(logbuf); 236 } 237 EXPORT_SYMBOL(scsi_print_command); 238 239 static size_t 240 scsi_format_extd_sense(char *buffer, size_t buf_len, 241 unsigned char asc, unsigned char ascq) 242 { 243 size_t off = 0; 244 const char *extd_sense_fmt = NULL; 245 const char *extd_sense_str = scsi_extd_sense_format(asc, ascq, 246 &extd_sense_fmt); 247 248 if (extd_sense_str) { 249 off = scnprintf(buffer, buf_len, "Add. Sense: %s", 250 extd_sense_str); 251 if (extd_sense_fmt) 252 off += scnprintf(buffer + off, buf_len - off, 253 "(%s%x)", extd_sense_fmt, ascq); 254 } else { 255 if (asc >= 0x80) 256 off = scnprintf(buffer, buf_len, "<<vendor>>"); 257 off += scnprintf(buffer + off, buf_len - off, 258 "ASC=0x%x ", asc); 259 if (ascq >= 0x80) 260 off += scnprintf(buffer + off, buf_len - off, 261 "<<vendor>>"); 262 off += scnprintf(buffer + off, buf_len - off, 263 "ASCQ=0x%x ", ascq); 264 } 265 return off; 266 } 267 268 static size_t 269 scsi_format_sense_hdr(char *buffer, size_t buf_len, 270 const struct scsi_sense_hdr *sshdr) 271 { 272 const char *sense_txt; 273 size_t off; 274 275 off = scnprintf(buffer, buf_len, "Sense Key : "); 276 sense_txt = scsi_sense_key_string(sshdr->sense_key); 277 if (sense_txt) 278 off += scnprintf(buffer + off, buf_len - off, 279 "%s ", sense_txt); 280 else 281 off += scnprintf(buffer + off, buf_len - off, 282 "0x%x ", sshdr->sense_key); 283 off += scnprintf(buffer + off, buf_len - off, 284 scsi_sense_is_deferred(sshdr) ? "[deferred] " : "[current] "); 285 286 if (sshdr->response_code >= 0x72) 287 off += scnprintf(buffer + off, buf_len - off, "[descriptor] "); 288 return off; 289 } 290 291 static void 292 scsi_log_dump_sense(const struct scsi_device *sdev, const char *name, int tag, 293 const unsigned char *sense_buffer, int sense_len) 294 { 295 char *logbuf; 296 size_t logbuf_len; 297 int i; 298 299 logbuf = scsi_log_reserve_buffer(&logbuf_len); 300 if (!logbuf) 301 return; 302 303 for (i = 0; i < sense_len; i += 16) { 304 int len = min(sense_len - i, 16); 305 size_t off; 306 307 off = sdev_format_header(logbuf, logbuf_len, 308 name, tag); 309 hex_dump_to_buffer(&sense_buffer[i], len, 16, 1, 310 logbuf + off, logbuf_len - off, 311 false); 312 dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf); 313 } 314 scsi_log_release_buffer(logbuf); 315 } 316 317 static void 318 scsi_log_print_sense_hdr(const struct scsi_device *sdev, const char *name, 319 int tag, const struct scsi_sense_hdr *sshdr) 320 { 321 char *logbuf; 322 size_t off, logbuf_len; 323 324 logbuf = scsi_log_reserve_buffer(&logbuf_len); 325 if (!logbuf) 326 return; 327 off = sdev_format_header(logbuf, logbuf_len, name, tag); 328 off += scsi_format_sense_hdr(logbuf + off, logbuf_len - off, sshdr); 329 dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf); 330 scsi_log_release_buffer(logbuf); 331 332 logbuf = scsi_log_reserve_buffer(&logbuf_len); 333 if (!logbuf) 334 return; 335 off = sdev_format_header(logbuf, logbuf_len, name, tag); 336 off += scsi_format_extd_sense(logbuf + off, logbuf_len - off, 337 sshdr->asc, sshdr->ascq); 338 dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf); 339 scsi_log_release_buffer(logbuf); 340 } 341 342 static void 343 scsi_log_print_sense(const struct scsi_device *sdev, const char *name, int tag, 344 const unsigned char *sense_buffer, int sense_len) 345 { 346 struct scsi_sense_hdr sshdr; 347 348 if (scsi_normalize_sense(sense_buffer, sense_len, &sshdr)) 349 scsi_log_print_sense_hdr(sdev, name, tag, &sshdr); 350 else 351 scsi_log_dump_sense(sdev, name, tag, sense_buffer, sense_len); 352 } 353 354 /* 355 * Print normalized SCSI sense header with a prefix. 356 */ 357 void 358 scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name, 359 const struct scsi_sense_hdr *sshdr) 360 { 361 scsi_log_print_sense_hdr(sdev, name, -1, sshdr); 362 } 363 EXPORT_SYMBOL(scsi_print_sense_hdr); 364 365 /* Normalize and print sense buffer with name prefix */ 366 void __scsi_print_sense(const struct scsi_device *sdev, const char *name, 367 const unsigned char *sense_buffer, int sense_len) 368 { 369 scsi_log_print_sense(sdev, name, -1, sense_buffer, sense_len); 370 } 371 EXPORT_SYMBOL(__scsi_print_sense); 372 373 /* Normalize and print sense buffer in SCSI command */ 374 void scsi_print_sense(const struct scsi_cmnd *cmd) 375 { 376 scsi_log_print_sense(cmd->device, scmd_name(cmd), cmd->request->tag, 377 cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); 378 } 379 EXPORT_SYMBOL(scsi_print_sense); 380 381 void scsi_print_result(const struct scsi_cmnd *cmd, const char *msg, 382 int disposition) 383 { 384 char *logbuf; 385 size_t off, logbuf_len; 386 const char *mlret_string = scsi_mlreturn_string(disposition); 387 const char *hb_string = scsi_hostbyte_string(cmd->result); 388 const char *db_string = scsi_driverbyte_string(cmd->result); 389 unsigned long cmd_age = (jiffies - cmd->jiffies_at_alloc) / HZ; 390 391 logbuf = scsi_log_reserve_buffer(&logbuf_len); 392 if (!logbuf) 393 return; 394 395 off = sdev_format_header(logbuf, logbuf_len, 396 scmd_name(cmd), cmd->request->tag); 397 398 if (off >= logbuf_len) 399 goto out_printk; 400 401 if (msg) { 402 off += scnprintf(logbuf + off, logbuf_len - off, 403 "%s: ", msg); 404 if (WARN_ON(off >= logbuf_len)) 405 goto out_printk; 406 } 407 if (mlret_string) 408 off += scnprintf(logbuf + off, logbuf_len - off, 409 "%s ", mlret_string); 410 else 411 off += scnprintf(logbuf + off, logbuf_len - off, 412 "UNKNOWN(0x%02x) ", disposition); 413 if (WARN_ON(off >= logbuf_len)) 414 goto out_printk; 415 416 off += scnprintf(logbuf + off, logbuf_len - off, "Result: "); 417 if (WARN_ON(off >= logbuf_len)) 418 goto out_printk; 419 420 if (hb_string) 421 off += scnprintf(logbuf + off, logbuf_len - off, 422 "hostbyte=%s ", hb_string); 423 else 424 off += scnprintf(logbuf + off, logbuf_len - off, 425 "hostbyte=0x%02x ", host_byte(cmd->result)); 426 if (WARN_ON(off >= logbuf_len)) 427 goto out_printk; 428 429 if (db_string) 430 off += scnprintf(logbuf + off, logbuf_len - off, 431 "driverbyte=%s ", db_string); 432 else 433 off += scnprintf(logbuf + off, logbuf_len - off, 434 "driverbyte=0x%02x ", 435 driver_byte(cmd->result)); 436 437 off += scnprintf(logbuf + off, logbuf_len - off, 438 "cmd_age=%lus", cmd_age); 439 440 out_printk: 441 dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf); 442 scsi_log_release_buffer(logbuf); 443 } 444 EXPORT_SYMBOL(scsi_print_result); 445