linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Improve code for detecting errors near the end of a CD
@ 2005-10-14 20:07 Alan Stern
  2005-10-15  0:49 ` Douglas Gilbert
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Stern @ 2005-10-14 20:07 UTC (permalink / raw)
  To: James Bottomley; +Cc: Jens Axboe, SCSI development list

James:

This revised patch (as572b) improves the code in sr.c that detects where
read/write errors occurred and moves it to a separate (but inline) routine
for greater clarity.

The original code contained a glaring mistake: testing
(SCpnt->sense_buffer[0] & 0x90) to see if the Information field in the
sense data is valid.  The Valid bit is 0x80; the test against 0x90 can
never fail, thanks to an earlier test: (SCpnt->sense_buffer[0] & 0x7f) ==
0x70).

The patch is careful to check that the error sector is within the range of
blocks that were supposed to be transferred, and it rounds the reported 
number of bytes transferred down to a multiple of the block layer's block 
size.

The major enhancement is that the patch uses the Residue to calculate the 
error sector, if the sense data doesn't already provide it.  This helps 
with a drive I use.  Since the Residue isn't always reported correctly, 
the patch is careful to check that it has a reasonable value: somewhere 
strictly between 0 and the total transfer length.

Alan Stern



Signed-off-by: Alan Stern <stern@rowland.harvard.edu>

---

Index: usb-2.6/drivers/scsi/sr.c
===================================================================
--- usb-2.6.orig/drivers/scsi/sr.c
+++ usb-2.6/drivers/scsi/sr.c
@@ -203,7 +203,44 @@ int sr_media_change(struct cdrom_device_
 	}
 	return retval;
 }
- 
+
+/* 
+ * Use the information from a failed command to compute how much data
+ * was transferred successfully, and find the sector that caused the
+ * error.  Be careful to avoid errors from numerical overflow.
+ */
+static inline int calc_good_bytes(struct scsi_cmnd *SCpnt,
+		long *error_sector, int hw_blocksize,
+		int sectors_per_bio_block)
+{
+	int req_bytes = SCpnt->bufflen;
+	unsigned int sectors = 0;
+
+	if (SCpnt->sense_buffer[0] & 0x80) {
+		/* The Information field is valid; use it */
+		unsigned int start_block, error_block, blocks;
+
+		start_block = (unsigned int) SCpnt->request->sector /
+				(hw_blocksize >> 9);
+		error_block = (SCpnt->sense_buffer[3] << 24) |
+				(SCpnt->sense_buffer[4] << 16) |
+				(SCpnt->sense_buffer[5] << 8) |
+				SCpnt->sense_buffer[6];
+		blocks = error_block - start_block;
+		if (blocks < req_bytes / hw_blocksize)
+			sectors = blocks * (hw_blocksize >> 9);
+	} else {
+		/* Fall back on the Residue */
+		if (SCpnt->resid > 0 && SCpnt->resid < req_bytes)
+			sectors = (req_bytes - SCpnt->resid) >> 9;
+	}
+	*error_sector = SCpnt->request->sector + sectors;
+
+	/* Round the amount to a multiple of the block layer's block size */
+	sectors &= ~(sectors_per_bio_block - 1);
+	return sectors << 9;
+}
+
 /*
  * rw_intr is the interrupt routine for the device driver.
  *
@@ -235,25 +272,17 @@ static void rw_intr(struct scsi_cmnd * S
 		case MEDIUM_ERROR:
 		case VOLUME_OVERFLOW:
 		case ILLEGAL_REQUEST:
-			if (!(SCpnt->sense_buffer[0] & 0x90))
-				break;
 			if (!blk_fs_request(SCpnt->request))
 				break;
-			error_sector = (SCpnt->sense_buffer[3] << 24) |
-				(SCpnt->sense_buffer[4] << 16) |
-				(SCpnt->sense_buffer[5] << 8) |
-				SCpnt->sense_buffer[6];
 			if (SCpnt->request->bio != NULL)
 				block_sectors =
 					bio_sectors(SCpnt->request->bio);
 			if (block_sectors < 4)
 				block_sectors = 4;
-			if (cd->device->sector_size == 2048)
-				error_sector <<= 2;
-			error_sector &= ~(block_sectors - 1);
-			good_bytes = (error_sector - SCpnt->request->sector) << 9;
-			if (good_bytes < 0 || good_bytes >= this_count)
-				good_bytes = 0;
+
+			good_bytes = calc_good_bytes(SCpnt, &error_sector,
+					cd->device->sector_size,
+					block_sectors);
 			/*
 			 * The SCSI specification allows for the value
 			 * returned by READ CAPACITY to be up to 75 2K


^ permalink raw reply	[flat|nested] 9+ messages in thread
* [PATCH] Improve code for detecting errors near the end of a CD
@ 2005-09-29 18:44 Alan Stern
  0 siblings, 0 replies; 9+ messages in thread
From: Alan Stern @ 2005-09-29 18:44 UTC (permalink / raw)
  To: James Bottomley; +Cc: SCSI development list

James:

This patch (as572) beefs up the code in sr.c that detects where read/write
errors occurred and moves it to a separate (but inline) routine for
greater clarity.

The original code contained one glaring mistake: testing
(SCpnt->sense_buffer[0] & 0x90) to see if the Information field in the
sense data is valid.  The Valid bit is 0x80; the test against 0x90 can
never fail, thanks to the earlier test: (SCpnt->sense_buffer[0] & 0x7f) ==
0x70).

The code also contained a rather puzzling line:

			error_sector &= ~(block_sectors - 1);

This makes no sense at all, so I have removed it.  The patch does of 
course check that error_sector is within the range of blocks that were 
supposed to be transferred.

The major enhancement is that the patch uses the Residue to calculate the 
error sector, if the sense data doesn't already provide it.  This helps 
with a drive I use.  Since the Residue isn't always reported correctly, 
the patch is careful to check that it has a reasonable value: somewhere 
strictly between 0 and the total transfer length.

Alan Stern



Signed-off-by: Alan Stern <stern@rowland.harvard.edu>

Index: usb-2.6/drivers/scsi/sr.c
===================================================================
--- usb-2.6.orig/drivers/scsi/sr.c
+++ usb-2.6/drivers/scsi/sr.c
@@ -203,7 +203,41 @@ int sr_media_change(struct cdrom_device_
 	}
 	return retval;
 }
- 
+
+/* 
+ * Use the information from a failed command to compute how much data
+ * was transferred successfully, and find the sector that caused the
+ * error.
+ */
+static inline int calc_good_bytes(struct scsi_cmnd *SCpnt,
+		long *error_sector, int blocksize)
+{
+	int num_bytes = SCpnt->bufflen;
+	unsigned int start_block, error_block, num_blocks = 0;
+
+	start_block = (unsigned int) SCpnt->request->sector / (blocksize >> 9);
+
+	if (SCpnt->sense_buffer[0] & 0x80) {
+		/* The Information field is valid; use it */
+		error_block = (SCpnt->sense_buffer[3] << 24) |
+				(SCpnt->sense_buffer[4] << 16) |
+				(SCpnt->sense_buffer[5] << 8) |
+				SCpnt->sense_buffer[6];
+		if (error_block >= start_block)
+			num_blocks = error_block - start_block;
+
+	} else {
+		/* Fall back on the Residue */
+		if (SCpnt->resid > 0 && SCpnt->resid < num_bytes)
+			num_blocks = (num_bytes - SCpnt->resid) / blocksize;
+	}
+
+	if (num_blocks >= num_bytes / blocksize)
+		num_blocks = 0;
+	*error_sector = (start_block + num_blocks) * (blocksize >> 9);
+	return num_blocks * blocksize;
+}
+
 /*
  * rw_intr is the interrupt routine for the device driver.
  *
@@ -235,25 +269,16 @@ static void rw_intr(struct scsi_cmnd * S
 		case MEDIUM_ERROR:
 		case VOLUME_OVERFLOW:
 		case ILLEGAL_REQUEST:
-			if (!(SCpnt->sense_buffer[0] & 0x90))
-				break;
 			if (!blk_fs_request(SCpnt->request))
 				break;
-			error_sector = (SCpnt->sense_buffer[3] << 24) |
-				(SCpnt->sense_buffer[4] << 16) |
-				(SCpnt->sense_buffer[5] << 8) |
-				SCpnt->sense_buffer[6];
 			if (SCpnt->request->bio != NULL)
 				block_sectors =
 					bio_sectors(SCpnt->request->bio);
 			if (block_sectors < 4)
 				block_sectors = 4;
-			if (cd->device->sector_size == 2048)
-				error_sector <<= 2;
-			error_sector &= ~(block_sectors - 1);
-			good_bytes = (error_sector - SCpnt->request->sector) << 9;
-			if (good_bytes < 0 || good_bytes >= this_count)
-				good_bytes = 0;
+
+			good_bytes = calc_good_bytes(SCpnt, &error_sector,
+					cd->device->sector_size);
 			/*
 			 * The SCSI specification allows for the value
 			 * returned by READ CAPACITY to be up to 75 2K


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2005-10-21  3:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-10-14 20:07 [PATCH] Improve code for detecting errors near the end of a CD Alan Stern
2005-10-15  0:49 ` Douglas Gilbert
2005-10-15  2:36   ` Alan Stern
2005-10-15  3:35     ` Douglas Gilbert
2005-10-19 20:32   ` Alan Stern
2005-10-20  2:56     ` Douglas Gilbert
2005-10-20 16:04       ` Alan Stern
2005-10-21  3:05         ` Douglas Gilbert
  -- strict thread matches above, loose matches on Subject: below --
2005-09-29 18:44 Alan Stern

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).