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