[PATCH] Fix handling of failed requests in scsi_io_completion

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

 



This patch (as1133) fixes a bug in the interaction between
scsi_end_request() and scsi_io_completion().  The bug was triggered by
recent changes to accomodate USB drives can't access their last few
sectors using multi-sector transfers (the so-called last_sector_bug
flag).

The bug shows up when a multi-sector I/O request has been broken up
into single-sector commands.  If one of those commands gets an error,
the remainder of the request never gets completed.  This is because
the "bytes" value passed to scsi_end_request() is completely wrong; it
is the transfer size of the current command, not the total transfer
size of the request.

Changes made in the patch include:

	Use the blk_rq_bytes() routine to get the remaining size
	of a request instead of calculating it by hand.

	Don't bother to call blk_end_request() for a BLK_TYPE_FS
	request when the number of bytes transferred is 0.  (This
	test isn't really needed; it is a possibly premature
	optimization.)

	Remove a comment in scsi_io_completion() that has bothered
	me for a long time.  Although it's less than two lines long,
	it contains at least three major mistakes.

	Replace a bunch of repetitious calls to scsi_end_request() for
	-EIO errors by either "goto retry" or "goto fail", depending
	on whether or not the individual request should be retried.

	In the "retry" case, don't claim that any additional bytes
	have been transferred since the last call to
	scsi_end_request().  They haven't.

	In the "fail" case, pass the total number of bytes remaining
	in the request (i.e., blk_rq_bytes(req)) so that the entire
	request will complete.

Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

---

This patch could be broken up into several simpler patches, of which 
all but the last would be uncontroversial.  But first I'd like to get 
some feedback concerning the overall correctness.

Alan Stern



Index: usb-2.6/drivers/scsi/scsi_lib.c
===================================================================
--- usb-2.6.orig/drivers/scsi/scsi_lib.c
+++ usb-2.6/drivers/scsi/scsi_lib.c
@@ -674,16 +674,13 @@ static struct scsi_cmnd *scsi_end_reques
 	 * If there are blocks left over at the end, set up the command
 	 * to queue the remainder of them.
 	 */
-	if (blk_end_request(req, error, bytes)) {
-		int leftover = (req->hard_nr_sectors << 9);
-
-		if (blk_pc_request(req))
-			leftover = req->data_len;
+	if (unlikely(bytes == 0 && blk_fs_request(req)) ||
+			blk_end_request(req, error, bytes)) {
 
 		/* kill remainder if no retrys */
-		if (error && blk_noretry_request(req))
-			blk_end_request(req, error, leftover);
-		else {
+		if (error && blk_noretry_request(req)) {
+			blk_end_request(req, error, blk_rq_bytes(req));
+		} else {
 			if (requeue) {
 				/*
 				 * Bleah.  Leftovers again.  Stick the
@@ -852,7 +849,6 @@ static void scsi_end_bidi_request(struct
 void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
 {
 	int result = cmd->result;
-	int this_count = scsi_bufflen(cmd);
 	struct request_queue *q = cmd->device->request_queue;
 	struct request *req = cmd->request;
 	int error = 0;
@@ -909,9 +905,6 @@ void scsi_io_completion(struct scsi_cmnd
 	if (scsi_end_request(cmd, error, good_bytes, result == 0) == NULL)
 		return;
 
-	/* good_bytes = 0, or (inclusive) there were leftovers and
-	 * result = 0, so scsi_end_request couldn't retry.
-	 */
 	if (sense_valid && !sense_deferred) {
 		switch (sshdr.sense_key) {
 		case UNIT_ATTENTION:
@@ -920,8 +913,7 @@ void scsi_io_completion(struct scsi_cmnd
 				 * and quietly refuse further access.
 				 */
 				cmd->device->changed = 1;
-				scsi_end_request(cmd, -EIO, this_count, 1);
-				return;
+				goto retry;
 			} else {
 				/* Must have been a power glitch, or a
 				 * bus reset.  Could not have been a
@@ -951,15 +943,13 @@ void scsi_io_completion(struct scsi_cmnd
 				 */
 				scsi_requeue_command(q, cmd);
 			} else if (sshdr.asc == 0x10) /* DIX */
-				scsi_end_request(cmd, -EIO, this_count, 0);
+				goto fail;
 			else
-				scsi_end_request(cmd, -EIO, this_count, 1);
+				goto retry;
 			return;
 		case ABORTED_COMMAND:
-			if (sshdr.asc == 0x10) { /* DIF */
-				scsi_end_request(cmd, -EIO, this_count, 0);
-				return;
-			}
+			if (sshdr.asc == 0x10)  /* DIF */
+				goto fail;
 			break;
 		case NOT_READY:
 			/* If the device is in the process of becoming
@@ -985,8 +975,7 @@ void scsi_io_completion(struct scsi_cmnd
 							 "Device not ready",
 							 &sshdr);
 
-			scsi_end_request(cmd, -EIO, this_count, 1);
-			return;
+			goto retry;
 		case VOLUME_OVERFLOW:
 			if (!(req->cmd_flags & REQ_QUIET)) {
 				scmd_printk(KERN_INFO, cmd,
@@ -995,8 +984,7 @@ void scsi_io_completion(struct scsi_cmnd
 				scsi_print_sense("", cmd);
 			}
 			/* See SSC3rXX or current. */
-			scsi_end_request(cmd, -EIO, this_count, 1);
-			return;
+			goto retry;
 		default:
 			break;
 		}
@@ -1015,8 +1003,13 @@ void scsi_io_completion(struct scsi_cmnd
 			if (driver_byte(result) & DRIVER_SENSE)
 				scsi_print_sense("", cmd);
 		}
+		goto fail;
 	}
-	scsi_end_request(cmd, -EIO, this_count, !result);
+ retry:
+	scsi_end_request(cmd, -EIO, 0, 1);
+	return;
+ fail:
+	scsi_end_request(cmd, -EIO, blk_rq_bytes(req), 0);
 }
 
 static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[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