[SCSI] use a completion in scsi_send_eh_cmnd
scsi_send_eh_cmnd currently uses a semaphore and an overload of eh_timer to either get a completion for a command for a timeout. Switch to using a completion and wait_for_completion_timeout to simply the code and not having to deal with the races ourselves. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
parent
262eef663b
commit
7dfdc9a52b
3 changed files with 32 additions and 81 deletions
|
@ -416,44 +416,16 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
|
|||
return FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* scsi_eh_times_out - timeout function for error handling.
|
||||
* @scmd: Cmd that is timing out.
|
||||
*
|
||||
* Notes:
|
||||
* During error handling, the kernel thread will be sleeping waiting
|
||||
* for some action to complete on the device. our only job is to
|
||||
* record that it timed out, and to wake up the thread.
|
||||
**/
|
||||
static void scsi_eh_times_out(struct scsi_cmnd *scmd)
|
||||
{
|
||||
scmd->eh_eflags |= SCSI_EH_REC_TIMEOUT;
|
||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__,
|
||||
scmd));
|
||||
|
||||
up(scmd->device->host->eh_action);
|
||||
}
|
||||
|
||||
/**
|
||||
* scsi_eh_done - Completion function for error handling.
|
||||
* @scmd: Cmd that is done.
|
||||
**/
|
||||
static void scsi_eh_done(struct scsi_cmnd *scmd)
|
||||
{
|
||||
/*
|
||||
* if the timeout handler is already running, then just set the
|
||||
* flag which says we finished late, and return. we have no
|
||||
* way of stopping the timeout handler from running, so we must
|
||||
* always defer to it.
|
||||
*/
|
||||
if (del_timer(&scmd->eh_timeout)) {
|
||||
scmd->request->rq_status = RQ_SCSI_DONE;
|
||||
|
||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n",
|
||||
__FUNCTION__, scmd, scmd->result));
|
||||
|
||||
up(scmd->device->host->eh_action);
|
||||
}
|
||||
SCSI_LOG_ERROR_RECOVERY(3,
|
||||
printk("%s scmd: %p result: %x\n",
|
||||
__FUNCTION__, scmd, scmd->result));
|
||||
complete(scmd->device->host->eh_action);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,10 +433,6 @@ static void scsi_eh_done(struct scsi_cmnd *scmd)
|
|||
* @scmd: SCSI Cmd to send.
|
||||
* @timeout: Timeout for cmd.
|
||||
*
|
||||
* Notes:
|
||||
* The initialization of the structures is quite a bit different in
|
||||
* this case, and furthermore, there is a different completion handler
|
||||
* vs scsi_dispatch_cmd.
|
||||
* Return value:
|
||||
* SUCCESS or FAILED or NEEDS_RETRY
|
||||
**/
|
||||
|
@ -472,24 +440,16 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
|
|||
{
|
||||
struct scsi_device *sdev = scmd->device;
|
||||
struct Scsi_Host *shost = sdev->host;
|
||||
DECLARE_MUTEX_LOCKED(sem);
|
||||
DECLARE_COMPLETION(done);
|
||||
unsigned long timeleft;
|
||||
unsigned long flags;
|
||||
int rtn = SUCCESS;
|
||||
int rtn;
|
||||
|
||||
/*
|
||||
* we will use a queued command if possible, otherwise we will
|
||||
* emulate the queuing and calling of completion function ourselves.
|
||||
*/
|
||||
if (sdev->scsi_level <= SCSI_2)
|
||||
scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) |
|
||||
(sdev->lun << 5 & 0xe0);
|
||||
|
||||
scsi_add_timer(scmd, timeout, scsi_eh_times_out);
|
||||
|
||||
/*
|
||||
* set up the semaphore so we wait for the command to complete.
|
||||
*/
|
||||
shost->eh_action = &sem;
|
||||
shost->eh_action = &done;
|
||||
scmd->request->rq_status = RQ_SCSI_BUSY;
|
||||
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
|
@ -497,47 +457,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
|
|||
shost->hostt->queuecommand(scmd, scsi_eh_done);
|
||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||
|
||||
down(&sem);
|
||||
scsi_log_completion(scmd, SUCCESS);
|
||||
timeleft = wait_for_completion_timeout(&done, timeout);
|
||||
|
||||
scmd->request->rq_status = RQ_SCSI_DONE;
|
||||
shost->eh_action = NULL;
|
||||
|
||||
/*
|
||||
* see if timeout. if so, tell the host to forget about it.
|
||||
* in other words, we don't want a callback any more.
|
||||
*/
|
||||
if (scmd->eh_eflags & SCSI_EH_REC_TIMEOUT) {
|
||||
scmd->eh_eflags &= ~SCSI_EH_REC_TIMEOUT;
|
||||
scsi_log_completion(scmd, SUCCESS);
|
||||
|
||||
/*
|
||||
* as far as the low level driver is
|
||||
* concerned, this command is still active, so
|
||||
* we must give the low level driver a chance
|
||||
* to abort it. (db)
|
||||
*
|
||||
* FIXME(eric) - we are not tracking whether we could
|
||||
* abort a timed out command or not. not sure how
|
||||
* we should treat them differently anyways.
|
||||
*/
|
||||
if (shost->hostt->eh_abort_handler)
|
||||
shost->hostt->eh_abort_handler(scmd);
|
||||
|
||||
scmd->request->rq_status = RQ_SCSI_DONE;
|
||||
rtn = FAILED;
|
||||
}
|
||||
|
||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd: %p, rtn:%x\n",
|
||||
__FUNCTION__, scmd, rtn));
|
||||
SCSI_LOG_ERROR_RECOVERY(3,
|
||||
printk("%s: scmd: %p, timeleft: %ld\n",
|
||||
__FUNCTION__, scmd, timeleft));
|
||||
|
||||
/*
|
||||
* now examine the actual status codes to see whether the command
|
||||
* actually did complete normally.
|
||||
* If there is time left scsi_eh_done got called, and we will
|
||||
* examine the actual status codes to see whether the command
|
||||
* actually did complete normally, else tell the host to forget
|
||||
* about this command.
|
||||
*/
|
||||
if (rtn == SUCCESS) {
|
||||
if (timeleft) {
|
||||
rtn = scsi_eh_completed_normally(scmd);
|
||||
SCSI_LOG_ERROR_RECOVERY(3,
|
||||
printk("%s: scsi_eh_completed_normally %x\n",
|
||||
__FUNCTION__, rtn));
|
||||
|
||||
switch (rtn) {
|
||||
case SUCCESS:
|
||||
case NEEDS_RETRY:
|
||||
|
@ -547,6 +489,15 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
|
|||
rtn = FAILED;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* FIXME(eric) - we are not tracking whether we could
|
||||
* abort a timed out command or not. not sure how
|
||||
* we should treat them differently anyways.
|
||||
*/
|
||||
if (shost->hostt->eh_abort_handler)
|
||||
shost->hostt->eh_abort_handler(scmd);
|
||||
rtn = FAILED;
|
||||
}
|
||||
|
||||
return rtn;
|
||||
|
|
|
@ -22,7 +22,6 @@ struct Scsi_Host;
|
|||
* Scsi Error Handler Flags
|
||||
*/
|
||||
#define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */
|
||||
#define SCSI_EH_REC_TIMEOUT 0x0002 /* EH retry timed out */
|
||||
|
||||
#define SCSI_SENSE_VALID(scmd) \
|
||||
(((scmd)->sense_buffer[0] & 0x70) == 0x70)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
|
||||
struct block_device;
|
||||
struct completion;
|
||||
struct module;
|
||||
struct scsi_cmnd;
|
||||
struct scsi_device;
|
||||
|
@ -467,8 +468,8 @@ struct Scsi_Host {
|
|||
|
||||
struct list_head eh_cmd_q;
|
||||
struct task_struct * ehandler; /* Error recovery thread. */
|
||||
struct semaphore * eh_action; /* Wait for specific actions on the
|
||||
host. */
|
||||
struct completion * eh_action; /* Wait for specific actions on the
|
||||
host. */
|
||||
wait_queue_head_t host_wait;
|
||||
struct scsi_host_template *hostt;
|
||||
struct scsi_transport_template *transportt;
|
||||
|
|
Loading…
Reference in a new issue