summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/isci/request.c52
-rw-r--r--drivers/scsi/isci/task.c230
2 files changed, 129 insertions, 153 deletions
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 8039f1c..c88e270 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -814,9 +814,8 @@ static void isci_task_save_for_upper_layer_completion(
break;
case isci_perform_aborted_io_completion:
- /*
- * No notification because this request is already
- * in the abort path.
+ /* No notification to libsas because this request is
+ * already in the abort path.
*/
dev_warn(&host->pdev->dev,
"%s: Aborted - task = %p, response=%d, status=%d\n",
@@ -824,6 +823,19 @@ static void isci_task_save_for_upper_layer_completion(
task,
response,
status);
+
+ /* Wake up whatever process was waiting for this
+ * request to complete.
+ */
+ WARN_ON(request->io_request_completion == NULL);
+
+ if (request->io_request_completion != NULL) {
+
+ /* Signal whoever is waiting that this
+ * request is complete.
+ */
+ complete(request->io_request_completion);
+ }
break;
case isci_perform_error_io_completion:
@@ -847,7 +859,7 @@ static void isci_task_save_for_upper_layer_completion(
response,
status);
- /* Add to the aborted list. */
+ /* Add to the error to libsas list. */
list_add(&request->completed_node,
&host->requests_to_errorback);
break;
@@ -873,8 +885,6 @@ void isci_request_io_request_complete(
struct ssp_response_iu *resp_iu;
void *resp_buf;
unsigned long task_flags;
- unsigned long state_flags;
- struct completion *io_request_completion;
struct isci_remote_device *isci_device = request->isci_device;
enum service_response response = SAS_TASK_UNDELIVERED;
enum exec_status status = SAS_ABORTED_TASK;
@@ -891,9 +901,8 @@ void isci_request_io_request_complete(
task->data_dir,
completion_status);
- spin_lock_irqsave(&request->state_lock, state_flags);
+ spin_lock(&request->state_lock);
request_status = isci_request_get_state(request);
- spin_unlock_irqrestore(&request->state_lock, state_flags);
/* Decode the request status. Note that if the request has been
* aborted by a task management function, we don't care
@@ -928,6 +937,8 @@ void isci_request_io_request_complete(
complete_to_host = isci_perform_aborted_io_completion;
/* This was an aborted request. */
+
+ spin_unlock(&request->state_lock);
break;
case aborting:
@@ -955,6 +966,8 @@ void isci_request_io_request_complete(
complete_to_host = isci_perform_aborted_io_completion;
/* This was an aborted request. */
+
+ spin_unlock(&request->state_lock);
break;
case terminating:
@@ -977,13 +990,20 @@ void isci_request_io_request_complete(
else
status = SAS_ABORTED_TASK;
- complete_to_host = isci_perform_normal_io_completion;
+ complete_to_host = isci_perform_aborted_io_completion;
/* This was a terminated request. */
+
+ spin_unlock(&request->state_lock);
break;
default:
+ /* The request is done from an SCU HW perspective. */
+ request->status = completed;
+
+ spin_unlock(&request->state_lock);
+
/* This is an active request being completed from the core. */
switch (completion_status) {
@@ -1185,20 +1205,6 @@ void isci_request_io_request_complete(
*/
request->sci_request_handle = NULL;
- /* Save possible completion ptr. */
- io_request_completion = request->io_request_completion;
-
- if (io_request_completion) {
-
- /* This is inherantly a regular I/O request,
- * since we are currently in the regular
- * I/O completion callback function.
- * Signal whoever is waiting that this
- * request is complete.
- */
- complete(io_request_completion);
- }
-
isci_host_can_dequeue(isci_host, 1);
}
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 779f6cf..02c40c0 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -618,9 +618,6 @@ static enum isci_request_status isci_task_validate_request_to_abort(
old_state = isci_request_change_started_to_aborted(
isci_request, aborted_io_completion);
- /* Only abort requests in the started state. */
- if (old_state != started)
- old_state = unallocated;
}
return old_state;
@@ -644,10 +641,23 @@ static void isci_request_cleanup_completed_loiterer(
spin_lock_irqsave(&isci_host->scic_lock, flags);
list_del_init(&isci_request->dev_node);
- if (task != NULL)
- task->lldd_task = NULL;
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ if (task != NULL) {
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ task->lldd_task = NULL;
+
+ isci_set_task_doneflags(task);
+
+ /* If this task is not in the abort path, call task_done. */
+ if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
+
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ task->task_done(task);
+ } else
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ }
isci_request_free(isci_host, isci_request);
}
/**
@@ -684,17 +694,20 @@ static void isci_terminate_request_core(
spin_lock_irqsave(&isci_request->state_lock, flags);
request_status = isci_request_get_state(isci_request);
- /* TMFs are in their own thread */
- if ((isci_request->ttype == io_task) &&
- ((request_status == aborted) ||
- (request_status == aborting) ||
- (request_status == terminating)))
+ if ((isci_request->ttype == io_task) /* TMFs are in their own thread */
+ && ((request_status == aborted)
+ || (request_status == aborting)
+ || (request_status == terminating)
+ || (request_status == completed)
+ )
+ ) {
+
/* 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);
spin_lock_irqsave(&isci_host->scic_lock, flags);
@@ -765,10 +778,10 @@ static void isci_terminate_request(
new_request_state
);
- spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ if ((old_state == started) || (old_state == completed)) {
- if (old_state == started)
- /* This request was not already being aborted. If it had been,
+ /* If the old_state is started:
+ * This request was not already being aborted. If it had been,
* then the aborting I/O (ie. the TMF request) would not be in
* the aborting state, and thus would be terminated here. Note
* that since the TMF completion's call to the kernel function
@@ -777,9 +790,15 @@ static void isci_terminate_request(
* special wait here for already aborting requests - the
* termination of the TMF request will force the request
* to finish it's already started terminate.
+ *
+ * If old_state == completed:
+ * This request completed from the SCU hardware perspective
+ * and now just needs cleaning up in terms of freeing the
+ * request and potentially calling up to libsas.
*/
isci_terminate_request_core(isci_host, isci_device,
isci_request, &request_completion);
+ }
}
/**
@@ -863,10 +882,6 @@ void isci_terminate_pending_requests(
isci_terminate_request(isci_host, isci_device,
isci_request, new_request_state
);
-
- /* Set the 'done' state on the task. */
- if (task)
- isci_task_all_done(task);
}
} while (!done);
}
@@ -1067,13 +1082,15 @@ static void isci_abort_task_process_cb(
int isci_task_abort_task(struct sas_task *task)
{
DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
- struct isci_request *old_request = NULL;
+ struct isci_request *old_request = NULL;
+ enum isci_request_status old_state;
struct isci_remote_device *isci_device = NULL;
- struct isci_host *isci_host = NULL;
- struct isci_tmf tmf;
- int ret = TMF_RESP_FUNC_FAILED;
- unsigned long flags;
- bool any_dev_reset, device_stopping;
+ struct isci_host *isci_host = NULL;
+ struct isci_tmf tmf;
+ int ret = TMF_RESP_FUNC_FAILED;
+ unsigned long flags;
+ bool any_dev_reset = false;
+ bool device_stopping;
/* Get the isci_request reference from the task. Note that
* this check does not depend on the pending request list
@@ -1093,21 +1110,6 @@ int isci_task_abort_task(struct sas_task *task)
device_stopping = (isci_device->status == isci_stopping)
|| (isci_device->status == isci_stopped);
-#ifdef NOMORE
- /* This abort task function is the first stop of the libsas error
- * handler thread. Since libsas is executing in a thread with a
- * referernce to the "task" parameter, that task cannot be completed
- * directly back to the upper layers. In order to make sure that
- * the task is managed correctly if this abort task fails, set the
- * "SAS_TASK_STATE_ABORTED" bit now such that completions up the
- * stack will be intercepted and only allowed to happen in the
- * libsas SCSI error handler thread.
- */
- spin_lock_irqsave(&task->task_state_lock, flags);
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-#endif /* NOMORE */
-
/* This version of the driver will fail abort requests for
* SATA/STP. Failing the abort request this way will cause the
* SCSI error handler thread to escalate to LUN reset
@@ -1123,35 +1125,27 @@ int isci_task_abort_task(struct sas_task *task)
dev_dbg(&isci_host->pdev->dev,
"%s: old_request == %p\n", __func__, old_request);
+ if (!device_stopping)
+ any_dev_reset = isci_device_is_reset_pending(isci_host,isci_device);
+
spin_lock_irqsave(&task->task_state_lock, flags);
/* Don't do resets to stopping devices. */
- if (device_stopping)
- task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
+ if (device_stopping) {
- /* See if there is a pending device reset for this device. */
- any_dev_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET;
-
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET;
+ any_dev_reset = false;
- if ((isci_device != NULL) && !device_stopping)
+ } else /* See if there is a pending device reset for this device. */
any_dev_reset = any_dev_reset
- || isci_device_is_reset_pending(isci_host,
- isci_device
- );
+ || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET);
/* If the extraction of the request reference from the task
* failed, then the request has been completed (or if there is a
* pending reset then this abort request function must be failed
* in order to escalate to the target reset).
*/
- if ((old_request == NULL) ||
- ((old_request != NULL) &&
- (old_request->sci_request_handle == NULL) &&
- (old_request->complete_in_target)) ||
- any_dev_reset) {
-
- spin_lock_irqsave(&task->task_state_lock, flags);
+ if ((old_request == NULL) || any_dev_reset) {
/* If the device reset task flag is set, fail the task
* management request. Otherwise, the original request
@@ -1164,6 +1158,11 @@ int isci_task_abort_task(struct sas_task *task)
*/
task->task_state_flags &= ~SAS_TASK_STATE_DONE;
+ /* Make the reset happen as soon as possible. */
+ task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
+
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
/* Fail the task management request in order to
* escalate to the target reset.
*/
@@ -1176,13 +1175,8 @@ int isci_task_abort_task(struct sas_task *task)
"task %p on dev %p\n",
__func__, task, isci_device);
- } else {
- ret = TMF_RESP_FUNC_COMPLETE;
-
- dev_dbg(&isci_host->pdev->dev,
- "%s: abort task not needed for %p\n",
- __func__, task);
+ } else {
/* The request has already completed and there
* is nothing to do here other than to set the task
* done bit, and indicate that the task abort function
@@ -1190,89 +1184,65 @@ int isci_task_abort_task(struct sas_task *task)
*/
isci_set_task_doneflags(task);
- /* Set the abort bit to make sure that libsas sticks the
- * task in the completed task queue.
- */
-/* task->task_state_flags |= SAS_TASK_STATE_ABORTED; */
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
- /* Check for the situation where the request was
- * left around on the device list but the
- * request already completed.
- */
- if (old_request && !old_request->sci_request_handle) {
+ ret = TMF_RESP_FUNC_COMPLETE;
- isci_request_cleanup_completed_loiterer(
- isci_host, isci_device, old_request
- );
- }
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: abort task not needed for %p\n",
+ __func__, task);
}
- spin_unlock_irqrestore(&task->task_state_lock, flags);
return ret;
}
+ else
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
spin_lock_irqsave(&isci_host->scic_lock, flags);
- /* Sanity check the request status, and set the I/O kernel completion
+ /* Check the request status and change to "aborting" if currently
+ * "starting"; if true then set the I/O kernel completion
* struct that will be triggered when the request completes.
*/
- if (isci_task_validate_request_to_abort(
- old_request,
- isci_host,
- isci_device,
- &aborted_io_completion)
- == unallocated) {
- dev_dbg(&isci_host->pdev->dev,
- "%s: old_request not valid for device = %p\n",
- __func__,
- isci_device);
- old_request = NULL;
- }
-
- if (!old_request) {
-
- /* There is no isci_request attached to the sas_task.
- * It must have been completed and detached.
- */
- dev_dbg(&isci_host->pdev->dev,
- "%s: old_request == NULL\n",
- __func__);
+ old_state = isci_task_validate_request_to_abort(
+ old_request, isci_host, isci_device,
+ &aborted_io_completion);
+ if ((old_state != started) && (old_state != completed)) {
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
- /* Set the state on the task. */
- isci_task_all_done(task);
+ /* The request was already being handled by someone else (because
+ * they got to set the state away from started).
+ */
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: device = %p; old_request %p already being aborted\n",
+ __func__,
+ isci_device, old_request);
return TMF_RESP_FUNC_COMPLETE;
}
- if (task->task_proto == SAS_PROTOCOL_SMP || device_stopping) {
-
- if (device_stopping)
- dev_dbg(&isci_host->pdev->dev,
- "%s: device is stopping, thus no TMF\n",
- __func__);
- else
- dev_dbg(&isci_host->pdev->dev,
- "%s: request is SMP, thus no TMF\n",
- __func__);
-
- old_request->complete_in_target = true;
+ if ((task->task_proto == SAS_PROTOCOL_SMP)
+ || device_stopping
+ || old_request->complete_in_target
+ ) {
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
+ dev_dbg(&isci_host->pdev->dev,
+ "%s: SMP request (%d)"
+ " or device is stopping (%d)"
+ " or complete_in_target (%d), thus no TMF\n",
+ __func__, (task->task_proto == SAS_PROTOCOL_SMP),
+ device_stopping, old_request->complete_in_target);
+
/* Set the state on the task. */
isci_task_all_done(task);
ret = TMF_RESP_FUNC_COMPLETE;
/* Stopping and SMP devices are not sent a TMF, and are not
- * reset, but the outstanding I/O request is terminated here.
- *
- * Clean up the request on our side, and wait for the aborted
- * I/O to complete.
+ * reset, but the outstanding I/O request is terminated below.
*/
- isci_terminate_request_core(isci_host, isci_device, old_request,
- &aborted_io_completion);
} else {
/* Fill in the tmf stucture */
isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_task_abort,
@@ -1288,23 +1258,23 @@ int isci_task_abort_task(struct sas_task *task)
ret = isci_task_execute_tmf(isci_host, &tmf,
ISCI_ABORT_TASK_TIMEOUT_MS);
- if (ret == TMF_RESP_FUNC_COMPLETE) {
- old_request->complete_in_target = true;
-
- /* Clean up the request on our side, and wait for the aborted I/O to
- * complete.
- */
- isci_terminate_request_core(isci_host, isci_device, old_request,
- &aborted_io_completion);
-
- /* Set the state on the task. */
- isci_task_all_done(task);
- } else
+ if (ret != TMF_RESP_FUNC_COMPLETE)
dev_err(&isci_host->pdev->dev,
"%s: isci_task_send_tmf failed\n",
__func__);
}
+ if (ret == TMF_RESP_FUNC_COMPLETE) {
+ old_request->complete_in_target = true;
+
+ /* Clean up the request on our side, and wait for the aborted I/O to
+ * complete.
+ */
+ isci_terminate_request_core(isci_host, isci_device, old_request,
+ &aborted_io_completion);
+ }
+ /* Make sure we do not leave a reference to aborted_io_completion */
+ old_request->io_request_completion = NULL;
return ret;
}