Merge tag 'iomap-5.13-merge-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux-2.6-microblaze.git] / drivers / scsi / scsi_logging.c
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);