aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@suse.de>2010-12-17 15:36:34 -0500
committerJames Bottomley <James.Bottomley@suse.de>2010-12-23 00:26:48 -0500
commita8733c7baf457b071528e385a0b7d4aaec79287c (patch)
tree5eb6b8c52b20cb604d76ba21b15304fda4703a06 /drivers/scsi/sd.c
parente9ccc998b70fbe59626f393bb0328402159c6b5c (diff)
[SCSI] fix medium error problems with some arrays which can cause data corruption
Our current handling of medium error assumes that data is returned up to the bad sector. This assumption holds good for all disk devices, all DIF arrays and most ordinary arrays. However, an LSI array engine was recently discovered which reports a medium error without returning any data. This means that when we report good data up to the medium error, we've reported junk originally in the buffer as good. Worse, if the read consists of requested data plus a readahead, and the error occurs in readahead, we'll just strip off the readahead and report junk up to userspace as good data with no error. The fix for this is to have the error position computation take into account the amount of data returned by the driver using the scsi residual data. Unfortunately, not every driver fills in this data, but for those who don't, it's set to zero, which means we'll think a full set of data was transferred and the behaviour will be identical to the prior behaviour of the code (believe the buffer up to the error sector). All modern drivers seem to set the residual, so that should fix up the LSI failure/corruption case. Reported-by: Douglas Gilbert <dgilbert@interlog.com> Cc: Stable Tree <stable@kernel.org> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index d8e2caf9f468..365024b0c407 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1188,6 +1188,12 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
1188 u64 end_lba = blk_rq_pos(scmd->request) + (scsi_bufflen(scmd) / 512); 1188 u64 end_lba = blk_rq_pos(scmd->request) + (scsi_bufflen(scmd) / 512);
1189 u64 bad_lba; 1189 u64 bad_lba;
1190 int info_valid; 1190 int info_valid;
1191 /*
1192 * resid is optional but mostly filled in. When it's unused,
1193 * its value is zero, so we assume the whole buffer transferred
1194 */
1195 unsigned int transferred = scsi_bufflen(scmd) - scsi_get_resid(scmd);
1196 unsigned int good_bytes;
1191 1197
1192 if (scmd->request->cmd_type != REQ_TYPE_FS) 1198 if (scmd->request->cmd_type != REQ_TYPE_FS)
1193 return 0; 1199 return 0;
@@ -1221,7 +1227,8 @@ static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
1221 /* This computation should always be done in terms of 1227 /* This computation should always be done in terms of
1222 * the resolution of the device's medium. 1228 * the resolution of the device's medium.
1223 */ 1229 */
1224 return (bad_lba - start_lba) * scmd->device->sector_size; 1230 good_bytes = (bad_lba - start_lba) * scmd->device->sector_size;
1231 return min(good_bytes, transferred);
1225} 1232}
1226 1233
1227/** 1234/**