Limit lifetime of scsi commands receiving DID_RESET status. Signed-off-by: Michael Reed <mdr@xxxxxxx>
Limit lifetime of scsi commands receiving DID_RESET status. Signed-off-by: Michael Reed <mdr@xxxxxxx> --- rg61u/include/scsi/scsi.h 2006-10-31 21:08:47.000000000 -0600 +++ rg61/include/scsi/scsi.h 2007-01-29 15:58:40.633779567 -0600 @@ -353,6 +353,7 @@ struct scsi_lun { #define SCSI_MLQUEUE_HOST_BUSY 0x1055 #define SCSI_MLQUEUE_DEVICE_BUSY 0x1056 #define SCSI_MLQUEUE_EH_RETRY 0x1057 +#define SCSI_MLQUEUE_DID_RESET 0x1058 /* * Use these to separate status msg and our bytes --- rg61u/drivers/scsi/scsi_lib.c 2007-01-29 15:11:11.466213588 -0600 +++ rg61/drivers/scsi/scsi_lib.c 2007-01-29 15:58:40.637779387 -0600 @@ -101,10 +101,10 @@ static void scsi_unprep_request(struct r * * Returns: Nothing. * - * Notes: We do this for one of two cases. Either the host is busy + * Notes: We do this for one of three cases. 1) the host is busy * and it cannot accept any more commands for the time being, - * or the device returned QUEUE_FULL and can accept no more - * commands. + * 2) the device returned QUEUE_FULL and can accept no more + * commands, or 3) the LLDD returned DID_RESET. * Notes: This could be called either from an interrupt context or a * normal process context. */ @@ -138,9 +138,11 @@ int scsi_queue_insert(struct scsi_cmnd * /* * Decrement the counters, since these commands are no longer - * active on the host/device. + * active on the host/device. If the reason is SCSI_MLQUEUE_DID_RESET + * then scsi_device_unbusy() was called by scsi_finish_command(). */ - scsi_device_unbusy(device); + if (reason != SCSI_MLQUEUE_DID_RESET) + scsi_device_unbusy(device); /* * Requeue this command. It will go before all other commands @@ -792,6 +794,33 @@ static void scsi_release_buffers(struct } /* + * Function: scsi_command_expired() + * + * Purpose: Check scsi a command's age before retrying it. + * + * Arguments: cmd - command that we are checking for timeout. + * + * Returns: non-zero if command has exceeded its lifetime + * zero otherwise + * + * Notes: A command's lifetime is considered to be the number + * of (retries permitted plus one) * command timeout. + * + */ +static int scsi_command_expired(struct scsi_cmnd *cmd) +{ + int ret = 0; + unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command; + if (time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) { + sdev_printk(KERN_ERR, cmd->device, + "timing out command, waited %lus\n", + wait_for/HZ); + ret = 1; + } + return ret; +} + +/* * Function: scsi_io_completion() * * Purpose: Completion processing for block device I/O requests. @@ -962,12 +991,26 @@ void scsi_io_completion(struct scsi_cmnd } } 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. + * If no data was transferred and the command has not expired, + * just reinsert the command on the queue. If the command + * HAS expired, we fall through to call scsi_end_request. + * + * If data was transferred, regenerate the command to transfer + * only untransferred data by calling scsi_requeue_command(). */ - scsi_requeue_command(q, cmd); - return; + if (!good_bytes) { + if (!(scsi_command_expired(cmd))) { + scsi_queue_insert(cmd, SCSI_MLQUEUE_DID_RESET); + return; + } + /* fall through to call scsi_end_request() */ + } + else { + scsi_requeue_command(q, cmd); + return; + } } if (result) { if (!(req->cmd_flags & REQ_QUIET)) { @@ -1381,17 +1424,12 @@ static void scsi_kill_request(struct req static void scsi_softirq_done(struct request *rq) { struct scsi_cmnd *cmd = rq->completion_data; - unsigned long wait_for = (cmd->allowed + 1) * cmd->timeout_per_command; int disposition; INIT_LIST_HEAD(&cmd->eh_entry); disposition = scsi_decide_disposition(cmd); - if (disposition != SUCCESS && - time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) { - sdev_printk(KERN_ERR, cmd->device, - "timing out command, waited %lus\n", - wait_for/HZ); + if (disposition != SUCCESS && scsi_command_expired(cmd)) { disposition = SUCCESS; }