Re: 2.6.17-mm5 dislikes raid-1, just like mm4

James Bottomley
2006-07-02T13:53:39Z

On Sun, 2006-07-02 at 17:13 +1200, Reuben Farrelly wrote:
> Just for kicks, after testing those two trees (see previous email) I
> took my
> 2.6.17-mm5 without git-scsi-misc and then patched git-scsi-misc.patch
> back in,
> rebuilt and rebooted and noted that RAID broke again. Reverted the
> patch and it
> all worked.
>
> So I can conclude that definitely and reproduceably that's the
> one.........

OK, I have a theory. I think

[SCSI] sd/scsi_lib simplify sd_rw_intr and scsi_io_completion

Failed to take into account completion of zero length commands (which is
what a flush is). Could you try the whole of -mm with this patch?

Thanks,

James

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 4c4add5..3d04a9f 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -855,7 +855,8 @@ static void scsi_release_buffers(struct
* b) We can just use scsi_requeue_command() here. This would
* be used if we just wanted to retry, for example.
*/
-void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
+void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes,
+ unsigned int block_bytes)
{
int result = cmd->result;
int this_count = cmd->bufflen;
@@ -920,72 +921,87 @@ void scsi_io_completion(struct scsi_cmnd
* Next deal with any sectors which we were able to correctly
* handle.
*/
- if (good_bytes > 0) {
- SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, "
- "%d bytes done.\n",
+ if (good_bytes >= 0) {
+ SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, %d bytes done.\n",
req->nr_sectors, good_bytes));
SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg));

if (clear_errors)
req->errors = 0;
+ /*
+ * If multiple sectors are requested in one buffer, then
+ * they will have been finished off by the first command.
+ * If not, then we have a multi-buffer command.
+ *
+ * If block_bytes != 0, it means we had a medium error
+ * of some sort, and that we want to mark some number of
+ * sectors as not uptodate. Thus we want to inhibit
+ * requeueing right here - we will requeue down below
+ * when we handle the bad sectors.
+ */

- /* A number of bytes were successfully read. If there
- * is leftovers and there is some kind of error
- * (result != 0), retry the rest.
+ /*
+ * If the command completed without error, then either
+ * finish off the rest of the command, or start a new one.
*/
- if (scsi_end_request(cmd, 1, good_bytes, !!result) == NULL)
+ if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL)
return;
}
-
- /* good_bytes = 0, or (inclusive) there were leftovers and
- * result = 0, so scsi_end_request couldn't retry.
+ /*
+ * Now, if we were good little boys and girls, Santa left us a request
+ * sense buffer. We can extract information from this, so we
+ * can choose a block to remap, etc.
*/
if (sense_valid && !sense_deferred) {
switch (sshdr.sense_key) {
case UNIT_ATTENTION:
if (cmd->device->removable) {
- /* Detected disc change. Set a bit
+ /* detected disc change. set a bit
* and quietly refuse further access.
*/
cmd->device->changed = 1;
- scsi_end_request(cmd, 0, this_count, 1);
+ scsi_end_request(cmd, 0,
+ this_count, 1);
return;
} else {
- /* Must have been a power glitch, or a
- * bus reset. Could not have been a
- * media change, so we just retry the
- * request and see what happens.
- */
+ /*
+ * Must have been a power glitch, or a
+ * bus reset. Could not have been a
+ * media change, so we just retry the
+ * request and see what happens.
+ */
scsi_requeue_command(q, cmd);
return;
}
break;
case ILLEGAL_REQUEST:
- /* If we had an ILLEGAL REQUEST returned, then
- * we may have performed an unsupported
- * command. The only thing this should be
- * would be a ten byte read where only a six
- * byte read was supported. Also, on a system
- * where READ CAPACITY failed, we may have
- * read past the end of the disk.
- */
+ /*
+ * If we had an ILLEGAL REQUEST returned, then we may
+ * have performed an unsupported command. The only
+ * thing this should be would be a ten byte read where
+ * only a six byte read was supported. Also, on a
+ * system where READ CAPACITY failed, we may have read
+ * past the end of the disk.
+ */
if ((cmd->device->use_10_for_rw &&
sshdr.asc == 0x20 && sshdr.ascq == 0x00) &&
(cmd->cmnd[0] == READ_10 ||
cmd->cmnd[0] == WRITE_10)) {
cmd->device->use_10_for_rw = 0;
- /* This will cause a retry with a
- * 6-byte command.
+ /*
+ * This will cause a retry with a 6-byte
+ * command.
*/
scsi_requeue_command(q, cmd);
- return;
+ result = 0;
} else {
scsi_end_request(cmd, 0, this_count, 1);
return;
}
break;
case NOT_READY:
- /* If the device is in the process of becoming
+ /*
+ * If the device is in the process of becoming
* ready, or has a temporary blockage, retry.
*/
if (sshdr.asc == 0x04) {
@@ -1005,7 +1021,7 @@ void scsi_io_completion(struct scsi_cmnd
}
if (!(req->flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
- "Device not ready: ");
+ "Device not ready: ");
scsi_print_sense_hdr("", &sshdr);
}
scsi_end_request(cmd, 0, this_count, 1);
@@ -1013,21 +1029,21 @@ void scsi_io_completion(struct scsi_cmnd
case VOLUME_OVERFLOW:
if (!(req->flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
- "Volume overflow, CDB: ");
+ "Volume overflow, CDB: ");
__scsi_print_command(cmd->data_cmnd);
scsi_print_sense("", cmd);
}
- /* See SSC3rXX or current. */
- scsi_end_request(cmd, 0, this_count, 1);
+ scsi_end_request(cmd, 0, block_bytes, 1);
return;
default:
break;
}
- }
+ } /* driver byte != 0 */
if (host_byte(result) == DID_RESET) {
- /* Third party bus reset or reset for error recovery
- * reasons. Just retry the request and see what
- * happens.
+ /*
+ * Third party bus reset or reset for error
+ * recovery reasons. Just retry the request
+ * and see what happens.
*/
scsi_requeue_command(q, cmd);
return;
@@ -1035,13 +1051,21 @@ void scsi_io_completion(struct scsi_cmnd
if (result) {
if (!(req->flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
- "SCSI error: return code = 0x%08x\n",
- result);
+ "SCSI error: return code = 0x%x\n", result);
+
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
}
+ /*
+ * Mark a single buffer as not uptodate. Queue the remainder.
+ * We sometimes get this cruft in the event that a medium error
+ * isn't properly reported.
+ */
+ block_bytes = req->hard_cur_sectors << 9;
+ if (!block_bytes)
+ block_bytes = req->data_len;
+ scsi_end_request(cmd, 0, block_bytes, 1);
}
- scsi_end_request(cmd, 0, this_count, !result);
}
EXPORT_SYMBOL(scsi_io_completion);

@@ -1145,7 +1169,7 @@ static void scsi_blk_pc_done(struct scsi
* successfully. Since this is a REQ_BLOCK_PC command the
* caller should check the request's errors value
*/
- scsi_io_completion(cmd, cmd->bufflen);
+ scsi_io_completion(cmd, cmd->bufflen, 0);
}

static void scsi_setup_blk_pc_cmnd(struct scsi_cmnd *cmd)
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index f899ff0..3541990 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -891,10 +891,11 @@ #endif
static void sd_rw_intr(struct scsi_cmnd * SCpnt)
{
int result = SCpnt->result;
- unsigned int xfer_size = SCpnt->request_bufflen;
- unsigned int good_bytes = result ? 0 : xfer_size;
- u64 start_lba = SCpnt->request->sector;
- u64 bad_lba;
+ int this_count = SCpnt->request_bufflen;
+ int good_bytes = (result == 0 ? this_count : 0);
+ sector_t block_sectors = 1;
+ u64 first_err_block;
+ sector_t error_sector;
struct scsi_sense_hdr sshdr;
int sense_valid = 0;
int sense_deferred = 0;
@@ -905,6 +906,7 @@ static void sd_rw_intr(struct scsi_cmnd
if (sense_valid)
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
+
#ifdef CONFIG_SCSI_LOGGING
SCSI_LOG_HLCOMPLETE(1, printk("sd_rw_intr: %s: res=0x%x\n",
SCpnt->request->rq_disk->disk_name, result));
@@ -914,72 +916,89 @@ #ifdef CONFIG_SCSI_LOGGING
sshdr.sense_key, sshdr.asc, sshdr.ascq));
}
#endif
- if (driver_byte(result) != DRIVER_SENSE &&
- (!sense_valid || sense_deferred))
- goto out;
+ /*
+ Handle MEDIUM ERRORs that indicate partial success. Since this is a
+ relatively rare error condition, no care is taken to avoid
+ unnecessary additional work such as memcpy's that could be avoided.
+ */
+ if (driver_byte(result) != 0 &&
+ sense_valid && !sense_deferred) {
+ switch (sshdr.sense_key) {
+ case MEDIUM_ERROR:
+ if (!blk_fs_request(SCpnt->request))
+ break;
+ info_valid = scsi_get_sense_info_fld(
+ SCpnt->sense_buffer, SCSI_SENSE_BUFFERSIZE,
+ &first_err_block);
+ /*
+ * May want to warn and skip if following cast results
+ * in actual truncation (if sector_t < 64 bits)
+ */
+ error_sector = (sector_t)first_err_block;
+ if (SCpnt->request->bio != NULL)
+ block_sectors = bio_sectors(SCpnt->request->bio);
+ switch (SCpnt->device->sector_size) {
+ case 1024:
+ error_sector <<= 1;
+ if (block_sectors < 2)
+ block_sectors = 2;
+ break;
+ case 2048:
+ error_sector <<= 2;
+ if (block_sectors < 4)
+ block_sectors = 4;
+ break;
+ case 4096:
+ error_sector <<=3;
+ if (block_sectors < 8)
+ block_sectors = 8;
+ break;
+ case 256:
+ error_sector >>= 1;
+ break;
+ default:
+ break;
+ }

- switch (sshdr.sense_key) {
- case HARDWARE_ERROR:
- case MEDIUM_ERROR:
- if (!blk_fs_request(SCpnt->request))
- goto out;
- info_valid = scsi_get_sense_info_fld(SCpnt->sense_buffer,
- SCSI_SENSE_BUFFERSIZE,
- &bad_lba);
- if (!info_valid)
- goto out;
- if (xfer_size <= SCpnt->device->sector_size)
- goto out;
- switch (SCpnt->device->sector_size) {
- case 256:
- start_lba <<= 1;
- break;
- case 512:
+ error_sector &= ~(block_sectors - 1);
+ good_bytes = (error_sector - SCpnt->request->sector) << 9;
+ if (good_bytes < 0 || good_bytes >= this_count)
+ good_bytes = 0;
break;
- case 1024:
- start_lba >>= 1;
- break;
- case 2048:
- start_lba >>= 2;
+
+ case RECOVERED_ERROR: /* an error occurred, but it recovered */
+ case NO_SENSE: /* LLDD got sense data */
+ /*
+ * Inform the user, but make sure that it's not treated
+ * as a hard error.
+ */
+ scsi_print_sense("sd", SCpnt);
+ SCpnt->result = 0;
+ memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ good_bytes = this_count;
break;
- case 4096:
- start_lba >>= 3;
+
+ case ILLEGAL_REQUEST:
+ if (SCpnt->device->use_10_for_rw &&
+ (SCpnt->cmnd[0] == READ_10 ||
+ SCpnt->cmnd[0] == WRITE_10))
+ SCpnt->device->use_10_for_rw = 0;
+ if (SCpnt->device->use_10_for_ms &&
+ (SCpnt->cmnd[0] == MODE_SENSE_10 ||
+ SCpnt->cmnd[0] == MODE_SELECT_10))
+ SCpnt->device->use_10_for_ms = 0;
break;
+
default:
- /* Print something here with limiting frequency. */
- goto out;
break;
}
- /* This computation should always be done in terms of
- * the resolution of the device's medium.
- */
- good_bytes = (bad_lba - start_lba)*SCpnt->device->sector_size;
- break;
- case RECOVERED_ERROR:
- case NO_SENSE:
- /* Inform the user, but make sure that it's not treated
- * as a hard error.
- */
- scsi_print_sense("sd", SCpnt);
- SCpnt->result = 0;
- memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
- good_bytes = xfer_size;
- break;
- case ILLEGAL_REQUEST:
- if (SCpnt->device->use_10_for_rw &&
- (SCpnt->cmnd[0] == READ_10 ||
- SCpnt->cmnd[0] == WRITE_10))
- SCpnt->device->use_10_for_rw = 0;
- if (SCpnt->device->use_10_for_ms &&
- (SCpnt->cmnd[0] == MODE_SENSE_10 ||
- SCpnt->cmnd[0] == MODE_SELECT_10))
- SCpnt->device->use_10_for_ms = 0;
- break;
- default:
- break;
}
- out:
- scsi_io_completion(SCpnt, good_bytes);
+ /*
+ * This calls the generic completion function, now that we know
+ * how many actual sectors finished, and how many sectors we need
+ * to say have failed.
+ */
+ scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
}

static int media_not_present(struct scsi_disk *sdkp,
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index fd94408..ebf6579 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -292,7 +292,7 @@ #endif
* how many actual sectors finished, and how many sectors we need
* to say have failed.
*/
- scsi_io_completion(SCpnt, good_bytes);
+ scsi_io_completion(SCpnt, good_bytes, block_sectors << 9);
}

static int sr_init_command(struct scsi_cmnd * SCpnt)
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index 371f70d..e46cd40 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -143,7 +143,7 @@ #define SCSI_STATE_MLQUEUE 0x100

extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t);
extern void scsi_put_command(struct scsi_cmnd *);
-extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
+extern void scsi_io_completion(struct scsi_cmnd *, unsigned int, unsigned int);
extern void scsi_finish_command(struct scsi_cmnd *cmd);
extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd);