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 : 0 : static char *scsi_log_reserve_buffer(size_t *len)
19 : : {
20 : 0 : *len = 128;
21 : 0 : return kmalloc(*len, GFP_ATOMIC);
22 : : }
23 : :
24 : : static void scsi_log_release_buffer(char *bufptr)
25 : : {
26 : 0 : kfree(bufptr);
27 : : }
28 : :
29 : : 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 : : 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 : 0 : return off;
51 : : }
52 : :
53 : 0 : 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 : 0 : if (!sdev)
61 : 0 : return;
62 : :
63 : 0 : logbuf = scsi_log_reserve_buffer(&logbuf_len);
64 : 0 : if (!logbuf)
65 : : return;
66 : :
67 : 0 : if (name)
68 : 0 : off += scnprintf(logbuf + off, logbuf_len - off,
69 : : "[%s] ", name);
70 : 0 : if (!WARN_ON(off >= logbuf_len)) {
71 : 0 : va_start(args, fmt);
72 : 0 : off += vscnprintf(logbuf + off, logbuf_len - off, fmt, args);
73 : 0 : va_end(args);
74 : : }
75 : 0 : dev_printk(level, &sdev->sdev_gendev, "%s", logbuf);
76 : : 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 : : va_list args;
84 : : char *logbuf;
85 : : 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 : : 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 : : 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 : : int sa, cdb0;
109 : : const char *cdb_name = NULL, *sa_name = NULL;
110 : : size_t off;
111 : :
112 : 0 : cdb0 = cdbp[0];
113 : 0 : if (cdb0 == VARIABLE_LENGTH_CMD) {
114 : : 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 : : if (cdb_name)
128 : : 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 : : 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 : 0 : off = scnprintf(buffer, buf_len,
148 : : "opcode=0x%x, sa=0x%x", cdb0, sa);
149 : : }
150 : 0 : WARN_ON(off >= buf_len);
151 : 0 : 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 : : int len, k;
158 : : 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 : 0 : return off;
174 : : }
175 : : EXPORT_SYMBOL(__scsi_format_command);
176 : :
177 : 0 : void scsi_print_command(struct scsi_cmnd *cmd)
178 : : {
179 : : int k;
180 : : char *logbuf;
181 : : size_t off, logbuf_len;
182 : :
183 : 0 : if (!cmd->cmnd)
184 : 0 : 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 : : scmd_name(cmd), cmd->request->tag);
192 : 0 : if (off >= logbuf_len)
193 : : goto out_printk;
194 : 0 : off += scnprintf(logbuf + off, logbuf_len - off, "CDB: ");
195 : 0 : if (WARN_ON(off >= logbuf_len))
196 : : 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 : : 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 : : 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 : : 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 : : scsi_log_release_buffer(logbuf);
228 : : }
229 : : 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 : : out_printk:
238 : 0 : 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 : 0 : 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 : 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 : : const char *sense_txt;
277 : : size_t off;
278 : :
279 : 0 : 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 : 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 : : 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 : : char *logbuf;
300 : : size_t logbuf_len;
301 : : int i;
302 : :
303 : 0 : logbuf = scsi_log_reserve_buffer(&logbuf_len);
304 : 0 : if (!logbuf)
305 : 0 : return;
306 : :
307 : 0 : for (i = 0; i < sense_len; i += 16) {
308 : 0 : int len = min(sense_len - i, 16);
309 : : 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 : : 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 : : char *logbuf;
326 : : size_t off, logbuf_len;
327 : :
328 : 0 : logbuf = scsi_log_reserve_buffer(&logbuf_len);
329 : 0 : if (!logbuf)
330 : 0 : 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 : : 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 : : sshdr->asc, sshdr->ascq);
342 : 0 : dev_printk(KERN_INFO, &sdev->sdev_gendev, "%s", logbuf);
343 : : 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 : : 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 : : 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 : 0 : logbuf = scsi_log_reserve_buffer(&logbuf_len);
395 : 0 : if (!logbuf)
396 : 0 : return;
397 : :
398 : 0 : off = sdev_format_header(logbuf, logbuf_len,
399 : : scmd_name(cmd), cmd->request->tag);
400 : :
401 : 0 : if (off >= logbuf_len)
402 : : goto out_printk;
403 : :
404 : 0 : if (msg) {
405 : 0 : off += scnprintf(logbuf + off, logbuf_len - off,
406 : : "%s: ", msg);
407 : 0 : 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 : 0 : off += scnprintf(logbuf + off, logbuf_len - off,
415 : : "UNKNOWN(0x%02x) ", disposition);
416 : 0 : if (WARN_ON(off >= logbuf_len))
417 : : goto out_printk;
418 : :
419 : 0 : off += scnprintf(logbuf + off, logbuf_len - off, "Result: ");
420 : 0 : 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 : 0 : off += scnprintf(logbuf + off, logbuf_len - off,
428 : 0 : "hostbyte=0x%02x ", host_byte(cmd->result));
429 : 0 : 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 : 0 : off += scnprintf(logbuf + off, logbuf_len - off,
437 : 0 : "driverbyte=0x%02x", driver_byte(cmd->result));
438 : : out_printk:
439 : 0 : dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf);
440 : : scsi_log_release_buffer(logbuf);
441 : : }
442 : : EXPORT_SYMBOL(scsi_print_result);
|