[PATCH] IDE CD end-of media error fix

This is a patch from Alan that fixes a real ide-cd.c regression causing
bogus "Media Check" failures for perfectly valid Fedora install ISOs, on
certain CD-ROM drives.

This is a forward port to 2.6.16 (from RHEL) of the minimal changes for the
end of media problem.  It may not be sufficient for some controllers
(promise notably) and it does not touch the locking so the error path
locking is as horked as in mainstream.

From: Ingo Molnar <mingo@elte.hu>

I have ported the patch to 2.6.17-rc4 and tested it by provoking
end-of-media IO errors with an unaligned ISO image.  Unlike the vanilla
kernel, the patched kernel interpreted the error condition correctly with
512 byte granularity:

 hdc: command error: status=0x51 { DriveReady SeekComplete Error }
 hdc: command error: error=0x54 { AbortedCommand LastFailedSense=0x05 }
 ide: failed opcode was: unknown
 ATAPI device hdc:
   Error: Illegal request -- (Sense key=0x05)
   Illegal mode for this track or incompatible medium -- (asc=0x64, ascq=0x00)
   The failed "Read 10" packet command was:
   "28 00 00 04 fb 78 00 00 06 00 00 00 00 00 00 00 "
 end_request: I/O error, dev hdc, sector 1306080
 Buffer I/O error on device hdc, logical block 163260
 Buffer I/O error on device hdc, logical block 163261
 Buffer I/O error on device hdc, logical block 163262

the unpatched kernel produces an incorrect error dump:

 hdc: command error: status=0x51 { DriveReady SeekComplete Error }
 hdc: command error: error=0x54 { AbortedCommand LastFailedSense=0x05 }
 ide: failed opcode was: unknown
 end_request: I/O error, dev hdc, sector 1306080
 Buffer I/O error on device hdc, logical block 163260
 hdc: command error: status=0x51 { DriveReady SeekComplete Error }
 hdc: command error: error=0x54 { AbortedCommand LastFailedSense=0x05 }
 ide: failed opcode was: unknown
 end_request: I/O error, dev hdc, sector 1306088
 Buffer I/O error on device hdc, logical block 163261
 hdc: command error: status=0x51 { DriveReady SeekComplete Error }
 hdc: command error: error=0x54 { AbortedCommand LastFailedSense=0x05 }
 ide: failed opcode was: unknown
 end_request: I/O error, dev hdc, sector 1306096
 Buffer I/O error on device hdc, logical block 163262

I do not have the right type of CD-ROM drive to reproduce the end-of-media
data corruption bug myself, but this same patch in RHEL solved it.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Bartlomiej Zolnierkiewicz <B.Zolnierkiewicz@elka.pw.edu.pl>
Cc: Jens Axboe <axboe@suse.de>
Cc: Matt Mackall <mpm@selenic.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Alan Cox 2006-06-25 05:47:44 -07:00 committed by Linus Torvalds
parent 8e3a67a992
commit dbe217af3b
3 changed files with 151 additions and 27 deletions

View File

@ -395,7 +395,8 @@ static int cdrom_log_sense(ide_drive_t *drive, struct request *rq,
* we cannot reliably check if drive can auto-close
*/
if (rq->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24)
log = 0;
break;
log = 1;
break;
case UNIT_ATTENTION:
/*
@ -417,6 +418,11 @@ void cdrom_analyze_sense_data(ide_drive_t *drive,
struct request *failed_command,
struct request_sense *sense)
{
unsigned long sector;
unsigned long bio_sectors;
unsigned long valid;
struct cdrom_info *info = drive->driver_data;
if (!cdrom_log_sense(drive, failed_command, sense))
return;
@ -429,6 +435,37 @@ void cdrom_analyze_sense_data(ide_drive_t *drive,
if (sense->sense_key == 0x05 && sense->asc == 0x24)
return;
if (sense->error_code == 0x70) { /* Current Error */
switch(sense->sense_key) {
case MEDIUM_ERROR:
case VOLUME_OVERFLOW:
case ILLEGAL_REQUEST:
if (!sense->valid)
break;
if (failed_command == NULL ||
!blk_fs_request(failed_command))
break;
sector = (sense->information[0] << 24) |
(sense->information[1] << 16) |
(sense->information[2] << 8) |
(sense->information[3]);
bio_sectors = bio_sectors(failed_command->bio);
if (bio_sectors < 4)
bio_sectors = 4;
if (drive->queue->hardsect_size == 2048)
sector <<= 2; /* Device sector size is 2K */
sector &= ~(bio_sectors -1);
valid = (sector - failed_command->sector) << 9;
if (valid < 0)
valid = 0;
if (sector < get_capacity(info->disk) &&
drive->probed_capacity - sector < 4 * 75) {
set_capacity(info->disk, sector);
}
}
}
#if VERBOSE_IDE_CD_ERRORS
{
int i;
@ -609,17 +646,23 @@ static void cdrom_end_request (ide_drive_t *drive, int uptodate)
sense = failed->sense;
failed->sense_len = rq->sense_len;
}
cdrom_analyze_sense_data(drive, failed, sense);
/*
* now end failed request
*/
spin_lock_irqsave(&ide_lock, flags);
end_that_request_chunk(failed, 0, failed->data_len);
end_that_request_last(failed, 0);
spin_unlock_irqrestore(&ide_lock, flags);
}
cdrom_analyze_sense_data(drive, failed, sense);
if (blk_fs_request(failed)) {
if (ide_end_dequeued_request(drive, failed, 0,
failed->hard_nr_sectors))
BUG();
} else {
spin_lock_irqsave(&ide_lock, flags);
end_that_request_chunk(failed, 0,
failed->data_len);
end_that_request_last(failed, 0);
spin_unlock_irqrestore(&ide_lock, flags);
}
} else
cdrom_analyze_sense_data(drive, NULL, sense);
}
if (!rq->current_nr_sectors && blk_fs_request(rq))
@ -633,6 +676,13 @@ static void cdrom_end_request (ide_drive_t *drive, int uptodate)
ide_end_request(drive, uptodate, nsectors);
}
static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 stat)
{
if (stat & 0x80)
return;
ide_dump_status(drive, msg, stat);
}
/* Returns 0 if the request should be continued.
Returns 1 if the request was ended. */
static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
@ -761,16 +811,16 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
sense_key == DATA_PROTECT) {
/* No point in retrying after an illegal
request or data protect error.*/
ide_dump_status (drive, "command error", stat);
ide_dump_status_no_sense (drive, "command error", stat);
do_end_request = 1;
} else if (sense_key == MEDIUM_ERROR) {
/* No point in re-trying a zillion times on a bad
* sector... If we got here the error is not correctable */
ide_dump_status (drive, "media error (bad sector)", stat);
ide_dump_status_no_sense (drive, "media error (bad sector)", stat);
do_end_request = 1;
} else if (sense_key == BLANK_CHECK) {
/* Disk appears blank ?? */
ide_dump_status (drive, "media error (blank)", stat);
ide_dump_status_no_sense (drive, "media error (blank)", stat);
do_end_request = 1;
} else if ((err & ~ABRT_ERR) != 0) {
/* Go to the default handler
@ -782,13 +832,27 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
do_end_request = 1;
}
if (do_end_request)
cdrom_end_request(drive, 0);
/* End a request through request sense analysis when we have
sense data. We need this in order to perform end of media
processing */
/* If we got a CHECK_CONDITION status,
queue a request sense command. */
if ((stat & ERR_STAT) != 0)
cdrom_queue_request_sense(drive, NULL, NULL);
if (do_end_request) {
if (stat & ERR_STAT) {
unsigned long flags;
spin_lock_irqsave(&ide_lock, flags);
blkdev_dequeue_request(rq);
HWGROUP(drive)->rq = NULL;
spin_unlock_irqrestore(&ide_lock, flags);
cdrom_queue_request_sense(drive, rq->sense, rq);
} else
cdrom_end_request(drive, 0);
} else {
/* If we got a CHECK_CONDITION status,
queue a request sense command. */
if (stat & ERR_STAT)
cdrom_queue_request_sense(drive, NULL, NULL);
}
} else {
blk_dump_rq_flags(rq, "ide-cd: bad rq");
cdrom_end_request(drive, 0);
@ -1491,8 +1555,7 @@ static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
}
static
int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
{
struct request_sense sense;
int retries = 10;
@ -2220,6 +2283,9 @@ static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense)
toc->capacity = 0x1fffff;
set_capacity(info->disk, toc->capacity * sectors_per_frame);
/* Save a private copy of te TOC capacity for error handling */
drive->probed_capacity = toc->capacity * sectors_per_frame;
blk_queue_hardsect_size(drive->queue,
sectors_per_frame << SECTOR_BITS);
@ -2342,6 +2408,7 @@ static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense)
if (!stat && (last_written > toc->capacity)) {
toc->capacity = last_written;
set_capacity(info->disk, toc->capacity * sectors_per_frame);
drive->probed_capacity = toc->capacity * sectors_per_frame;
}
/* Remember that we've read this stuff. */
@ -2698,14 +2765,11 @@ int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
* any other way to detect this...
*/
if (sense.sense_key == NOT_READY) {
if (sense.asc == 0x3a) {
if (sense.ascq == 1)
return CDS_NO_DISC;
else if (sense.ascq == 0 || sense.ascq == 2)
return CDS_TRAY_OPEN;
}
if (sense.asc == 0x3a && sense.ascq == 1)
return CDS_NO_DISC;
else
return CDS_TRAY_OPEN;
}
return CDS_DRIVE_NOT_READY;
}

View File

@ -222,6 +222,63 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
return ide_stopped;
}
/**
* ide_end_dequeued_request - complete an IDE I/O
* @drive: IDE device for the I/O
* @uptodate:
* @nr_sectors: number of sectors completed
*
* Complete an I/O that is no longer on the request queue. This
* typically occurs when we pull the request and issue a REQUEST_SENSE.
* We must still finish the old request but we must not tamper with the
* queue in the meantime.
*
* NOTE: This path does not handle barrier, but barrier is not supported
* on ide-cd anyway.
*/
int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
int uptodate, int nr_sectors)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&ide_lock, flags);
BUG_ON(!(rq->flags & REQ_STARTED));
/*
* if failfast is set on a request, override number of sectors and
* complete the whole request right now
*/
if (blk_noretry_request(rq) && end_io_error(uptodate))
nr_sectors = rq->hard_nr_sectors;
if (!blk_fs_request(rq) && end_io_error(uptodate) && !rq->errors)
rq->errors = -EIO;
/*
* decide whether to reenable DMA -- 3 is a random magic for now,
* if we DMA timeout more than 3 times, just stay in PIO
*/
if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) {
drive->state = 0;
HWGROUP(drive)->hwif->ide_dma_on(drive);
}
if (!end_that_request_first(rq, uptodate, nr_sectors)) {
add_disk_randomness(rq->rq_disk);
if (blk_rq_tagged(rq))
blk_queue_end_tag(drive->queue, rq);
end_that_request_last(rq, uptodate);
ret = 0;
}
spin_unlock_irqrestore(&ide_lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(ide_end_dequeued_request);
/**
* ide_complete_pm_request - end the current Power Management request
* @drive: target drive

View File

@ -630,6 +630,7 @@ typedef struct ide_drive_s {
unsigned int usage; /* current "open()" count for drive */
unsigned int failures; /* current failure count */
unsigned int max_failures; /* maximum allowed failure count */
u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
u64 capacity64; /* total number of sectors */
@ -1005,6 +1006,8 @@ extern ide_hwif_t ide_hwifs[]; /* master data repository */
extern int noautodma;
extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs);
int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
int uptodate, int nr_sectors);
/*
* This is used on exit from the driver to designate the next irq handler