[RFC, PATCH] SCSI tape: MTIOCSENSE ioctl to return the sense data from errors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The st driver currently provides very limited information about the
errors. The information in errno after an error usually only tells that the
command has failed. The MTIOCGET ioctl gives some information about the status
of the device.

Most (all?) of the current tape drives use the SCSI command set. It defines
the sense data that is returned from the device in case of errors. The sense
data contains all of the error information the device provides. What it
contains, depends on the device. One possibility is to return the sense data
together with some other information and leave the interpretation to user
space. In this way the kernel interface can stay constant even when the sense
data definition evolves.

To see if this idea is useful, here is a proof-of-concept patch for the st
driver in Linux 2.6.26-rc9. It defines a new ioctl (MTIOCSENSE) to return the
sense data. The returned data is the latest "useful" sense data received. What
"useful" actually means, will probably need refinement. Conceptually it means
the sense data that is relevant to the latest error condition the user is
interested in. For example, sense data from the latest read error, even if the
driver would receive other sense data from tape movement commands necessary to
implement the tape semantics. In addition to the sense data, the ioctl returns
the command resulting in check condition and a flag to tell if the sense data
is from the latest SCSI command sent to the device.

A small program to test the new ioctl can be found from URL
http://www.kolumbus.fi/kai.makisara/st.html

The patch below this text applies to 2.6.26-rc9 plus the two bug fixes I just
sent to linux-scsi.

Some questions:
- is this useful?
- would someone really use this?
- what information should be provided together with the sense data?
  - should the complete command be saved?
- is ioctl the best way to return this information?

Kai

-----------------------8<-----------------------------------------------
diff --git a/Documentation/scsi/st.txt b/Documentation/scsi/st.txt
index 4075260..7847655 100644
--- a/Documentation/scsi/st.txt
+++ b/Documentation/scsi/st.txt
@@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver.
 The driver is currently maintained by Kai Mäkisara (email
 Kai.Makisara@xxxxxxxxxxx)
 
-Last modified: Fri Jul 11 16:55:44 2008 by kai.makisara
+Last modified: Fri Jul 11 16:53:06 2008 by kai.makisara
 
 
 BASICS
@@ -291,6 +291,8 @@ The supported ioctls are:
 
 The following use the structure mtop:
 
+MTIOCTOP implements the following operations
+
 MTFSF   Space forward over count filemarks. Tape positioned after filemark.
 MTFSFM  As above but tape positioned before filemark.
 MTBSF	Space backward over count filemarks. Tape positioned before
@@ -457,6 +459,17 @@ MTIOCGET Returns some status information.
 	is set if there is no tape in the drive. GMT_EOD means either
 	end of recorded data or end of tape. GMT_EOT means end of tape.
 
+The following ioctl uses the structure mtsense to return error information:
+MTIOCSENSE returns the sense data from the previous "interesting" SCSI command
+	resulting in CHECK CONDITION. Sense data is not saved from a command
+	if is used only for supporting the implementation of the tape
+	semantics and the sense data from that command would overwrite the
+	sense data from the error interesting to the user. For instance, error
+	from backspacing over a block by st after a read error does not
+	overwrite the sense data from the read. The field 'latest' tells if
+	the sense data is from the previous SCSI command sent to the device
+	(all SCSI commands are counted). The cmd field is the command byte
+	from the SCSI command resulting in the sense data.
 
 MISCELLANEOUS COMPILE OPTIONS
 
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 1986ebc..918f20f 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -352,14 +352,21 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt)
 	char *name = tape_name(STp);
 	struct st_cmdstatus *cmdstatp;
 
+	STp->saved_sense_latest = 0;
 	if (!result)
 		return 0;
 
 	cmdstatp = &STp->buffer->cmdstat;
 	st_analyze_sense(SRpnt, cmdstatp);
 
-	if (cmdstatp->have_sense)
+	if (cmdstatp->have_sense) {
 		scode = STp->buffer->cmdstat.sense_hdr.sense_key;
+		if (SRpnt->save_sense) {
+			STp->saved_sense_cmd = SRpnt->cmd[0];
+			memcpy(STp->saved_sense, SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
+			STp->saved_sense_latest = 1;
+		}
+	}
 	else
 		scode = 0;
 
@@ -465,7 +472,7 @@ static void st_release_request(struct st_request *streq)
    has finished. */
 static struct st_request *
 st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd,
-	   int bytes, int direction, int timeout, int retries, int do_wait)
+	   int bytes, int direction, int timeout, int retries, int do_wait, int save_sense)
 {
 	struct completion *waiting;
 
@@ -491,6 +498,7 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd
 				(STp->buffer)->syscall_result = (-EBUSY);
 			return NULL;
 		}
+		SRpnt->save_sense = save_sense;
 		SRpnt->stp = STp;
 	}
 
@@ -612,7 +620,7 @@ static int cross_eof(struct scsi_tape * STp, int forward)
 		   tape_name(STp), forward ? "forward" : "backward"));
 
 	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
-			   STp->device->timeout, MAX_RETRIES, 1);
+			   STp->device->timeout, MAX_RETRIES, 1, 0);
 	if (!SRpnt)
 		return (STp->buffer)->syscall_result;
 
@@ -656,7 +664,7 @@ static int st_flush_write_buffer(struct scsi_tape * STp)
 		cmd[4] = blks;
 
 		SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
-				   STp->device->timeout, MAX_WRITE_RETRIES, 1);
+				   STp->device->timeout, MAX_WRITE_RETRIES, 1, 1);
 		if (!SRpnt)
 			return (STp->buffer)->syscall_result;
 
@@ -852,7 +860,7 @@ static int test_ready(struct scsi_tape *STp, int do_wait)
 		memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
 		cmd[0] = TEST_UNIT_READY;
 		SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
-				   STp->long_timeout, MAX_READY_RETRIES, 1);
+				   STp->long_timeout, MAX_READY_RETRIES, 1, 1);
 
 		if (!SRpnt) {
 			retval = (STp->buffer)->syscall_result;
@@ -986,7 +994,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
 		cmd[0] = READ_BLOCK_LIMITS;
 
 		SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
-				   STp->device->timeout, MAX_READY_RETRIES, 1);
+				   STp->device->timeout, MAX_READY_RETRIES, 1, 0);
 		if (!SRpnt) {
 			retval = (STp->buffer)->syscall_result;
 			goto err_out;
@@ -1013,7 +1021,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
 	cmd[4] = 12;
 
 	SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
-			STp->device->timeout, MAX_READY_RETRIES, 1);
+			   STp->device->timeout, MAX_READY_RETRIES, 1, 0);
 	if (!SRpnt) {
 		retval = (STp->buffer)->syscall_result;
 		goto err_out;
@@ -1240,7 +1248,7 @@ static int st_flush(struct file *filp, fl_owner_t id)
 		cmd[4] = 1 + STp->two_fm;
 
 		SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
-				   STp->device->timeout, MAX_WRITE_RETRIES, 1);
+				   STp->device->timeout, MAX_WRITE_RETRIES, 1, 1);
 		if (!SRpnt) {
 			result = (STp->buffer)->syscall_result;
 			goto out;
@@ -1627,7 +1635,7 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
 		cmd[4] = blks;
 
 		SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
-				   STp->device->timeout, MAX_WRITE_RETRIES, !async_write);
+				   STp->device->timeout, MAX_WRITE_RETRIES, !async_write, 1);
 		if (!SRpnt) {
 			retval = STbp->syscall_result;
 			goto out;
@@ -1797,7 +1805,7 @@ static long read_tape(struct scsi_tape *STp, long count,
 
 	SRpnt = *aSRpnt;
 	SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
-			   STp->device->timeout, MAX_RETRIES, 1);
+			   STp->device->timeout, MAX_RETRIES, 1, 1);
 	release_buffering(STp, 1);
 	*aSRpnt = SRpnt;
 	if (!SRpnt)
@@ -2314,7 +2322,7 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
 	cmd[4] = 255;
 
 	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE,
-			   STp->device->timeout, 0, 1);
+			   STp->device->timeout, 0, 1, 0);
 	if (SRpnt == NULL)
 		return (STp->buffer)->syscall_result;
 
@@ -2345,7 +2353,7 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow)
 	(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
 
 	SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE,
-			   (slow ? STp->long_timeout : STp->device->timeout), 0, 1);
+			   (slow ? STp->long_timeout : STp->device->timeout), 0, 1, 0);
 	if (SRpnt == NULL)
 		return (STp->buffer)->syscall_result;
 
@@ -2470,7 +2478,7 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod
 		);
 
 	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
-			   timeout, MAX_RETRIES, 1);
+			   timeout, MAX_RETRIES, 1, 1);
 	if (!SRpnt)
 		return (STp->buffer)->syscall_result;
 
@@ -2770,7 +2778,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
 	}
 
 	SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
-			   timeout, MAX_RETRIES, 1);
+			   timeout, MAX_RETRIES, 1, 1);
 	if (!SRpnt)
 		return (STp->buffer)->syscall_result;
 
@@ -2937,7 +2945,7 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti
 			scmd[1] = 1;
 	}
 	SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
-			STp->device->timeout, MAX_READY_RETRIES, 1);
+			   STp->device->timeout, MAX_READY_RETRIES, 1, 1);
 	if (!SRpnt)
 		return (STp->buffer)->syscall_result;
 
@@ -3042,7 +3050,7 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition
 	}
 
 	SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
-			   timeout, MAX_READY_RETRIES, 1);
+			   timeout, MAX_READY_RETRIES, 1, 1);
 	if (!SRpnt)
 		return (STp->buffer)->syscall_result;
 
@@ -3548,6 +3556,32 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
 			retval = (-EFAULT);
 		goto out;
 	}
+
+	if (cmd_type == _IOC_TYPE(MTIOCSENSE) && cmd_nr == _IOC_NR(MTIOCSENSE)) {
+		struct mtsense *mt_sensep;
+
+		mt_sensep = kzalloc(sizeof(struct mtsense), GFP_KERNEL);
+		if (!mt_sensep) {
+			retval = -ENOMEM;
+			goto out;
+		}
+
+		if (STp->saved_sense) {
+			mt_sensep->length = _IOC_SIZE(cmd_in) - sizeof(int);
+			mt_sensep->latest = STp->saved_sense_latest;
+			if (mt_sensep->length > SCSI_SENSE_BUFFERSIZE)
+				mt_sensep->length = SCSI_SENSE_BUFFERSIZE;
+			mt_sensep->cmd = STp->saved_sense_cmd;
+			memcpy(mt_sensep->data, STp->saved_sense, SCSI_SENSE_BUFFERSIZE);
+		}
+
+		i = copy_to_user(p, mt_sensep, sizeof(struct mtsense));
+		if (i)
+			retval = (-EFAULT);
+		kfree(mt_sensep);
+		goto out;
+	}
+
 	mutex_unlock(&STp->lock);
 	switch (cmd_in) {
 		case SCSI_IOCTL_GET_IDLUN:
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h
index b92712f..b3fb81e 100644
--- a/drivers/scsi/st.h
+++ b/drivers/scsi/st.h
@@ -27,6 +27,7 @@ struct st_request {
 	unsigned char cmd[MAX_COMMAND_SIZE];
 	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
 	int result;
+	int save_sense;
 	struct scsi_tape *stp;
 	struct completion *waiting;
 };
@@ -160,6 +161,13 @@ struct scsi_tape {
 	int recover_count;     /* From tape opening */
 	int recover_reg;       /* From last status call */
 
+	/* The latest sense data to be returned to the user. If the first byte is zero,
+	   there is no sense data to return. */
+	unsigned char saved_sense[SCSI_SENSE_BUFFERSIZE];
+	unsigned char saved_sense_cmd;  /* the command resulting in request sense */
+	unsigned char saved_sense_latest; /* is the sense data from the latest command
+					     submitted to the device */
+
 #if DEBUG
 	unsigned char write_pending;
 	int nbr_finished;
diff --git a/include/linux/mtio.h b/include/linux/mtio.h
index ef01d6a..96ca0d7 100644
--- a/include/linux/mtio.h
+++ b/include/linux/mtio.h
@@ -122,11 +122,20 @@ struct	mtpos {
 	long 	mt_blkno;	/* current block number */
 };
 
+/* structure for MTIOCSENSE - mag tape get sense data */
+struct mtsense {
+	__u16 length;		/* length of the returned data */
+	__u8 cmd;		/* the command that returned the sense data */
+	__u8 latest;		/* is this sense data from the latest SCSI command to device? */
+	__u8 data[252];		/* space for all data in REQUEST SENSE response */
+};
+
 
 /* mag tape io control commands */
 #define	MTIOCTOP	_IOW('m', 1, struct mtop)	/* do a mag tape op */
 #define	MTIOCGET	_IOR('m', 2, struct mtget)	/* get tape status */
 #define	MTIOCPOS	_IOR('m', 3, struct mtpos)	/* get tape position */
+#define MTIOCSENSE      _IOR('m', 4, struct mtsense)    /* get latest sense data */
 
 
 /* Generic Mag Tape (device independent) status macros for examining

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux