Commit 7dfdc9a5 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by James Bottomley

[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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 262eef66
...@@ -416,44 +416,16 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) ...@@ -416,44 +416,16 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
return FAILED; 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. * scsi_eh_done - Completion function for error handling.
* @scmd: Cmd that is done. * @scmd: Cmd that is done.
**/ **/
static void scsi_eh_done(struct scsi_cmnd *scmd) static void scsi_eh_done(struct scsi_cmnd *scmd)
{ {
/* SCSI_LOG_ERROR_RECOVERY(3,
* if the timeout handler is already running, then just set the printk("%s scmd: %p result: %x\n",
* 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)); __FUNCTION__, scmd, scmd->result));
complete(scmd->device->host->eh_action);
up(scmd->device->host->eh_action);
}
} }
/** /**
...@@ -461,10 +433,6 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) ...@@ -461,10 +433,6 @@ static void scsi_eh_done(struct scsi_cmnd *scmd)
* @scmd: SCSI Cmd to send. * @scmd: SCSI Cmd to send.
* @timeout: Timeout for cmd. * @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: * Return value:
* SUCCESS or FAILED or NEEDS_RETRY * SUCCESS or FAILED or NEEDS_RETRY
**/ **/
...@@ -472,24 +440,16 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) ...@@ -472,24 +440,16 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
{ {
struct scsi_device *sdev = scmd->device; struct scsi_device *sdev = scmd->device;
struct Scsi_Host *shost = sdev->host; struct Scsi_Host *shost = sdev->host;
DECLARE_MUTEX_LOCKED(sem); DECLARE_COMPLETION(done);
unsigned long timeleft;
unsigned long flags; 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) if (sdev->scsi_level <= SCSI_2)
scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) |
(sdev->lun << 5 & 0xe0); (sdev->lun << 5 & 0xe0);
scsi_add_timer(scmd, timeout, scsi_eh_times_out); shost->eh_action = &done;
/*
* set up the semaphore so we wait for the command to complete.
*/
shost->eh_action = &sem;
scmd->request->rq_status = RQ_SCSI_BUSY; scmd->request->rq_status = RQ_SCSI_BUSY;
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
...@@ -497,47 +457,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) ...@@ -497,47 +457,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
shost->hostt->queuecommand(scmd, scsi_eh_done); shost->hostt->queuecommand(scmd, scsi_eh_done);
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
down(&sem); timeleft = wait_for_completion_timeout(&done, timeout);
scsi_log_completion(scmd, SUCCESS);
scmd->request->rq_status = RQ_SCSI_DONE;
shost->eh_action = NULL; shost->eh_action = NULL;
/* scsi_log_completion(scmd, SUCCESS);
* 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;
/*
* 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", SCSI_LOG_ERROR_RECOVERY(3,
__FUNCTION__, scmd, rtn)); printk("%s: scmd: %p, timeleft: %ld\n",
__FUNCTION__, scmd, timeleft));
/* /*
* now examine the actual status codes to see whether the command * If there is time left scsi_eh_done got called, and we will
* actually did complete normally. * 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); rtn = scsi_eh_completed_normally(scmd);
SCSI_LOG_ERROR_RECOVERY(3, SCSI_LOG_ERROR_RECOVERY(3,
printk("%s: scsi_eh_completed_normally %x\n", printk("%s: scsi_eh_completed_normally %x\n",
__FUNCTION__, rtn)); __FUNCTION__, rtn));
switch (rtn) { switch (rtn) {
case SUCCESS: case SUCCESS:
case NEEDS_RETRY: case NEEDS_RETRY:
...@@ -547,6 +489,15 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) ...@@ -547,6 +489,15 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
rtn = FAILED; rtn = FAILED;
break; 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; return rtn;
......
...@@ -22,7 +22,6 @@ struct Scsi_Host; ...@@ -22,7 +22,6 @@ struct Scsi_Host;
* Scsi Error Handler Flags * Scsi Error Handler Flags
*/ */
#define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */ #define SCSI_EH_CANCEL_CMD 0x0001 /* Cancel this cmd */
#define SCSI_EH_REC_TIMEOUT 0x0002 /* EH retry timed out */
#define SCSI_SENSE_VALID(scmd) \ #define SCSI_SENSE_VALID(scmd) \
(((scmd)->sense_buffer[0] & 0x70) == 0x70) (((scmd)->sense_buffer[0] & 0x70) == 0x70)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
struct block_device; struct block_device;
struct completion;
struct module; struct module;
struct scsi_cmnd; struct scsi_cmnd;
struct scsi_device; struct scsi_device;
...@@ -467,7 +468,7 @@ struct Scsi_Host { ...@@ -467,7 +468,7 @@ struct Scsi_Host {
struct list_head eh_cmd_q; struct list_head eh_cmd_q;
struct task_struct * ehandler; /* Error recovery thread. */ struct task_struct * ehandler; /* Error recovery thread. */
struct semaphore * eh_action; /* Wait for specific actions on the struct completion * eh_action; /* Wait for specific actions on the
host. */ host. */
wait_queue_head_t host_wait; wait_queue_head_t host_wait;
struct scsi_host_template *hostt; struct scsi_host_template *hostt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment