Re-introduce "[SCSI] extend the last_sector_bug flag to cover more sectors"
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Aug 2008 04:42:21 +0000 (21:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Aug 2008 04:42:21 +0000 (21:42 -0700)
This re-introduces commit 2b142900784c6e38c8d39fa57d5f95ef08e735d8,
which was reverted due to the regression it caused by commit
fca082c9f1e11ec07efa8d2f9f13688521253f36.

That regression was not root-caused by the original commit, it was just
uncovered by it, and the real fix was done by Alan Stern in commit
580da34847488b404218d1d7f53b156f245f5555 ("Fix USB storage hang on
command abort").

We can thus re-introduce the change that was confirmed by Alan Jenkins
to be still required by his odd card reader.

Cc: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/scsi/sd.c
drivers/scsi/sd.h
include/scsi/scsi_device.h

index 8e08d51a0f0531d8bf661dc0d3bf8f822d35c357..e5e7d78564545543925fc6136c6e7c7fc994c028 100644 (file)
@@ -375,6 +375,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        struct gendisk *disk = rq->rq_disk;
        struct scsi_disk *sdkp;
        sector_t block = rq->sector;
+       sector_t threshold;
        unsigned int this_count = rq->nr_sectors;
        unsigned int timeout = sdp->timeout;
        int ret;
@@ -422,13 +423,21 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
        }
 
        /*
-        * Some devices (some sdcards for one) don't like it if the
-        * last sector gets read in a larger then 1 sector read.
+        * Some SD card readers can't handle multi-sector accesses which touch
+        * the last one or two hardware sectors.  Split accesses as needed.
         */
-       if (unlikely(sdp->last_sector_bug &&
-           rq->nr_sectors > sdp->sector_size / 512 &&
-           block + this_count == get_capacity(disk)))
-               this_count -= sdp->sector_size / 512;
+       threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS *
+               (sdp->sector_size / 512);
+
+       if (unlikely(sdp->last_sector_bug && block + this_count > threshold)) {
+               if (block < threshold) {
+                       /* Access up to the threshold but not beyond */
+                       this_count = threshold - block;
+               } else {
+                       /* Access only a single hardware sector */
+                       this_count = sdp->sector_size / 512;
+               }
+       }
 
        SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
                                        (unsigned long long)block));
index 550b2f70a1f8330f93fea284c19f13554297ef86..95b9f06534d579bf72797c20ecd5546970f4b190 100644 (file)
  */
 #define SD_BUF_SIZE            512
 
+/*
+ * Number of sectors at the end of the device to avoid multi-sector
+ * accesses to in the case of last_sector_bug
+ */
+#define SD_LAST_BUGGY_SECTORS  8
+
 struct scsi_disk {
        struct scsi_driver *driver;     /* always &sd_template */
        struct scsi_device *device;
index 9cecc409f0f8418cea3e547447e210d8304f4178..291d56a19167db9b8688dbbba446d051daae3b17 100644 (file)
@@ -140,7 +140,8 @@ struct scsi_device {
        unsigned fix_capacity:1;        /* READ_CAPACITY is too high by 1 */
        unsigned guess_capacity:1;      /* READ_CAPACITY might be too high by 1 */
        unsigned retry_hwerror:1;       /* Retry HARDWARE_ERROR */
-       unsigned last_sector_bug:1;     /* Always read last sector in a 1 sector read */
+       unsigned last_sector_bug:1;     /* do not use multisector accesses on
+                                          SD_LAST_BUGGY_SECTORS */
 
        DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
        struct list_head event_list;    /* asserted events */