diff options
Diffstat (limited to 'drivers/scsi/scsi_lib.c')
-rw-r--r-- | drivers/scsi/scsi_lib.c | 151 |
1 files changed, 87 insertions, 64 deletions
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fa45a1a66867..f2f51e0333eb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -183,13 +183,15 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) * @timeout: request timeout in seconds * @retries: number of times to retry request * @flags: or into request flags; + * @resid: optional residual length * * returns the req->errors value which is the scsi_cmnd result * field. */ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, - unsigned char *sense, int timeout, int retries, int flags) + unsigned char *sense, int timeout, int retries, int flags, + int *resid) { struct request *req; int write = (data_direction == DMA_TO_DEVICE); @@ -224,6 +226,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, if (unlikely(req->data_len > 0 && req->data_len <= bufflen)) memset(buffer + (bufflen - req->data_len), 0, req->data_len); + if (resid) + *resid = req->data_len; ret = req->errors; out: blk_put_request(req); @@ -235,7 +239,8 @@ EXPORT_SYMBOL(scsi_execute); int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, - struct scsi_sense_hdr *sshdr, int timeout, int retries) + struct scsi_sense_hdr *sshdr, int timeout, int retries, + int *resid) { char *sense = NULL; int result; @@ -246,7 +251,7 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, return DRIVER_ERROR << 24; } result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen, - sense, timeout, retries, 0); + sense, timeout, retries, 0, resid); if (sshdr) scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr); @@ -648,8 +653,8 @@ static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd) struct request *req = cmd->request; unsigned long flags; - scsi_unprep_request(req); spin_lock_irqsave(q->queue_lock, flags); + scsi_unprep_request(req); blk_requeue_request(q, req); spin_unlock_irqrestore(q->queue_lock, flags); @@ -875,16 +880,24 @@ static void scsi_end_bidi_request(struct scsi_cmnd *cmd) * (the normal case for most drivers), we don't need * the logic to deal with cleaning up afterwards. * - * We must do one of several things here: + * We must call scsi_end_request(). This will finish off + * the specified number of sectors. If we are done, the + * command block will be released and the queue function + * will be goosed. If we are not done then we have to + * figure out what to do next: + * + * a) We can call scsi_requeue_command(). The request + * will be unprepared and put back on the queue. Then + * a new command will be created for it. This should + * be used if we made forward progress, or if we want + * to switch from READ(10) to READ(6) for example. * - * a) Call scsi_end_request. This will finish off the - * specified number of sectors. If we are done, the - * command block will be released, and the queue - * function will be goosed. If we are not done, then - * scsi_end_request will directly goose the queue. + * b) We can call scsi_queue_insert(). The request will + * be put back on the queue and retried using the same + * command as before, possibly after a delay. * - * b) We can just use scsi_requeue_command() here. This would - * be used if we just wanted to retry, for example. + * c) We can call blk_end_request() with -EIO to fail + * the remainder of the request. */ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) { @@ -896,6 +909,9 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) struct scsi_sense_hdr sshdr; int sense_valid = 0; int sense_deferred = 0; + enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY, + ACTION_DELAYED_RETRY} action; + char *description = NULL; if (result) { sense_valid = scsi_command_normalize_sense(cmd, &sshdr); @@ -947,10 +963,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) return; this_count = blk_rq_bytes(req); - /* good_bytes = 0, or (inclusive) there were leftovers and - * result = 0, so scsi_end_request couldn't retry. - */ - if (sense_valid && !sense_deferred) { + if (host_byte(result) == DID_RESET) { + /* Third party bus reset or reset for error recovery + * reasons. Just retry the command and see what + * happens. + */ + action = ACTION_RETRY; + } else if (sense_valid && !sense_deferred) { switch (sshdr.sense_key) { case UNIT_ATTENTION: if (cmd->device->removable) { @@ -958,16 +977,15 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) * and quietly refuse further access. */ cmd->device->changed = 1; - scsi_end_request(cmd, -EIO, this_count, 1); - return; + description = "Media Changed"; + action = ACTION_FAIL; } 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. + * command and see what happens. */ - scsi_requeue_command(q, cmd); - return; + action = ACTION_RETRY; } break; case ILLEGAL_REQUEST: @@ -983,21 +1001,18 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) sshdr.asc == 0x20 && sshdr.ascq == 0x00) && (cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == WRITE_10)) { + /* This will issue a new 6-byte command. */ cmd->device->use_10_for_rw = 0; - /* This will cause a retry with a - * 6-byte command. - */ - scsi_requeue_command(q, cmd); - } else if (sshdr.asc == 0x10) /* DIX */ - scsi_end_request(cmd, -EIO, this_count, 0); - else - scsi_end_request(cmd, -EIO, this_count, 1); - return; + action = ACTION_REPREP; + } else + action = ACTION_FAIL; + break; case ABORTED_COMMAND: if (sshdr.asc == 0x10) { /* DIF */ - scsi_end_request(cmd, -EIO, this_count, 0); - return; - } + action = ACTION_FAIL; + description = "Data Integrity Failure"; + } else + action = ACTION_RETRY; break; case NOT_READY: /* If the device is in the process of becoming @@ -1012,49 +1027,57 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) case 0x07: /* operation in progress */ case 0x08: /* Long write in progress */ case 0x09: /* self test in progress */ - scsi_requeue_command(q, cmd); - return; - default: + action = ACTION_DELAYED_RETRY; break; } + } else { + description = "Device not ready"; + action = ACTION_FAIL; } - if (!(req->cmd_flags & REQ_QUIET)) - scsi_cmd_print_sense_hdr(cmd, - "Device not ready", - &sshdr); - - scsi_end_request(cmd, -EIO, this_count, 1); - return; + break; case VOLUME_OVERFLOW: - if (!(req->cmd_flags & REQ_QUIET)) { - scmd_printk(KERN_INFO, cmd, - "Volume overflow, CDB: "); - __scsi_print_command(cmd->cmnd); - scsi_print_sense("", cmd); - } /* See SSC3rXX or current. */ - scsi_end_request(cmd, -EIO, this_count, 1); - return; + action = ACTION_FAIL; + break; default: + description = "Unhandled sense code"; + action = ACTION_FAIL; break; } + } else { + description = "Unhandled error code"; + action = ACTION_FAIL; } - if (host_byte(result) == DID_RESET) { - /* Third party bus reset or reset for error recovery - * reasons. Just retry the request and see what - * happens. - */ - scsi_requeue_command(q, cmd); - return; - } - if (result) { + + switch (action) { + case ACTION_FAIL: + /* Give up and fail the remainder of the request */ if (!(req->cmd_flags & REQ_QUIET)) { + if (description) + scmd_printk(KERN_INFO, cmd, "%s", + description); scsi_print_result(cmd); if (driver_byte(result) & DRIVER_SENSE) scsi_print_sense("", cmd); } + blk_end_request(req, -EIO, blk_rq_bytes(req)); + scsi_next_command(cmd); + break; + case ACTION_REPREP: + /* Unprep the request and put it back at the head of the queue. + * A new command will be prepared and issued. + */ + scsi_requeue_command(q, cmd); + break; + case ACTION_RETRY: + /* Retry the same command immediately */ + scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY); + break; + case ACTION_DELAYED_RETRY: + /* Retry the same command after a delay */ + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + break; } - scsi_end_request(cmd, -EIO, this_count, !result); } static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb, @@ -1998,7 +2021,7 @@ scsi_mode_select(struct scsi_device *sdev, int pf, int sp, int modepage, } ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, real_buffer, len, - sshdr, timeout, retries); + sshdr, timeout, retries, NULL); kfree(real_buffer); return ret; } @@ -2063,7 +2086,7 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, memset(buffer, 0, len); result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len, - sshdr, timeout, retries); + sshdr, timeout, retries, NULL); /* This code looks awful: what it's doing is making sure an * ILLEGAL REQUEST sense return identifies the actual command @@ -2145,7 +2168,7 @@ scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries, /* try to eat the UNIT_ATTENTION if there are enough retries */ do { result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr, - timeout, retries); + timeout, retries, NULL); if (sdev->removable && scsi_sense_valid(sshdr) && sshdr->sense_key == UNIT_ATTENTION) sdev->changed = 1; |