mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-20 13:41:30 +00:00
[SCSI] extend the last_sector_bug flag to cover more sectors
The last_sector_bug flag was added to work around a bug in certain usb cardreaders, where they would crash if a multiple sector read included the last sector. The original implementation avoids this by e.g. splitting an 8 sector read which includes the last sector into a 7 sector read, and a single sector read for the last sector. The flag is enabled for all USB devices. This revealed a second bug in other usb cardreaders, which crash when they get a multiple sector read which stops 1 sector short of the last sector. Affected hardware includes the Kingston "MobileLite" external USB cardreader and the internal USB cardreader on the Asus EeePC. Extend the last_sector_bug workaround to ensure that any access which touches the last 8 hardware sectors of the device is a single sector long. Requests are shrunk as necessary to meet this constraint. This gives us a safety margin against potential unknown or future bugs affecting multi-sector access to the end of the device. The two known bugs only affect the last 2 sectors. However, they suggest that these devices are prone to fencepost errors and that multi-sector access to the end of the device is not well tested. Popular OS's use multi-sector accesses, but they rarely read the last few sectors. Linux (with udev & vol_id) automatically reads sectors from the end of the device on insertion. It is assumed that single sector accesses are more thoroughly tested during development. Signed-off-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk> Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
4cfc51017d
commit
2b14290078
3 changed files with 23 additions and 7 deletions
|
@ -375,6 +375,7 @@ static int sd_prep_fn(struct request_queue *q, struct request *rq)
|
||||||
struct gendisk *disk = rq->rq_disk;
|
struct gendisk *disk = rq->rq_disk;
|
||||||
struct scsi_disk *sdkp;
|
struct scsi_disk *sdkp;
|
||||||
sector_t block = rq->sector;
|
sector_t block = rq->sector;
|
||||||
|
sector_t threshold;
|
||||||
unsigned int this_count = rq->nr_sectors;
|
unsigned int this_count = rq->nr_sectors;
|
||||||
unsigned int timeout = sdp->timeout;
|
unsigned int timeout = sdp->timeout;
|
||||||
int ret;
|
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
|
* Some SD card readers can't handle multi-sector accesses which touch
|
||||||
* last sector gets read in a larger then 1 sector read.
|
* the last one or two hardware sectors. Split accesses as needed.
|
||||||
*/
|
*/
|
||||||
if (unlikely(sdp->last_sector_bug &&
|
threshold = get_capacity(disk) - SD_LAST_BUGGY_SECTORS *
|
||||||
rq->nr_sectors > sdp->sector_size / 512 &&
|
(sdp->sector_size / 512);
|
||||||
block + this_count == get_capacity(disk)))
|
|
||||||
this_count -= 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",
|
SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
|
||||||
(unsigned long long)block));
|
(unsigned long long)block));
|
||||||
|
|
|
@ -31,6 +31,12 @@
|
||||||
*/
|
*/
|
||||||
#define SD_BUF_SIZE 512
|
#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_disk {
|
||||||
struct scsi_driver *driver; /* always &sd_template */
|
struct scsi_driver *driver; /* always &sd_template */
|
||||||
struct scsi_device *device;
|
struct scsi_device *device;
|
||||||
|
|
|
@ -140,7 +140,8 @@ struct scsi_device {
|
||||||
unsigned fix_capacity:1; /* READ_CAPACITY is too high by 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 guess_capacity:1; /* READ_CAPACITY might be too high by 1 */
|
||||||
unsigned retry_hwerror:1; /* Retry HARDWARE_ERROR */
|
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 */
|
DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */
|
||||||
struct list_head event_list; /* asserted events */
|
struct list_head event_list; /* asserted events */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue