Skip to content

Commit

Permalink
[SCSI] isci: Handle task request timeouts correctly.
Browse files Browse the repository at this point in the history
In the case where "task" requests timeout (note that this class of
requests can also include SATA/STP soft reset FIS transmissions),
handle the case where the task was being managed by some call to
terminate the task request by completing both the tmf and the aborting
process.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
  • Loading branch information
Jeff Skirvin authored and James Bottomley committed Oct 31, 2011
1 parent d689168 commit b343dff
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 42 deletions.
149 changes: 107 additions & 42 deletions drivers/scsi/isci/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,61 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost,
return ireq;
}

/**
* isci_request_mark_zombie() - This function must be called with scic_lock held.
*/
static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_request *ireq)
{
struct completion *tmf_completion = NULL;
struct completion *req_completion;

/* Set the request state to "dead". */
ireq->status = dead;

req_completion = ireq->io_request_completion;
ireq->io_request_completion = NULL;

if (ireq->ttype == io_task) {

/* Break links with the sas_task - the callback is done
* elsewhere.
*/
struct sas_task *task = isci_request_access_task(ireq);

if (task)
task->lldd_task = NULL;

ireq->ttype_ptr.io_task_ptr = NULL;
} else {
/* Break links with the TMF request. */
struct isci_tmf *tmf = isci_request_access_tmf(ireq);

/* In the case where a task request is dying,
* the thread waiting on the complete will sit and
* timeout unless we wake it now. Since the TMF
* has a default error status, complete it here
* to wake the waiting thread.
*/
if (tmf) {
tmf_completion = tmf->complete;
tmf->complete = NULL;
}
ireq->ttype_ptr.tmf_task_ptr = NULL;
dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n",
__func__, tmf->tmf_code, tmf->io_tag);
}

dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n",
ireq->io_tag);

/* Don't force waiting threads to timeout. */
if (req_completion)
complete(req_completion);

if (tmf_completion != NULL)
complete(tmf_completion);
}

static int isci_task_execute_tmf(struct isci_host *ihost,
struct isci_remote_device *idev,
struct isci_tmf *tmf, unsigned long timeout_ms)
Expand Down Expand Up @@ -375,6 +430,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,

/* Assign the pointer to the TMF's completion kernel wait structure. */
tmf->complete = &completion;
tmf->status = SCI_FAILURE_TIMEOUT;

ireq = isci_task_request_build(ihost, idev, tag, tmf);
if (!ireq)
Expand Down Expand Up @@ -410,18 +466,35 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
msecs_to_jiffies(timeout_ms));

if (timeleft == 0) {
/* The TMF did not complete - this could be because
* of an unplug. Terminate the TMF request now.
*/
spin_lock_irqsave(&ihost->scic_lock, flags);

if (tmf->cb_state_func != NULL)
tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data);
tmf->cb_state_func(isci_tmf_timed_out, tmf,
tmf->cb_data);

sci_controller_terminate_request(ihost,
idev,
ireq);
sci_controller_terminate_request(ihost, idev, ireq);

spin_unlock_irqrestore(&ihost->scic_lock, flags);

wait_for_completion(tmf->complete);
timeleft = wait_for_completion_timeout(
&completion,
msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC));

if (!timeleft) {
/* Strange condition - the termination of the TMF
* request timed-out.
*/
spin_lock_irqsave(&ihost->scic_lock, flags);

/* If the TMF status has not changed, kill it. */
if (tmf->status == SCI_FAILURE_TIMEOUT)
isci_request_mark_zombie(ihost, ireq);

spin_unlock_irqrestore(&ihost->scic_lock, flags);
}
}

isci_print_tmf(tmf);
Expand Down Expand Up @@ -645,42 +718,27 @@ static void isci_terminate_request_core(struct isci_host *ihost,
__func__, isci_request, io_request_completion);

/* Wait here for the request to complete. */
#define TERMINATION_TIMEOUT_MSEC 500
termination_completed
= wait_for_completion_timeout(
io_request_completion,
msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC));
msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC));

if (!termination_completed) {

/* The request to terminate has timed out. */
spin_lock_irqsave(&ihost->scic_lock,
flags);
spin_lock_irqsave(&ihost->scic_lock, flags);

/* Check for state changes. */
if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
if (!test_bit(IREQ_TERMINATED,
&isci_request->flags)) {

/* The best we can do is to have the
* request die a silent death if it
* ever really completes.
*
* Set the request state to "dead",
* and clear the task pointer so that
* an actual completion event callback
* doesn't do anything.
*/
isci_request->status = dead;
isci_request->io_request_completion
= NULL;

if (isci_request->ttype == io_task) {

/* Break links with the
* sas_task.
*/
isci_request->ttype_ptr.io_task_ptr
= NULL;
}
isci_request_mark_zombie(ihost,
isci_request);
needs_cleanup_handling = true;
} else
termination_completed = 1;

Expand Down Expand Up @@ -1302,30 +1360,32 @@ isci_task_request_complete(struct isci_host *ihost,
enum sci_task_status completion_status)
{
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
struct completion *tmf_complete;
struct completion *tmf_complete = NULL;
struct completion *request_complete = ireq->io_request_completion;

dev_dbg(&ihost->pdev->dev,
"%s: request = %p, status=%d\n",
__func__, ireq, completion_status);

isci_request_change_state(ireq, completed);

tmf->status = completion_status;
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);

if (tmf->proto == SAS_PROTOCOL_SSP) {
memcpy(&tmf->resp.resp_iu,
&ireq->ssp.rsp,
SSP_RESP_IU_MAX_SIZE);
} else if (tmf->proto == SAS_PROTOCOL_SATA) {
memcpy(&tmf->resp.d2h_fis,
&ireq->stp.rsp,
sizeof(struct dev_to_host_fis));
if (tmf) {
tmf->status = completion_status;

if (tmf->proto == SAS_PROTOCOL_SSP) {
memcpy(&tmf->resp.resp_iu,
&ireq->ssp.rsp,
SSP_RESP_IU_MAX_SIZE);
} else if (tmf->proto == SAS_PROTOCOL_SATA) {
memcpy(&tmf->resp.d2h_fis,
&ireq->stp.rsp,
sizeof(struct dev_to_host_fis));
}
/* PRINT_TMF( ((struct isci_tmf *)request->task)); */
tmf_complete = tmf->complete;
}

/* PRINT_TMF( ((struct isci_tmf *)request->task)); */
tmf_complete = tmf->complete;

sci_controller_complete_io(ihost, ireq->target_device, ireq);
/* set the 'terminated' flag handle to make sure it cannot be terminated
* or completed again.
Expand All @@ -1343,8 +1403,13 @@ isci_task_request_complete(struct isci_host *ihost,
list_del_init(&ireq->dev_node);
}

/* "request_complete" is set if the task was being terminated. */
if (request_complete)
complete(request_complete);

/* The task management part completes last. */
complete(tmf_complete);
if (tmf_complete)
complete(tmf_complete);
}

static void isci_smp_task_timedout(unsigned long _task)
Expand Down
2 changes: 2 additions & 0 deletions drivers/scsi/isci/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
#include <scsi/sas_ata.h>
#include "host.h"

#define ISCI_TERMINATION_TIMEOUT_MSEC 500

struct isci_request;

/**
Expand Down

0 comments on commit b343dff

Please sign in to comment.