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