Skip to content

Commit

Permalink
[SCSI] isci: Fix task management for SMP, SATA and on dev remove.
Browse files Browse the repository at this point in the history
libsas uses the LLDD abort task interface to handle I/O timeouts
in the SATA/STP and SMP discovery paths, so this change will terminate
STP/SMP requests. Also, if the device is gone, the lldd will prevent
libsas from further escalations in the error handler.

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 db49c2d commit 98145cb
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 184 deletions.
47 changes: 0 additions & 47 deletions drivers/scsi/isci/remote_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -1438,53 +1438,6 @@ int isci_remote_device_found(struct domain_device *domain_dev)

return status == SCI_SUCCESS ? 0 : -ENODEV;
}
/**
* isci_device_is_reset_pending() - This function will check if there is any
* pending reset condition on the device.
* @request: This parameter is the isci_device object.
*
* true if there is a reset pending for the device.
*/
bool isci_device_is_reset_pending(
struct isci_host *isci_host,
struct isci_remote_device *isci_device)
{
struct isci_request *isci_request;
struct isci_request *tmp_req;
bool reset_is_pending = false;
unsigned long flags;

dev_dbg(&isci_host->pdev->dev,
"%s: isci_device = %p\n", __func__, isci_device);

spin_lock_irqsave(&isci_host->scic_lock, flags);

/* Check for reset on all pending requests. */
list_for_each_entry_safe(isci_request, tmp_req,
&isci_device->reqs_in_process, dev_node) {
dev_dbg(&isci_host->pdev->dev,
"%s: isci_device = %p request = %p\n",
__func__, isci_device, isci_request);

if (isci_request->ttype == io_task) {
struct sas_task *task = isci_request_access_task(
isci_request);

spin_lock(&task->task_state_lock);
if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET)
reset_is_pending = true;
spin_unlock(&task->task_state_lock);
}
}

spin_unlock_irqrestore(&isci_host->scic_lock, flags);

dev_dbg(&isci_host->pdev->dev,
"%s: isci_device = %p reset_is_pending = %d\n",
__func__, isci_device, reset_is_pending);

return reset_is_pending;
}

/**
* isci_device_clear_reset_pending() - This function will clear if any pending
Expand Down
2 changes: 0 additions & 2 deletions drivers/scsi/isci/remote_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost,
struct isci_remote_device *idev);
void isci_remote_device_gone(struct domain_device *domain_dev);
int isci_remote_device_found(struct domain_device *domain_dev);
bool isci_device_is_reset_pending(struct isci_host *ihost,
struct isci_remote_device *idev);
void isci_device_clear_reset_pending(struct isci_host *ihost,
struct isci_remote_device *idev);
/**
Expand Down
169 changes: 64 additions & 105 deletions drivers/scsi/isci/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -920,22 +920,14 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
__func__, domain_device, isci_host, isci_device);

if (isci_device)
set_bit(IDEV_EH, &isci_device->flags);
if (!isci_device) {
/* If the device is gone, stop the escalations. */
dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__);

/* If there is a device reset pending on any request in the
* device's list, fail this LUN reset request in order to
* escalate to the device reset.
*/
if (!isci_device ||
isci_device_is_reset_pending(isci_host, isci_device)) {
dev_dbg(&isci_host->pdev->dev,
"%s: No dev (%p), or "
"RESET PENDING: domain_device=%p\n",
__func__, isci_device, domain_device);
ret = TMF_RESP_FUNC_FAILED;
ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
set_bit(IDEV_EH, &isci_device->flags);

/* Send the task management part of the reset. */
if (sas_protocol_ata(domain_device->tproto)) {
Expand Down Expand Up @@ -1044,7 +1036,7 @@ int isci_task_abort_task(struct sas_task *task)
struct isci_tmf tmf;
int ret = TMF_RESP_FUNC_FAILED;
unsigned long flags;
bool any_dev_reset = false;
int perform_termination = 0;

/* Get the isci_request reference from the task. Note that
* this check does not depend on the pending request list
Expand All @@ -1066,89 +1058,34 @@ int isci_task_abort_task(struct sas_task *task)
spin_unlock_irqrestore(&isci_host->scic_lock, flags);

dev_dbg(&isci_host->pdev->dev,
"%s: task = %p\n", __func__, task);

if (!isci_device || !old_request)
goto out;
"%s: dev = %p, task = %p, old_request == %p\n",
__func__, isci_device, task, old_request);

set_bit(IDEV_EH, &isci_device->flags);

/* 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
*/
if (sas_protocol_ata(task->task_proto)) {
dev_dbg(&isci_host->pdev->dev,
" task %p is for a STP/SATA device;"
" returning TMF_RESP_FUNC_FAILED\n"
" to cause a LUN reset...\n", task);
goto out;
}

dev_dbg(&isci_host->pdev->dev,
"%s: old_request == %p\n", __func__, old_request);

any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device);

spin_lock_irqsave(&task->task_state_lock, flags);

any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET);
if (isci_device)
set_bit(IDEV_EH, &isci_device->flags);

/* 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).
/* Device reset conditions signalled in task_state_flags are the
* responsbility of libsas to observe at the start of the error
* handler thread.
*/
if ((old_request == NULL) || any_dev_reset) {

/* If the device reset task flag is set, fail the task
* management request. Otherwise, the original request
* has completed.
*/
if (any_dev_reset) {

/* Turn off the task's DONE to make sure this
* task is escalated to a target reset.
*/
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.
*/
ret = TMF_RESP_FUNC_FAILED;

dev_dbg(&isci_host->pdev->dev,
"%s: Failing task abort in order to "
"escalate to target reset because\n"
"SAS_TASK_NEED_DEV_RESET is set for "
"task %p on dev %p\n",
__func__, task, isci_device);


} 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
* was sucessful.
*/
isci_set_task_doneflags(task);

spin_unlock_irqrestore(&task->task_state_lock, flags);
if (!isci_device || !old_request) {
/* 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
* was sucessful.
*/
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags |= SAS_TASK_STATE_DONE;
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
SAS_TASK_STATE_PENDING);
spin_unlock_irqrestore(&task->task_state_lock, flags);

ret = TMF_RESP_FUNC_COMPLETE;
ret = TMF_RESP_FUNC_COMPLETE;

dev_dbg(&isci_host->pdev->dev,
"%s: abort task not needed for %p\n",
__func__, task);
}
dev_dbg(&isci_host->pdev->dev,
"%s: abort task not needed for %p\n",
__func__, task);
goto out;
} else {
spin_unlock_irqrestore(&task->task_state_lock, flags);
}

spin_lock_irqsave(&isci_host->scic_lock, flags);
Expand Down Expand Up @@ -1177,24 +1114,44 @@ int isci_task_abort_task(struct sas_task *task)
goto out;
}
if (task->task_proto == SAS_PROTOCOL_SMP ||
sas_protocol_ata(task->task_proto) ||
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {

spin_unlock_irqrestore(&isci_host->scic_lock, flags);

dev_dbg(&isci_host->pdev->dev,
"%s: SMP request (%d)"
"%s: %s request"
" or complete_in_target (%d), thus no TMF\n",
__func__, (task->task_proto == SAS_PROTOCOL_SMP),
__func__,
((task->task_proto == SAS_PROTOCOL_SMP)
? "SMP"
: (sas_protocol_ata(task->task_proto)
? "SATA/STP"
: "<other>")
),
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));

/* Set the state on the task. */
isci_task_all_done(task);

ret = TMF_RESP_FUNC_COMPLETE;
if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags |= SAS_TASK_STATE_DONE;
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
SAS_TASK_STATE_PENDING);
spin_unlock_irqrestore(&task->task_state_lock, flags);
ret = TMF_RESP_FUNC_COMPLETE;
} else {
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
SAS_TASK_STATE_PENDING);
spin_unlock_irqrestore(&task->task_state_lock, flags);
}

/* Stopping and SMP devices are not sent a TMF, and are not
* reset, but the outstanding I/O request is terminated below.
/* STP and SMP devices are not sent a TMF, but the
* outstanding I/O request is terminated below. This is
* because SATA/STP and SMP discovery path timeouts directly
* call the abort task interface for cleanup.
*/
perform_termination = 1;

} else {
/* Fill in the tmf stucture */
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
Expand All @@ -1203,22 +1160,24 @@ int isci_task_abort_task(struct sas_task *task)

spin_unlock_irqrestore(&isci_host->scic_lock, flags);

#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */
#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf,
ISCI_ABORT_TASK_TIMEOUT_MS);

if (ret != TMF_RESP_FUNC_COMPLETE)
if (ret == TMF_RESP_FUNC_COMPLETE)
perform_termination = 1;
else
dev_dbg(&isci_host->pdev->dev,
"%s: isci_task_send_tmf failed\n",
__func__);
"%s: isci_task_send_tmf failed\n", __func__);
}
if (ret == TMF_RESP_FUNC_COMPLETE) {
if (perform_termination) {
set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags);

/* 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);
isci_terminate_request_core(isci_host, isci_device,
old_request);
}

/* Make sure we do not leave a reference to aborted_io_completion */
Expand Down
33 changes: 3 additions & 30 deletions drivers/scsi/isci/task.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,35 +226,6 @@ enum isci_completion_selection {
isci_perform_error_io_completion /* Use sas_task_abort */
};

static inline void isci_set_task_doneflags(
struct sas_task *task)
{
/* Since no futher action will be taken on this task,
* make sure to mark it complete from the lldd perspective.
*/
task->task_state_flags |= SAS_TASK_STATE_DONE;
task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
}
/**
* isci_task_all_done() - This function clears the task bits to indicate the
* LLDD is done with the task.
*
*
*/
static inline void isci_task_all_done(
struct sas_task *task)
{
unsigned long flags;

/* Since no futher action will be taken on this task,
* make sure to mark it complete from the lldd perspective.
*/
spin_lock_irqsave(&task->task_state_lock, flags);
isci_set_task_doneflags(task);
spin_unlock_irqrestore(&task->task_state_lock, flags);
}

/**
* isci_task_set_completion_status() - This function sets the completion status
* for the request.
Expand Down Expand Up @@ -336,7 +307,9 @@ isci_task_set_completion_status(
/* Fall through to the normal case... */
case isci_perform_normal_io_completion:
/* Normal notification (task_done) */
isci_set_task_doneflags(task);
task->task_state_flags |= SAS_TASK_STATE_DONE;
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
SAS_TASK_STATE_PENDING);
break;
default:
WARN_ONCE(1, "unknown task_notification_selection: %d\n",
Expand Down

0 comments on commit 98145cb

Please sign in to comment.