summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJeff Skirvin <jeffrey.d.skirvin@intel.com>2011-03-04 14:06:52 -0800
committerDan Williams <dan.j.williams@intel.com>2011-07-03 03:55:30 -0700
commit4dc043c41037fc6e369270daaa626465a8766565 (patch)
tree9021ca328112a5a171da0392df43c05a2d63fa52 /drivers
parentcbb65c665b341e560b7a3b37cc616376031b3ee5 (diff)
isci: Termination handling cleanup, added termination timeouts.
Added a request "dead" state for use when a termination wait times-out. isci_terminate_pending_requests now detaches the device's pending list and terminates each entry on the detached list. Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/isci/request.h3
-rw-r--r--drivers/scsi/isci/task.c233
2 files changed, 143 insertions, 93 deletions
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index 166295ee8cfd..b45c0f1f057f 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -71,7 +71,8 @@ enum isci_request_status {
completed = 0x03,
aborting = 0x04,
aborted = 0x05,
- terminating = 0x06
+ terminating = 0x06,
+ dead = 0x07
};
enum task_type {
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 5e80e8449223..c781a4ab4a50 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -663,6 +663,59 @@ static void isci_request_cleanup_completed_loiterer(
}
isci_request_free(isci_host, isci_request);
}
+
+/**
+* @isci_termination_timed_out(): this function will deal with a request for
+* which the wait for termination has timed-out.
+*
+* @isci_host This SCU.
+* @isci_request The I/O request being terminated.
+*/
+static void
+isci_termination_timed_out(
+ struct isci_host * host,
+ struct isci_request * request
+ )
+{
+ unsigned long state_flags;
+
+ dev_warn(&host->pdev->dev,
+ "%s: host = %p; request = %p\n",
+ __func__, host, request);
+
+ /* At this point, the request to terminate
+ * has timed out. The best we can do is to
+ * have the request die a silent death
+ * if it ever completes.
+ */
+ spin_lock_irqsave(&request->state_lock, state_flags);
+
+ if (request->status == started) {
+
+ /* Set the request state to "dead",
+ * and clear the task pointer so that an actual
+ * completion event callback doesn't do
+ * anything.
+ */
+ request->status = dead;
+
+ /* Clear the timeout completion event pointer.*/
+ request->io_request_completion = NULL;
+
+ if (request->ttype == io_task) {
+
+ /* Break links with the sas_task. */
+ if (request->ttype_ptr.io_task_ptr != NULL) {
+
+ request->ttype_ptr.io_task_ptr->lldd_task = NULL;
+ request->ttype_ptr.io_task_ptr = NULL;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&request->state_lock, state_flags);
+}
+
+
/**
* isci_terminate_request_core() - This function will terminate the given
* request, and wait for it to complete. This function must only be called
@@ -684,35 +737,20 @@ static void isci_terminate_request_core(
bool needs_cleanup_handling = false;
enum isci_request_status request_status;
unsigned long flags;
+ unsigned long timeout_remaining;
+
dev_dbg(&isci_host->pdev->dev,
"%s: device = %p; request = %p\n",
__func__, isci_device, isci_request);
- /* Peek at the current status of the request. This will tell
- * us if there was special handling on the request such that it
- * needs to be detached and freed here.
- */
- spin_lock_irqsave(&isci_request->state_lock, flags);
- request_status = isci_request_get_state(isci_request);
-
- if ((isci_request->ttype == io_task) /* TMFs are in their own thread */
- && ((request_status == aborted)
- || (request_status == aborting)
- || (request_status == terminating)
- || (request_status == completed)
- )
- ) {
+ spin_lock_irqsave(&isci_host->scic_lock, flags);
- /* The completion routine won't free a request in
- * the aborted/aborting/terminating state, so we do
- * it here.
- */
- needs_cleanup_handling = true;
- }
- spin_unlock_irqrestore(&isci_request->state_lock, flags);
+ /* Note that we are not going to control
+ * the target to abort the request.
+ */
+ isci_request->complete_in_target = true;
- spin_lock_irqsave(&isci_host->scic_lock, flags);
/* Make sure the request wasn't just sitting around signalling
* device condition (if the request handle is NULL, then the
* request completed but needed additional handling here).
@@ -733,13 +771,16 @@ static void isci_terminate_request_core(
* fail is when the io request is completed and
* being aborted.
*/
- if (status != SCI_SUCCESS)
+ if (status != SCI_SUCCESS) {
dev_err(&isci_host->pdev->dev,
"%s: scic_controller_terminate_request"
" returned = 0x%x\n",
__func__,
status);
- else {
+ /* Clear the completion pointer from the request. */
+ isci_request->io_request_completion = NULL;
+
+ } else {
if (was_terminated) {
dev_dbg(&isci_host->pdev->dev,
"%s: before completion wait (%p)\n",
@@ -747,21 +788,62 @@ static void isci_terminate_request_core(
isci_request->io_request_completion);
/* Wait here for the request to complete. */
- wait_for_completion(isci_request->io_request_completion);
+ #define TERMINATION_TIMEOUT_MSEC 50
+ timeout_remaining
+ = wait_for_completion_timeout(
+ isci_request->io_request_completion,
+ msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC));
+
+ if (!timeout_remaining) {
+
+ isci_termination_timed_out(isci_host,
+ isci_request);
+
+ dev_err(&isci_host->pdev->dev,
+ "%s: *** Timeout waiting for "
+ "termination(%p/%p)\n",
+ __func__,
+ isci_request->io_request_completion,
+ isci_request);
+
+ } else
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: after completion wait (%p)\n",
+ __func__,
+ isci_request->io_request_completion);
+ }
+ /* Clear the completion pointer from the request. */
+ isci_request->io_request_completion = NULL;
- dev_dbg(&isci_host->pdev->dev,
- "%s: after completion wait (%p)\n",
- __func__,
- isci_request->io_request_completion);
+ /* Peek at the status of the request. This will tell
+ * us if there was special handling on the request such that it
+ * needs to be detached and freed here.
+ */
+ spin_lock_irqsave(&isci_request->state_lock, flags);
+ request_status = isci_request_get_state(isci_request);
+
+ if ((isci_request->ttype == io_task) /* TMFs are in their own thread */
+ && ((request_status == aborted)
+ || (request_status == aborting)
+ || (request_status == terminating)
+ || (request_status == completed)
+ || (request_status == dead)
+ )
+ ) {
+
+ /* The completion routine won't free a request in
+ * the aborted/aborting/etc. states, so we do
+ * it here.
+ */
+ needs_cleanup_handling = true;
}
+ spin_unlock_irqrestore(&isci_request->state_lock, flags);
if (needs_cleanup_handling)
isci_request_cleanup_completed_loiterer(
isci_host, isci_device, isci_request
);
}
- /* Clear the completion pointer from the request. */
- isci_request->io_request_completion = NULL;
}
static void isci_terminate_request(
@@ -771,11 +853,7 @@ static void isci_terminate_request(
enum isci_request_status new_request_state)
{
enum isci_request_status old_state;
-
DECLARE_COMPLETION_ONSTACK(request_completion);
- unsigned long flags;
-
- spin_lock_irqsave(&isci_host->scic_lock, flags);
/* Change state to "new_request_state" if it is currently "started" */
old_state = isci_request_change_started_to_newstate(
@@ -823,73 +901,44 @@ void isci_terminate_pending_requests(
struct isci_remote_device *isci_device,
enum isci_request_status new_request_state)
{
- struct isci_request *isci_request;
- struct sas_task *task;
- bool done = false;
- unsigned long flags;
+ struct isci_request *request;
+ struct isci_request *next_request;
+ unsigned long flags;
+ struct list_head aborted_request_list;
+
+ INIT_LIST_HEAD(&aborted_request_list);
dev_dbg(&isci_host->pdev->dev,
"%s: isci_device = %p (new request state = %d)\n",
__func__, isci_device, new_request_state);
- #define ISCI_TERMINATE_SHOW_PENDING_REQUESTS
- #ifdef ISCI_TERMINATE_SHOW_PENDING_REQUESTS
- {
- struct isci_request *request;
-
- /* Only abort the task if it's in the
- * device's request_in_process list
- */
- list_for_each_entry(request,
- &isci_device->reqs_in_process,
- dev_node)
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p; request is on "
- "reqs_in_process list: %p\n",
- __func__, isci_device, request);
- }
- #endif /* ISCI_TERMINATE_SHOW_PENDING_REQUESTS */
-
- /* Clean up all pending requests. */
- do {
- spin_lock_irqsave(&isci_host->scic_lock, flags);
-
- if (list_empty(&isci_device->reqs_in_process)) {
-
- done = true;
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_lock_irqsave(&isci_host->scic_lock, flags);
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p; done.\n",
- __func__, isci_device);
- } else {
- /* The list was not empty - grab the first request. */
- isci_request = list_first_entry(
- &isci_device->reqs_in_process,
- struct isci_request, dev_node
- );
- /* Note that we are not expecting to have to control
- * the target to abort the request.
- */
- isci_request->complete_in_target = true;
+ /* Move all of the pending requests off of the device list. */
+ list_splice_init(&isci_device->reqs_in_process,
+ &aborted_request_list);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- /* Get the libsas task reference. */
- task = isci_request_access_task(isci_request);
+ /* Iterate through the now-local list. */
+ list_for_each_entry_safe(request, next_request,
+ &aborted_request_list, dev_node) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device=%p request=%p; task=%p\n",
- __func__, isci_device, isci_request, task);
+ dev_warn(&isci_host->pdev->dev,
+ "%s: isci_device=%p request=%p; task=%p\n",
+ __func__,
+ isci_device, request,
+ ((request->ttype == io_task)
+ ? isci_request_access_task(request)
+ : NULL));
- /* Mark all still pending I/O with the selected next
- * state.
- */
- isci_terminate_request(isci_host, isci_device,
- isci_request, new_request_state
- );
- }
- } while (!done);
+ /* Mark all still pending I/O with the selected next
+ * state, terminate and free it.
+ */
+ isci_terminate_request(isci_host, isci_device,
+ request, new_request_state
+ );
+ }
}
/**