Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[sfrench/cifs-2.6.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                 scsi_log_release_buffer(logbuf);
209                 for (k = 0; k < cmd->cmd_len; k += 16) {
210                         size_t linelen = min(cmd->cmd_len - k, 16);
211
212                         logbuf = scsi_log_reserve_buffer(&logbuf_len);
213                         if (!logbuf)
214                                 break;
215                         off = sdev_format_header(logbuf, logbuf_len,
216                                                  scmd_name(cmd),
217                                                  cmd->request->tag);
218                         if (!WARN_ON(off > logbuf_len - 58)) {
219                                 off += scnprintf(logbuf + off, logbuf_len - off,
220                                                  "CDB[%02x]: ", k);
221                                 hex_dump_to_buffer(&cmd->cmnd[k], linelen,
222                                                    16, 1, logbuf + off,
223                                                    logbuf_len - off, false);
224                         }
225                         dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s",
226                                    logbuf);
227                         scsi_log_release_buffer(logbuf);
228                 }
229                 return;
230         }
231         if (!WARN_ON(off > logbuf_len - 49)) {
232                 off += scnprintf(logbuf + off, logbuf_len - off, " ");
233                 hex_dump_to_buffer(cmd->cmnd, cmd->cmd_len, 16, 1,
234                                    logbuf + off, logbuf_len - off,
235                                    false);
236         }
237 out_printk:
238         dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf);
239         scsi_log_release_buffer(logbuf);
240 }
241 EXPORT_SYMBOL(scsi_print_command);
242
243 static size_t
244 scsi_format_extd_sense(char *buffer, size_t buf_len,
245                        unsigned char asc, unsigned char ascq)
246 {
247         size_t off = 0;
248         const char *extd_sense_fmt = NULL;
249         const char *extd_sense_str = scsi_extd_sense_format(asc, ascq,
250                                                             &extd_sense_fmt);
251
252         if (extd_sense_str) {
253                 off = scnprintf(buffer, buf_len, "Add. Sense: %s",
254                                 extd_sense_str);
255                 if (extd_sense_fmt)
256                         off += scnprintf(buffer + off, buf_len - off,
257                                          "(%s%x)", extd_sense_fmt, ascq);
258         } else {
259                 if (asc >= 0x80)
260                         off = scnprintf(buffer, buf_len, "<<vendor>>");
261                 off += scnprintf(buffer + off, buf_len - off,
262                                  "ASC=0x%x ", asc);
263                 if (ascq >= 0x80)
264                         off += scnprintf(buffer + off, buf_len - off,
265                                          "<<vendor>>");
266                 off += scnprintf(buffer + off, buf_len - off,
267                                  "ASCQ=0x%x ", ascq);
268         }
269         return off;
270 }
271
272 static size_t
273 scsi_format_sense_hdr(char *buffer, size_t buf_len,
274                       const struct scsi_sense_hdr *sshdr)
275 {
276         const char *sense_txt;
277         size_t off;
278
279         off = scnprintf(buffer, buf_len, "Sense Key : ");
280         sense_txt = scsi_sense_key_string(sshdr->sense_key);
281         if (sense_txt)
282                 off += scnprintf(buffer + off, buf_len - off,
283                                  "%s ", sense_txt);
284         else
285                 off += scnprintf(buffer + off, buf_len - off,
286                                  "0x%x ", sshdr->sense_key);
287         off += scnprintf(buffer + off, buf_len - off,
288                 scsi_sense_is_deferred(sshdr) ? "[deferred] " : "[current] ");
289
290         if (sshdr->response_code >= 0x72)
291                 off += scnprintf(buffer + off, buf_len - off, "[descriptor] ");
292         return off;
293 }
294
295 static void
296 scsi_log_dump_sense(const struct scsi_device *sdev, const char *name, int tag,
297                     const unsigned char *sense_buffer, int sense_len)
298 {
299         char *logbuf;
300         size_t logbuf_len;
301         int i;
302
303         logbuf = scsi_log_reserve_buffer(&logbuf_len);
304         if (!logbuf)
305                 return;
306
307         for (i = 0; i < sense_len; i += 16) {
308                 int len = min(sense_len - i, 16);
309                 size_t off;
310
311                 off = sdev_format_header(logbuf, logbuf_len,
312                                          name, tag);
313                 hex_dump_to_buffer(&sense_buffer[i], len, 16, 1,
314                                    logbuf + off, logbuf_len - off,
315                                    false);
316                 dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf);
317         }
318         scsi_log_release_buffer(logbuf);
319 }
320
321 static void
322 scsi_log_print_sense_hdr(const struct scsi_device *sdev, const char *name,
323                          int tag, const struct scsi_sense_hdr *sshdr)
324 {
325         char *logbuf;
326         size_t off, logbuf_len;
327
328         logbuf = scsi_log_reserve_buffer(&logbuf_len);
329         if (!logbuf)
330                 return;
331         off = sdev_format_header(logbuf, logbuf_len, name, tag);
332         off += scsi_format_sense_hdr(logbuf + off, logbuf_len - off, sshdr);
333         dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf);
334         scsi_log_release_buffer(logbuf);
335
336         logbuf = scsi_log_reserve_buffer(&logbuf_len);
337         if (!logbuf)
338                 return;
339         off = sdev_format_header(logbuf, logbuf_len, name, tag);
340         off += scsi_format_extd_sense(logbuf + off, logbuf_len - off,
341                                       sshdr->asc, sshdr->ascq);
342         dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf);
343         scsi_log_release_buffer(logbuf);
344 }
345
346 static void
347 scsi_log_print_sense(const struct scsi_device *sdev, const char *name, int tag,
348                      const unsigned char *sense_buffer, int sense_len)
349 {
350         struct scsi_sense_hdr sshdr;
351
352         if (scsi_normalize_sense(sense_buffer, sense_len, &sshdr))
353                 scsi_log_print_sense_hdr(sdev, name, tag, &sshdr);
354         else
355                 scsi_log_dump_sense(sdev, name, tag, sense_buffer, sense_len);
356 }
357
358 /*
359  * Print normalized SCSI sense header with a prefix.
360  */
361 void
362 scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name,
363                      const struct scsi_sense_hdr *sshdr)
364 {
365         scsi_log_print_sense_hdr(sdev, name, -1, sshdr);
366 }
367 EXPORT_SYMBOL(scsi_print_sense_hdr);
368
369 /* Normalize and print sense buffer with name prefix */
370 void __scsi_print_sense(const struct scsi_device *sdev, const char *name,
371                         const unsigned char *sense_buffer, int sense_len)
372 {
373         scsi_log_print_sense(sdev, name, -1, sense_buffer, sense_len);
374 }
375 EXPORT_SYMBOL(__scsi_print_sense);
376
377 /* Normalize and print sense buffer in SCSI command */
378 void scsi_print_sense(const struct scsi_cmnd *cmd)
379 {
380         scsi_log_print_sense(cmd->device, scmd_name(cmd), cmd->request->tag,
381                              cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
382 }
383 EXPORT_SYMBOL(scsi_print_sense);
384
385 void scsi_print_result(const struct scsi_cmnd *cmd, const char *msg,
386                        int disposition)
387 {
388         char *logbuf;
389         size_t off, logbuf_len;
390         const char *mlret_string = scsi_mlreturn_string(disposition);
391         const char *hb_string = scsi_hostbyte_string(cmd->result);
392         const char *db_string = scsi_driverbyte_string(cmd->result);
393
394         logbuf = scsi_log_reserve_buffer(&logbuf_len);
395         if (!logbuf)
396                 return;
397
398         off = sdev_format_header(logbuf, logbuf_len,
399                                  scmd_name(cmd), cmd->request->tag);
400
401         if (off >= logbuf_len)
402                 goto out_printk;
403
404         if (msg) {
405                 off += scnprintf(logbuf + off, logbuf_len - off,
406                                  "%s: ", msg);
407                 if (WARN_ON(off >= logbuf_len))
408                         goto out_printk;
409         }
410         if (mlret_string)
411                 off += scnprintf(logbuf + off, logbuf_len - off,
412                                  "%s ", mlret_string);
413         else
414                 off += scnprintf(logbuf + off, logbuf_len - off,
415                                  "UNKNOWN(0x%02x) ", disposition);
416         if (WARN_ON(off >= logbuf_len))
417                 goto out_printk;
418
419         off += scnprintf(logbuf + off, logbuf_len - off, "Result: ");
420         if (WARN_ON(off >= logbuf_len))
421                 goto out_printk;
422
423         if (hb_string)
424                 off += scnprintf(logbuf + off, logbuf_len - off,
425                                  "hostbyte=%s ", hb_string);
426         else
427                 off += scnprintf(logbuf + off, logbuf_len - off,
428                                  "hostbyte=0x%02x ", host_byte(cmd->result));
429         if (WARN_ON(off >= logbuf_len))
430                 goto out_printk;
431
432         if (db_string)
433                 off += scnprintf(logbuf + off, logbuf_len - off,
434                                  "driverbyte=%s", db_string);
435         else
436                 off += scnprintf(logbuf + off, logbuf_len - off,
437                                  "driverbyte=0x%02x", driver_byte(cmd->result));
438 out_printk:
439         dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf);
440         scsi_log_release_buffer(logbuf);
441 }
442 EXPORT_SYMBOL(scsi_print_result);