[PATCH] SCSI: add check_capacity flag and sd_read_last_sector()

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

 



This patch (as1196) adds a new scsi_device flag to tell sd.c that it
should verify the results of READ CAPACITY by trying the read the last
sector.  This is necessary because a large percentage of USB
mass-storage devices -- too many for a blacklist -- have a bug whereby
they return the total number of sectors rather than the index of the
last sector.

The new sd_read_last_sector() routine carries out the test, using a
very short timeout and a small number of retries.  Any working device
for which the check_capacity flag is set should be able to pass the
test easily.

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

---

James & Greg:

Although part of this patch touches usb-storage, the majority of 
it affects SCSI files.  Is it okay if the whole thing goes in via 
James's tree?

Alan Stern



Index: usb-2.6/include/scsi/scsi_device.h
===================================================================
--- usb-2.6.orig/include/scsi/scsi_device.h
+++ usb-2.6/include/scsi/scsi_device.h
@@ -142,6 +142,7 @@ struct scsi_device {
 	unsigned select_no_atn:1;
 	unsigned fix_capacity:1;	/* READ_CAPACITY is too high by 1 */
 	unsigned guess_capacity:1;	/* READ_CAPACITY might be too high by 1 */
+	unsigned check_capacity:1;	/* Verify READ_CAPACITY result */
 	unsigned retry_hwerror:1;	/* Retry HARDWARE_ERROR */
 	unsigned last_sector_bug:1;	/* do not use multisector accesses on
 					   SD_LAST_BUGGY_SECTORS */
Index: usb-2.6/drivers/scsi/sd.c
===================================================================
--- usb-2.6.orig/drivers/scsi/sd.c
+++ usb-2.6/drivers/scsi/sd.c
@@ -1426,6 +1426,89 @@ static int sd_try_rc16_first(struct scsi
 	return 0;
 }
 
+#if defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE)
+/*
+ * Test disk capacity by trying to read the last sector
+ */
+static int sd_read_last_sector(struct scsi_disk *sdkp, unsigned char *buffer,
+		int sector_size)
+{
+	struct scsi_device *sdp = sdkp->device;
+	u32 last;
+	int result, resid;
+	int rc = 0;
+	unsigned char cmd[10];
+
+	/* If the capacity is unknown, we can't test anything */
+	if (sdkp->capacity <= 0)
+		return rc;
+
+	/* One test should be enough (unless it's inconclusive) */
+	sdp->check_capacity = 0;
+
+	/* If the device doesn't use READ(10), assume we're okay */
+	if (!sdp->use_10_for_rw)
+		return rc;
+
+	/* If the capacity is too big for READ(10), assume we're okay */
+	if (sdkp->capacity > 0xffffffff)
+		return rc;
+
+	/* If the sector size is too big, assume we're okay */
+	if (sector_size > SD_BUF_SIZE)
+		return rc;
+	if (sector_size == 0)
+		sector_size = 512;
+
+	if (!sdkp->openers++ && sdp->removable)
+		scsi_set_medium_removal(sdp, SCSI_REMOVAL_PREVENT);
+
+	/* Start by trying to read the first sector, just to warm up */
+	memset(cmd, 0, 10);
+	cmd[0] = READ_10;
+	cmd[8] = 1;
+	result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
+			buffer, sector_size, NULL,
+			SD_TIMEOUT, SD_MAX_RETRIES, &resid);
+	if (result || resid) {
+		sdp->check_capacity = 1;	/* Try again later */
+		goto done;
+	}
+
+	/* Now try to read the last sector */
+	last = sdkp->capacity - 1;
+	cmd[2] = (last >> 24);
+	cmd[3] = (last >> 16);
+	cmd[4] = (last >> 8);
+	cmd[5] = last;
+
+	/* Allow only 1 retry, with a short timeout (some devices
+	 * violate protocol, forcing the command to be aborted).
+	 */
+	result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
+			buffer, sector_size, NULL, HZ, 1, &resid);
+	if (result || resid) {
+		sdp->fix_capacity = 1;		/* For next time */
+		rc = -EIO;
+	}
+
+ done:
+	if (!--sdkp->openers && sdp->removable) {
+		if (scsi_block_when_processing_errors(sdp))
+			scsi_set_medium_removal(sdp, SCSI_REMOVAL_ALLOW);
+	}
+	return rc;
+}
+
+#else
+static inline int sd_read_last_sector(struct scsi_disk *sdkp,
+		unsigned char *buffer, int sector_size)
+{
+	return 0;
+}
+
+#endif /* defined(CONFIG_USB_STORAGE) || ... */
+
 /*
  * read disk capacity
  */
@@ -1479,7 +1562,9 @@ sd_read_capacity(struct scsi_disk *sdkp,
 	 * the capacity.
 	 */
 	if (sdp->fix_capacity ||
-	    (sdp->guess_capacity && (sdkp->capacity & 0x01))) {
+	    (sdp->guess_capacity && (sdkp->capacity & 0x01)) ||
+	    (sdp->check_capacity &&
+			sd_read_last_sector(sdkp, buffer, sector_size) < 0)) {
 		sd_printk(KERN_INFO, sdkp, "Adjusting the sector count "
 				"from its reported value: %llu\n",
 				(unsigned long long) sdkp->capacity);
Index: usb-2.6/drivers/usb/storage/scsiglue.c
===================================================================
--- usb-2.6.orig/drivers/usb/storage/scsiglue.c
+++ usb-2.6/drivers/usb/storage/scsiglue.c
@@ -209,6 +209,14 @@ static int slave_configure(struct scsi_d
 		if (us->fflags & US_FL_CAPACITY_HEURISTICS)
 			sdev->guess_capacity = 1;
 
+		/* To catch devices that have the READ CAPACITY bug and
+		 * aren't on our blacklist, tell the SCSI disk driver to
+		 * test the last sector.  This flag will be ignored if
+		 * either of the previous flags causes the capacity to
+		 * be decremented. */
+		if (!(us->fflags & US_FL_CAPACITY_OK))
+			sdev->check_capacity = 1;
+
 		/* assume SPC3 or latter devices support sense size > 18 */
 		if (sdev->scsi_level > SCSI_SPC_2)
 			us->fflags |= US_FL_SANE_SENSE;
Index: usb-2.6/drivers/usb/storage/transport.c
===================================================================
--- usb-2.6.orig/drivers/usb/storage/transport.c
+++ usb-2.6/drivers/usb/storage/transport.c
@@ -560,6 +560,12 @@ static void last_sector_hacks(struct us_
 	if (sector + 1 != sdkp->capacity)
 		goto done;
 
+	/* Has the capacity already been decremented? */
+	if (sdkp->device->fix_capacity) {
+		us->use_last_sector_hacks = 0;
+		return;
+	}
+
 	if (srb->result == SAM_STAT_GOOD && scsi_get_resid(srb) == 0) {
 
 		/* The command succeeded.  We know this device doesn't

--
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