Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 18static char *scsi_log_reserve_buffer(size_t *len)
 19{
 20	*len = 128;
 21	return kmalloc(*len, GFP_ATOMIC);
 22}
 23
 24static void scsi_log_release_buffer(char *bufptr)
 25{
 26	kfree(bufptr);
 27}
 28
 29static 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
 35static 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
 53void 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}
 78EXPORT_SYMBOL(sdev_prefix_printk);
 79
 80void 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}
103EXPORT_SYMBOL(scmd_printk);
104
105static 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
154size_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}
175EXPORT_SYMBOL(__scsi_format_command);
176
177void 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	}
232out_printk:
233	dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf);
234out:
235	scsi_log_release_buffer(logbuf);
236}
237EXPORT_SYMBOL(scsi_print_command);
238
239static size_t
240scsi_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
268static size_t
269scsi_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
291static void
292scsi_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
317static void
318scsi_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
342static void
343scsi_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 */
357void
358scsi_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}
363EXPORT_SYMBOL(scsi_print_sense_hdr);
364
365/* Normalize and print sense buffer with name prefix */
366void __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}
371EXPORT_SYMBOL(__scsi_print_sense);
372
373/* Normalize and print sense buffer in SCSI command */
374void 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}
379EXPORT_SYMBOL(scsi_print_sense);
380
381void 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
440out_printk:
441	dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "%s", logbuf);
442	scsi_log_release_buffer(logbuf);
443}
444EXPORT_SYMBOL(scsi_print_result);