isci: Redesign device suspension, abort, cleanup.
This commit changes the means by which outstanding I/Os are handled for cleanup. The likelihood is that this commit will be broken into smaller pieces, however that will be a later revision. Among the changes: - All completion structures have been removed from the tmf and abort paths. - Now using one completed I/O list, with the I/O completed in host bit being used to select error or normal callback paths. Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
d80ecd5726
commit
14aaa9f0a3
11 changed files with 432 additions and 1488 deletions
|
@ -1089,33 +1089,25 @@ void isci_host_completion_routine(unsigned long data)
|
||||||
{
|
{
|
||||||
struct isci_host *ihost = (struct isci_host *)data;
|
struct isci_host *ihost = (struct isci_host *)data;
|
||||||
struct list_head completed_request_list;
|
struct list_head completed_request_list;
|
||||||
struct list_head errored_request_list;
|
|
||||||
struct list_head *current_position;
|
struct list_head *current_position;
|
||||||
struct list_head *next_position;
|
struct list_head *next_position;
|
||||||
struct isci_request *request;
|
struct isci_request *request;
|
||||||
struct isci_request *next_request;
|
|
||||||
struct sas_task *task;
|
struct sas_task *task;
|
||||||
u16 active;
|
u16 active;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&completed_request_list);
|
INIT_LIST_HEAD(&completed_request_list);
|
||||||
INIT_LIST_HEAD(&errored_request_list);
|
|
||||||
|
|
||||||
spin_lock_irq(&ihost->scic_lock);
|
spin_lock_irq(&ihost->scic_lock);
|
||||||
|
|
||||||
sci_controller_completion_handler(ihost);
|
sci_controller_completion_handler(ihost);
|
||||||
|
|
||||||
/* Take the lists of completed I/Os from the host. */
|
/* Take the lists of completed I/Os from the host. */
|
||||||
|
|
||||||
list_splice_init(&ihost->requests_to_complete,
|
list_splice_init(&ihost->requests_to_complete,
|
||||||
&completed_request_list);
|
&completed_request_list);
|
||||||
|
|
||||||
/* Take the list of errored I/Os from the host. */
|
|
||||||
list_splice_init(&ihost->requests_to_errorback,
|
|
||||||
&errored_request_list);
|
|
||||||
|
|
||||||
spin_unlock_irq(&ihost->scic_lock);
|
spin_unlock_irq(&ihost->scic_lock);
|
||||||
|
|
||||||
/* Process any completions in the lists. */
|
/* Process any completions in the list. */
|
||||||
list_for_each_safe(current_position, next_position,
|
list_for_each_safe(current_position, next_position,
|
||||||
&completed_request_list) {
|
&completed_request_list) {
|
||||||
|
|
||||||
|
@ -1123,68 +1115,37 @@ void isci_host_completion_routine(unsigned long data)
|
||||||
completed_node);
|
completed_node);
|
||||||
task = isci_request_access_task(request);
|
task = isci_request_access_task(request);
|
||||||
|
|
||||||
/* Normal notification (task_done) */
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: Normal - request/task = %p/%p\n",
|
|
||||||
__func__,
|
|
||||||
request,
|
|
||||||
task);
|
|
||||||
|
|
||||||
/* Return the task to libsas */
|
/* Return the task to libsas */
|
||||||
if (task != NULL) {
|
if (task != NULL) {
|
||||||
|
|
||||||
task->lldd_task = NULL;
|
task->lldd_task = NULL;
|
||||||
if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags) &&
|
||||||
|
!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
||||||
|
if (test_bit(IREQ_COMPLETE_IN_TARGET,
|
||||||
|
&request->flags)) {
|
||||||
|
|
||||||
|
/* Normal notification (task_done) */
|
||||||
|
dev_dbg(&ihost->pdev->dev, "%s: Normal"
|
||||||
|
" - request/task = %p/%p\n",
|
||||||
|
__func__, request, task);
|
||||||
|
|
||||||
/* If the task is already in the abort path,
|
|
||||||
* the task_done callback cannot be called.
|
|
||||||
*/
|
|
||||||
task->task_done(task);
|
task->task_done(task);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irq(&ihost->scic_lock);
|
|
||||||
isci_free_tag(ihost, request->io_tag);
|
|
||||||
spin_unlock_irq(&ihost->scic_lock);
|
|
||||||
}
|
|
||||||
list_for_each_entry_safe(request, next_request, &errored_request_list,
|
|
||||||
completed_node) {
|
|
||||||
|
|
||||||
task = isci_request_access_task(request);
|
|
||||||
|
|
||||||
/* Use sas_task_abort */
|
|
||||||
dev_warn(&ihost->pdev->dev,
|
|
||||||
"%s: Error - request/task = %p/%p\n",
|
|
||||||
__func__,
|
|
||||||
request,
|
|
||||||
task);
|
|
||||||
|
|
||||||
if (task != NULL) {
|
|
||||||
|
|
||||||
/* Put the task into the abort path if it's not there
|
|
||||||
* already.
|
|
||||||
*/
|
|
||||||
if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED))
|
|
||||||
sas_task_abort(task);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* This is a case where the request has completed with a
|
dev_warn(&ihost->pdev->dev,
|
||||||
* status such that it needed further target servicing,
|
"%s: Error - request/task"
|
||||||
* but the sas_task reference has already been removed
|
" = %p/%p\n",
|
||||||
* from the request. Since it was errored, it was not
|
__func__, request, task);
|
||||||
* being aborted, so there is nothing to do except free
|
|
||||||
* it.
|
sas_task_abort(task);
|
||||||
*/
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irq(&ihost->scic_lock);
|
spin_lock_irq(&ihost->scic_lock);
|
||||||
/* Remove the request from the remote device's list
|
|
||||||
* of pending requests.
|
|
||||||
*/
|
|
||||||
list_del_init(&request->dev_node);
|
|
||||||
isci_free_tag(ihost, request->io_tag);
|
isci_free_tag(ihost, request->io_tag);
|
||||||
spin_unlock_irq(&ihost->scic_lock);
|
spin_unlock_irq(&ihost->scic_lock);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* the coalesence timeout doubles at each encoding step, so
|
/* the coalesence timeout doubles at each encoding step, so
|
||||||
* update it based on the ilog2 value of the outstanding requests
|
* update it based on the ilog2 value of the outstanding requests
|
||||||
|
@ -2345,7 +2306,6 @@ static int sci_controller_dma_alloc(struct isci_host *ihost)
|
||||||
|
|
||||||
ireq->tc = &ihost->task_context_table[i];
|
ireq->tc = &ihost->task_context_table[i];
|
||||||
ireq->owning_controller = ihost;
|
ireq->owning_controller = ihost;
|
||||||
spin_lock_init(&ireq->state_lock);
|
|
||||||
ireq->request_daddr = dma;
|
ireq->request_daddr = dma;
|
||||||
ireq->isci_host = ihost;
|
ireq->isci_host = ihost;
|
||||||
ihost->reqs[i] = ireq;
|
ihost->reqs[i] = ireq;
|
||||||
|
@ -2697,6 +2657,10 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost,
|
||||||
return SCI_FAILURE_INVALID_STATE;
|
return SCI_FAILURE_INVALID_STATE;
|
||||||
}
|
}
|
||||||
status = sci_io_request_terminate(ireq);
|
status = sci_io_request_terminate(ireq);
|
||||||
|
|
||||||
|
dev_dbg(&ihost->pdev->dev, "%s: status=%d; ireq=%p; flags=%lx\n",
|
||||||
|
__func__, status, ireq, ireq->flags);
|
||||||
|
|
||||||
if ((status == SCI_SUCCESS) &&
|
if ((status == SCI_SUCCESS) &&
|
||||||
!test_bit(IREQ_PENDING_ABORT, &ireq->flags) &&
|
!test_bit(IREQ_PENDING_ABORT, &ireq->flags) &&
|
||||||
!test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) {
|
!test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) {
|
||||||
|
@ -2739,6 +2703,8 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost,
|
||||||
|
|
||||||
index = ISCI_TAG_TCI(ireq->io_tag);
|
index = ISCI_TAG_TCI(ireq->io_tag);
|
||||||
clear_bit(IREQ_ACTIVE, &ireq->flags);
|
clear_bit(IREQ_ACTIVE, &ireq->flags);
|
||||||
|
if (test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
|
||||||
|
wake_up_all(&ihost->eventq);
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
default:
|
default:
|
||||||
dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
|
dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n",
|
||||||
|
|
|
@ -205,7 +205,6 @@ struct isci_host {
|
||||||
wait_queue_head_t eventq;
|
wait_queue_head_t eventq;
|
||||||
struct tasklet_struct completion_tasklet;
|
struct tasklet_struct completion_tasklet;
|
||||||
struct list_head requests_to_complete;
|
struct list_head requests_to_complete;
|
||||||
struct list_head requests_to_errorback;
|
|
||||||
spinlock_t scic_lock;
|
spinlock_t scic_lock;
|
||||||
struct isci_request *reqs[SCI_MAX_IO_REQUESTS];
|
struct isci_request *reqs[SCI_MAX_IO_REQUESTS];
|
||||||
struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES];
|
struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES];
|
||||||
|
|
|
@ -556,7 +556,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_LIST_HEAD(&ihost->requests_to_complete);
|
INIT_LIST_HEAD(&ihost->requests_to_complete);
|
||||||
INIT_LIST_HEAD(&ihost->requests_to_errorback);
|
|
||||||
for (i = 0; i < SCI_MAX_PORTS; i++) {
|
for (i = 0; i < SCI_MAX_PORTS; i++) {
|
||||||
struct isci_port *iport = &ihost->ports[i];
|
struct isci_port *iport = &ihost->ports[i];
|
||||||
|
|
||||||
|
|
|
@ -80,49 +80,6 @@ static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_remote_device_not_ready() - This function is called by the ihost when
|
|
||||||
* the remote device is not ready. We mark the isci device as ready (not
|
|
||||||
* "ready_for_io") and signal the waiting proccess.
|
|
||||||
* @isci_host: This parameter specifies the isci host object.
|
|
||||||
* @isci_device: This parameter specifies the remote device
|
|
||||||
*
|
|
||||||
* sci_lock is held on entrance to this function.
|
|
||||||
*/
|
|
||||||
static void isci_remote_device_not_ready(struct isci_host *ihost,
|
|
||||||
struct isci_remote_device *idev, u32 reason)
|
|
||||||
{
|
|
||||||
struct isci_request *ireq;
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: isci_device = %p\n", __func__, idev);
|
|
||||||
|
|
||||||
switch (reason) {
|
|
||||||
case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
|
|
||||||
set_bit(IDEV_GONE, &idev->flags);
|
|
||||||
break;
|
|
||||||
case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
|
|
||||||
set_bit(IDEV_IO_NCQERROR, &idev->flags);
|
|
||||||
|
|
||||||
/* Suspend the remote device so the I/O can be terminated. */
|
|
||||||
sci_remote_device_suspend(idev);
|
|
||||||
|
|
||||||
/* Kill all outstanding requests for the device. */
|
|
||||||
list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) {
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: isci_device = %p request = %p\n",
|
|
||||||
__func__, idev, ireq);
|
|
||||||
|
|
||||||
sci_controller_terminate_request(ihost, idev, ireq);
|
|
||||||
}
|
|
||||||
/* Fall through into the default case... */
|
|
||||||
default:
|
|
||||||
clear_bit(IDEV_IO_READY, &idev->flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isci_remote_device_ready() - This function is called by the ihost when the
|
* isci_remote_device_ready() - This function is called by the ihost when the
|
||||||
* remote device is ready. We mark the isci device as ready and signal the
|
* remote device is ready. We mark the isci device as ready and signal the
|
||||||
|
@ -142,49 +99,121 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
|
||||||
wake_up(&ihost->eventq);
|
wake_up(&ihost->eventq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isci_remote_device_suspendcheck(struct isci_remote_device *idev)
|
static enum sci_status sci_remote_device_terminate_req(
|
||||||
|
struct isci_host *ihost,
|
||||||
|
struct isci_remote_device *idev,
|
||||||
|
int check_abort,
|
||||||
|
struct isci_request *ireq)
|
||||||
{
|
{
|
||||||
return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
|
dev_dbg(&ihost->pdev->dev,
|
||||||
|| !test_bit(IDEV_ALLOCATED, &idev->flags);
|
"%s: idev=%p; flags=%lx; req=%p; req target=%p\n",
|
||||||
|
__func__, idev, idev->flags, ireq, ireq->target_device);
|
||||||
|
|
||||||
|
if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
|
||||||
|
(ireq->target_device != idev) ||
|
||||||
|
(check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags)))
|
||||||
|
return SCI_SUCCESS;
|
||||||
|
|
||||||
|
set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags);
|
||||||
|
|
||||||
|
return sci_controller_terminate_request(ihost, idev, ireq);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sci_status isci_remote_device_suspend(
|
static enum sci_status sci_remote_device_terminate_reqs_checkabort(
|
||||||
struct isci_host *ihost,
|
struct isci_remote_device *idev,
|
||||||
struct isci_remote_device *idev)
|
int chk)
|
||||||
{
|
{
|
||||||
enum sci_status status;
|
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||||
|
enum sci_status status = SCI_SUCCESS;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
|
||||||
|
struct isci_request *ireq = ihost->reqs[i];
|
||||||
|
enum sci_status s;
|
||||||
|
|
||||||
|
s = sci_remote_device_terminate_req(ihost, idev, chk, ireq);
|
||||||
|
if (s != SCI_SUCCESS)
|
||||||
|
status = s;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum sci_status isci_remote_device_terminate_requests(
|
||||||
|
struct isci_host *ihost,
|
||||||
|
struct isci_remote_device *idev,
|
||||||
|
struct isci_request *ireq)
|
||||||
|
{
|
||||||
|
enum sci_status status = SCI_SUCCESS;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
if (isci_get_device(idev->domain_dev) == NULL) {
|
if (isci_get_device(idev) == NULL) {
|
||||||
|
dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n",
|
||||||
|
__func__, idev);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
status = SCI_FAILURE;
|
status = SCI_FAILURE;
|
||||||
} else {
|
} else {
|
||||||
status = sci_remote_device_suspend(idev);
|
dev_dbg(&ihost->pdev->dev,
|
||||||
|
"%s: idev=%p, ireq=%p; started_request_count=%d, "
|
||||||
|
"about to wait\n",
|
||||||
|
__func__, idev, ireq, idev->started_request_count);
|
||||||
|
if (ireq) {
|
||||||
|
/* Terminate a specific TC. */
|
||||||
|
sci_remote_device_terminate_req(ihost, idev, 0, ireq);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
if (status == SCI_SUCCESS) {
|
wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE,
|
||||||
dev_dbg(&ihost->pdev->dev,
|
&ireq->flags));
|
||||||
"%s: idev=%p, about to wait\n",
|
|
||||||
__func__, idev);
|
|
||||||
wait_event(ihost->eventq,
|
|
||||||
isci_remote_device_suspendcheck(idev));
|
|
||||||
status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
|
|
||||||
? SCI_SUCCESS : SCI_FAILURE;
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: idev=%p, wait done, device is %s\n",
|
|
||||||
__func__, idev,
|
|
||||||
test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
|
|
||||||
? "<suspended>" : "<deallocated!>");
|
|
||||||
|
|
||||||
} else
|
} else {
|
||||||
dev_dbg(scirdev_to_dev(idev),
|
/* Terminate all TCs. */
|
||||||
"%s: sci_remote_device_suspend failed, "
|
sci_remote_device_terminate_requests(idev);
|
||||||
"status = %d\n", __func__, status);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
wait_event(ihost->eventq,
|
||||||
|
idev->started_request_count == 0);
|
||||||
|
}
|
||||||
|
dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n",
|
||||||
|
__func__, idev);
|
||||||
isci_put_device(idev);
|
isci_put_device(idev);
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isci_remote_device_not_ready() - This function is called by the ihost when
|
||||||
|
* the remote device is not ready. We mark the isci device as ready (not
|
||||||
|
* "ready_for_io") and signal the waiting proccess.
|
||||||
|
* @isci_host: This parameter specifies the isci host object.
|
||||||
|
* @isci_device: This parameter specifies the remote device
|
||||||
|
*
|
||||||
|
* sci_lock is held on entrance to this function.
|
||||||
|
*/
|
||||||
|
static void isci_remote_device_not_ready(struct isci_host *ihost,
|
||||||
|
struct isci_remote_device *idev,
|
||||||
|
u32 reason)
|
||||||
|
{
|
||||||
|
dev_dbg(&ihost->pdev->dev,
|
||||||
|
"%s: isci_device = %p\n", __func__, idev);
|
||||||
|
|
||||||
|
switch (reason) {
|
||||||
|
case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
|
||||||
|
set_bit(IDEV_GONE, &idev->flags);
|
||||||
|
break;
|
||||||
|
case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
|
||||||
|
set_bit(IDEV_IO_NCQERROR, &idev->flags);
|
||||||
|
|
||||||
|
/* Suspend the remote device so the I/O can be terminated. */
|
||||||
|
sci_remote_device_suspend(idev);
|
||||||
|
|
||||||
|
/* Kill all outstanding requests for the device. */
|
||||||
|
sci_remote_device_terminate_requests(idev);
|
||||||
|
|
||||||
|
/* Fall through into the default case... */
|
||||||
|
default:
|
||||||
|
clear_bit(IDEV_IO_READY, &idev->flags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* called once the remote node context is ready to be freed.
|
/* called once the remote node context is ready to be freed.
|
||||||
* The remote device can now report that its stop operation is complete. none
|
* The remote device can now report that its stop operation is complete. none
|
||||||
*/
|
*/
|
||||||
|
@ -196,36 +225,10 @@ static void rnc_destruct_done(void *_dev)
|
||||||
sci_change_state(&idev->sm, SCI_DEV_STOPPED);
|
sci_change_state(&idev->sm, SCI_DEV_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum sci_status sci_remote_device_terminate_requests_checkabort(
|
|
||||||
struct isci_remote_device *idev,
|
|
||||||
int check_abort_pending)
|
|
||||||
{
|
|
||||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
|
||||||
enum sci_status status = SCI_SUCCESS;
|
|
||||||
u32 i;
|
|
||||||
|
|
||||||
for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
|
|
||||||
struct isci_request *ireq = ihost->reqs[i];
|
|
||||||
enum sci_status s;
|
|
||||||
|
|
||||||
if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
|
|
||||||
(ireq->target_device != idev) ||
|
|
||||||
(check_abort_pending && !test_bit(IREQ_PENDING_ABORT,
|
|
||||||
&ireq->flags)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
s = sci_controller_terminate_request(ihost, idev, ireq);
|
|
||||||
if (s != SCI_SUCCESS)
|
|
||||||
status = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum sci_status sci_remote_device_terminate_requests(
|
enum sci_status sci_remote_device_terminate_requests(
|
||||||
struct isci_remote_device *idev)
|
struct isci_remote_device *idev)
|
||||||
{
|
{
|
||||||
return sci_remote_device_terminate_requests_checkabort(idev, 0);
|
return sci_remote_device_terminate_reqs_checkabort(idev, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
|
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
|
||||||
|
@ -771,10 +774,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
|
||||||
if (status != SCI_SUCCESS)
|
if (status != SCI_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = sci_remote_node_context_start_task(&idev->rnc, ireq);
|
|
||||||
if (status != SCI_SUCCESS)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
status = sci_request_start(ireq);
|
status = sci_request_start(ireq);
|
||||||
if (status != SCI_SUCCESS)
|
if (status != SCI_SUCCESS)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -796,8 +795,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
|
||||||
sci_remote_node_context_suspend(
|
sci_remote_node_context_suspend(
|
||||||
&idev->rnc, SCI_SOFTWARE_SUSPENSION,
|
&idev->rnc, SCI_SOFTWARE_SUSPENSION,
|
||||||
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
|
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
|
||||||
sci_remote_node_context_resume(
|
|
||||||
&idev->rnc, sci_remote_device_continue_request, idev);
|
status = sci_remote_node_context_start_task(&idev->rnc, ireq,
|
||||||
|
sci_remote_device_continue_request, idev);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
sci_remote_device_start_request(idev, ireq, status);
|
sci_remote_device_start_request(idev, ireq, status);
|
||||||
|
@ -811,7 +811,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
|
||||||
if (status != SCI_SUCCESS)
|
if (status != SCI_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
status = sci_remote_node_context_start_task(&idev->rnc, ireq);
|
/* Resume the RNC as needed: */
|
||||||
|
status = sci_remote_node_context_start_task(&idev->rnc, ireq,
|
||||||
|
NULL, NULL);
|
||||||
if (status != SCI_SUCCESS)
|
if (status != SCI_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1322,20 +1324,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev)
|
|
||||||
{
|
|
||||||
DECLARE_COMPLETION_ONSTACK(aborted_task_completion);
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: idev = %p\n", __func__, idev);
|
|
||||||
|
|
||||||
/* Cleanup all requests pending for this device. */
|
|
||||||
isci_terminate_pending_requests(ihost, idev);
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: idev = %p, done\n", __func__, idev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function builds the isci_remote_device when a libsas dev_found message
|
* This function builds the isci_remote_device when a libsas dev_found message
|
||||||
* is received.
|
* is received.
|
||||||
|
@ -1495,32 +1483,28 @@ int isci_remote_device_found(struct domain_device *dev)
|
||||||
return status == SCI_SUCCESS ? 0 : -ENODEV;
|
return status == SCI_SUCCESS ? 0 : -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sci_status isci_remote_device_reset(
|
enum sci_status isci_remote_device_suspend_terminate(
|
||||||
struct isci_host *ihost,
|
struct isci_host *ihost,
|
||||||
struct isci_remote_device *idev)
|
struct isci_remote_device *idev,
|
||||||
|
struct isci_request *ireq)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
enum sci_status status;
|
enum sci_status status;
|
||||||
|
|
||||||
/* Put the device into a reset state so the suspension will not
|
/* Put the device into suspension. */
|
||||||
* automatically resume.
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
status = sci_remote_device_reset(idev);
|
sci_remote_device_suspend(idev);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
if (status != SCI_SUCCESS) {
|
|
||||||
|
/* Terminate and wait for the completions. */
|
||||||
|
status = isci_remote_device_terminate_requests(ihost, idev, ireq);
|
||||||
|
if (status != SCI_SUCCESS)
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: sci_remote_device_reset(%p) returned %d!\n",
|
"%s: isci_remote_device_terminate_requests(%p) "
|
||||||
|
"returned %d!\n",
|
||||||
__func__, idev, status);
|
__func__, idev, status);
|
||||||
return status;
|
|
||||||
}
|
/* NOTE: RNC resumption is left to the caller! */
|
||||||
/* Wait for the device suspend. */
|
|
||||||
status = isci_remote_device_suspend(ihost, idev);
|
|
||||||
if (status != SCI_SUCCESS) {
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: isci_remote_device_suspend(%p) returned %d!\n",
|
|
||||||
__func__, idev, status);
|
|
||||||
}
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,7 +1517,7 @@ int isci_remote_device_is_safe_to_abort(
|
||||||
enum sci_status sci_remote_device_abort_requests_pending_abort(
|
enum sci_status sci_remote_device_abort_requests_pending_abort(
|
||||||
struct isci_remote_device *idev)
|
struct isci_remote_device *idev)
|
||||||
{
|
{
|
||||||
return sci_remote_device_terminate_requests_checkabort(idev, 1);
|
return sci_remote_device_terminate_reqs_checkabort(idev, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sci_status isci_remote_device_reset_complete(
|
enum sci_status isci_remote_device_reset_complete(
|
||||||
|
@ -1545,7 +1529,6 @@ enum sci_status isci_remote_device_reset_complete(
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
status = sci_remote_device_reset_complete(idev);
|
status = sci_remote_device_reset_complete(idev);
|
||||||
sci_remote_device_resume(idev, NULL, NULL);
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -85,7 +85,6 @@ struct isci_remote_device {
|
||||||
#define IDEV_GONE 3
|
#define IDEV_GONE 3
|
||||||
#define IDEV_IO_READY 4
|
#define IDEV_IO_READY 4
|
||||||
#define IDEV_IO_NCQERROR 5
|
#define IDEV_IO_NCQERROR 5
|
||||||
#define IDEV_TXRX_SUSPENDED 6
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
struct isci_port *isci_port;
|
struct isci_port *isci_port;
|
||||||
|
@ -107,10 +106,8 @@ struct isci_remote_device {
|
||||||
|
|
||||||
/* device reference routines must be called under sci_lock */
|
/* device reference routines must be called under sci_lock */
|
||||||
static inline struct isci_remote_device *isci_get_device(
|
static inline struct isci_remote_device *isci_get_device(
|
||||||
struct domain_device *dev)
|
struct isci_remote_device *idev)
|
||||||
{
|
{
|
||||||
struct isci_remote_device *idev = dev->lldd_dev;
|
|
||||||
|
|
||||||
if (idev)
|
if (idev)
|
||||||
kref_get(&idev->kref);
|
kref_get(&idev->kref);
|
||||||
return idev;
|
return idev;
|
||||||
|
@ -378,4 +375,14 @@ enum sci_status isci_remote_device_reset(
|
||||||
enum sci_status isci_remote_device_reset_complete(
|
enum sci_status isci_remote_device_reset_complete(
|
||||||
struct isci_host *ihost,
|
struct isci_host *ihost,
|
||||||
struct isci_remote_device *idev);
|
struct isci_remote_device *idev);
|
||||||
|
|
||||||
|
enum sci_status isci_remote_device_suspend_terminate(
|
||||||
|
struct isci_host *ihost,
|
||||||
|
struct isci_remote_device *idev,
|
||||||
|
struct isci_request *ireq);
|
||||||
|
|
||||||
|
enum sci_status isci_remote_device_terminate_requests(
|
||||||
|
struct isci_host *ihost,
|
||||||
|
struct isci_remote_device *idev,
|
||||||
|
struct isci_request *ireq);
|
||||||
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
|
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
|
||||||
|
|
|
@ -317,8 +317,6 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_
|
||||||
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
||||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||||
|
|
||||||
set_bit(IDEV_TXRX_SUSPENDED, &idev->flags);
|
|
||||||
|
|
||||||
/* Terminate outstanding requests pending abort. */
|
/* Terminate outstanding requests pending abort. */
|
||||||
sci_remote_device_abort_requests_pending_abort(idev);
|
sci_remote_device_abort_requests_pending_abort(idev);
|
||||||
|
|
||||||
|
@ -326,16 +324,6 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_
|
||||||
sci_remote_node_context_continue_state_transitions(rnc);
|
sci_remote_node_context_continue_state_transitions(rnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sci_remote_node_context_tx_rx_suspended_state_exit(
|
|
||||||
struct sci_base_state_machine *sm)
|
|
||||||
{
|
|
||||||
struct sci_remote_node_context *rnc
|
|
||||||
= container_of(sm, typeof(*rnc), sm);
|
|
||||||
struct isci_remote_device *idev = rnc_to_dev(rnc);
|
|
||||||
|
|
||||||
clear_bit(IDEV_TXRX_SUSPENDED, &idev->flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sci_remote_node_context_await_suspend_state_exit(
|
static void sci_remote_node_context_await_suspend_state_exit(
|
||||||
struct sci_base_state_machine *sm)
|
struct sci_base_state_machine *sm)
|
||||||
{
|
{
|
||||||
|
@ -366,8 +354,6 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = {
|
||||||
},
|
},
|
||||||
[SCI_RNC_TX_RX_SUSPENDED] = {
|
[SCI_RNC_TX_RX_SUSPENDED] = {
|
||||||
.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
|
.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
|
||||||
.exit_state
|
|
||||||
= sci_remote_node_context_tx_rx_suspended_state_exit,
|
|
||||||
},
|
},
|
||||||
[SCI_RNC_AWAIT_SUSPENSION] = {
|
[SCI_RNC_AWAIT_SUSPENSION] = {
|
||||||
.exit_state = sci_remote_node_context_await_suspend_state_exit,
|
.exit_state = sci_remote_node_context_await_suspend_state_exit,
|
||||||
|
@ -671,8 +657,11 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
|
enum sci_status sci_remote_node_context_start_task(
|
||||||
struct isci_request *ireq)
|
struct sci_remote_node_context *sci_rnc,
|
||||||
|
struct isci_request *ireq,
|
||||||
|
scics_sds_remote_node_context_callback cb_fn,
|
||||||
|
void *cb_p)
|
||||||
{
|
{
|
||||||
enum scis_sds_remote_node_context_states state;
|
enum scis_sds_remote_node_context_states state;
|
||||||
|
|
||||||
|
@ -684,7 +673,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
case SCI_RNC_TX_SUSPENDED:
|
case SCI_RNC_TX_SUSPENDED:
|
||||||
case SCI_RNC_TX_RX_SUSPENDED:
|
case SCI_RNC_TX_RX_SUSPENDED:
|
||||||
sci_remote_node_context_resume(sci_rnc, NULL, NULL);
|
sci_remote_node_context_resume(sci_rnc, cb_fn, cb_p);
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
default:
|
default:
|
||||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||||
|
|
|
@ -211,7 +211,9 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
|
||||||
scics_sds_remote_node_context_callback cb_fn,
|
scics_sds_remote_node_context_callback cb_fn,
|
||||||
void *cb_p);
|
void *cb_p);
|
||||||
enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
|
enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
|
||||||
struct isci_request *ireq);
|
struct isci_request *ireq,
|
||||||
|
scics_sds_remote_node_context_callback cb_fn,
|
||||||
|
void *cb_p);
|
||||||
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
|
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
|
||||||
struct isci_request *ireq);
|
struct isci_request *ireq);
|
||||||
int sci_remote_node_context_is_safe_to_abort(
|
int sci_remote_node_context_is_safe_to_abort(
|
||||||
|
|
|
@ -2491,9 +2491,6 @@ static void isci_request_process_response_iu(
|
||||||
* @request: This parameter is the completed isci_request object.
|
* @request: This parameter is the completed isci_request object.
|
||||||
* @response_ptr: This parameter specifies the service response for the I/O.
|
* @response_ptr: This parameter specifies the service response for the I/O.
|
||||||
* @status_ptr: This parameter specifies the exec status for the I/O.
|
* @status_ptr: This parameter specifies the exec status for the I/O.
|
||||||
* @complete_to_host_ptr: This parameter specifies the action to be taken by
|
|
||||||
* the LLDD with respect to completing this request or forcing an abort
|
|
||||||
* condition on the I/O.
|
|
||||||
* @open_rej_reason: This parameter specifies the encoded reason for the
|
* @open_rej_reason: This parameter specifies the encoded reason for the
|
||||||
* abandon-class reject.
|
* abandon-class reject.
|
||||||
*
|
*
|
||||||
|
@ -2504,14 +2501,12 @@ static void isci_request_set_open_reject_status(
|
||||||
struct sas_task *task,
|
struct sas_task *task,
|
||||||
enum service_response *response_ptr,
|
enum service_response *response_ptr,
|
||||||
enum exec_status *status_ptr,
|
enum exec_status *status_ptr,
|
||||||
enum isci_completion_selection *complete_to_host_ptr,
|
|
||||||
enum sas_open_rej_reason open_rej_reason)
|
enum sas_open_rej_reason open_rej_reason)
|
||||||
{
|
{
|
||||||
/* Task in the target is done. */
|
/* Task in the target is done. */
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
*response_ptr = SAS_TASK_UNDELIVERED;
|
*response_ptr = SAS_TASK_UNDELIVERED;
|
||||||
*status_ptr = SAS_OPEN_REJECT;
|
*status_ptr = SAS_OPEN_REJECT;
|
||||||
*complete_to_host_ptr = isci_perform_normal_io_completion;
|
|
||||||
task->task_status.open_rej_reason = open_rej_reason;
|
task->task_status.open_rej_reason = open_rej_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2521,9 +2516,6 @@ static void isci_request_set_open_reject_status(
|
||||||
* @request: This parameter is the completed isci_request object.
|
* @request: This parameter is the completed isci_request object.
|
||||||
* @response_ptr: This parameter specifies the service response for the I/O.
|
* @response_ptr: This parameter specifies the service response for the I/O.
|
||||||
* @status_ptr: This parameter specifies the exec status for the I/O.
|
* @status_ptr: This parameter specifies the exec status for the I/O.
|
||||||
* @complete_to_host_ptr: This parameter specifies the action to be taken by
|
|
||||||
* the LLDD with respect to completing this request or forcing an abort
|
|
||||||
* condition on the I/O.
|
|
||||||
*
|
*
|
||||||
* none.
|
* none.
|
||||||
*/
|
*/
|
||||||
|
@ -2532,8 +2524,7 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
struct isci_request *request,
|
struct isci_request *request,
|
||||||
struct sas_task *task,
|
struct sas_task *task,
|
||||||
enum service_response *response_ptr,
|
enum service_response *response_ptr,
|
||||||
enum exec_status *status_ptr,
|
enum exec_status *status_ptr)
|
||||||
enum isci_completion_selection *complete_to_host_ptr)
|
|
||||||
{
|
{
|
||||||
unsigned int cstatus;
|
unsigned int cstatus;
|
||||||
|
|
||||||
|
@ -2574,9 +2565,6 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
*status_ptr = SAS_ABORTED_TASK;
|
*status_ptr = SAS_ABORTED_TASK;
|
||||||
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
|
|
||||||
*complete_to_host_ptr =
|
|
||||||
isci_perform_normal_io_completion;
|
|
||||||
} else {
|
} else {
|
||||||
/* Task in the target is not done. */
|
/* Task in the target is not done. */
|
||||||
*response_ptr = SAS_TASK_UNDELIVERED;
|
*response_ptr = SAS_TASK_UNDELIVERED;
|
||||||
|
@ -2587,9 +2575,6 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
*status_ptr = SAM_STAT_TASK_ABORTED;
|
*status_ptr = SAM_STAT_TASK_ABORTED;
|
||||||
|
|
||||||
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
|
|
||||||
*complete_to_host_ptr =
|
|
||||||
isci_perform_error_io_completion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -2618,8 +2603,6 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
*status_ptr = SAS_ABORTED_TASK;
|
*status_ptr = SAS_ABORTED_TASK;
|
||||||
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
|
|
||||||
*complete_to_host_ptr = isci_perform_normal_io_completion;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
@ -2630,7 +2613,7 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_WRONG_DEST);
|
SAS_OREJ_WRONG_DEST);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
|
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
|
||||||
|
@ -2640,56 +2623,56 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
*/
|
*/
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_RESV_AB0);
|
SAS_OREJ_RESV_AB0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
|
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_RESV_AB1);
|
SAS_OREJ_RESV_AB1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
|
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_RESV_AB2);
|
SAS_OREJ_RESV_AB2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
|
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_RESV_AB3);
|
SAS_OREJ_RESV_AB3);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
|
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_BAD_DEST);
|
SAS_OREJ_BAD_DEST);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
|
case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_STP_NORES);
|
SAS_OREJ_STP_NORES);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
|
case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_EPROTO);
|
SAS_OREJ_EPROTO);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
|
case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
|
||||||
|
|
||||||
isci_request_set_open_reject_status(
|
isci_request_set_open_reject_status(
|
||||||
request, task, response_ptr, status_ptr,
|
request, task, response_ptr, status_ptr,
|
||||||
complete_to_host_ptr, SAS_OREJ_CONN_RATE);
|
SAS_OREJ_CONN_RATE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCU_TASK_DONE_LL_R_ERR:
|
case SCU_TASK_DONE_LL_R_ERR:
|
||||||
|
@ -2721,97 +2704,14 @@ static void isci_request_handle_controller_specific_errors(
|
||||||
*response_ptr = SAS_TASK_UNDELIVERED;
|
*response_ptr = SAS_TASK_UNDELIVERED;
|
||||||
*status_ptr = SAM_STAT_TASK_ABORTED;
|
*status_ptr = SAM_STAT_TASK_ABORTED;
|
||||||
|
|
||||||
if (task->task_proto == SAS_PROTOCOL_SMP) {
|
if (task->task_proto == SAS_PROTOCOL_SMP)
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
|
else
|
||||||
*complete_to_host_ptr = isci_perform_normal_io_completion;
|
|
||||||
} else {
|
|
||||||
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
|
|
||||||
*complete_to_host_ptr = isci_perform_error_io_completion;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_task_save_for_upper_layer_completion() - This function saves the
|
|
||||||
* request for later completion to the upper layer driver.
|
|
||||||
* @host: This parameter is a pointer to the host on which the the request
|
|
||||||
* should be queued (either as an error or success).
|
|
||||||
* @request: This parameter is the completed request.
|
|
||||||
* @response: This parameter is the response code for the completed task.
|
|
||||||
* @status: This parameter is the status code for the completed task.
|
|
||||||
*
|
|
||||||
* none.
|
|
||||||
*/
|
|
||||||
static void isci_task_save_for_upper_layer_completion(
|
|
||||||
struct isci_host *host,
|
|
||||||
struct isci_request *request,
|
|
||||||
enum service_response response,
|
|
||||||
enum exec_status status,
|
|
||||||
enum isci_completion_selection task_notification_selection)
|
|
||||||
{
|
|
||||||
struct sas_task *task = isci_request_access_task(request);
|
|
||||||
|
|
||||||
task_notification_selection
|
|
||||||
= isci_task_set_completion_status(task, response, status,
|
|
||||||
task_notification_selection);
|
|
||||||
|
|
||||||
/* Tasks aborted specifically by a call to the lldd_abort_task
|
|
||||||
* function should not be completed to the host in the regular path.
|
|
||||||
*/
|
|
||||||
switch (task_notification_selection) {
|
|
||||||
|
|
||||||
case isci_perform_normal_io_completion:
|
|
||||||
/* Normal notification (task_done) */
|
|
||||||
|
|
||||||
/* Add to the completed list. */
|
|
||||||
list_add(&request->completed_node,
|
|
||||||
&host->requests_to_complete);
|
|
||||||
|
|
||||||
/* Take the request off the device's pending request list. */
|
|
||||||
list_del_init(&request->dev_node);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case isci_perform_aborted_io_completion:
|
|
||||||
/* No notification to libsas because this request is
|
|
||||||
* already in the abort path.
|
|
||||||
*/
|
|
||||||
/* 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:
|
|
||||||
/* Use sas_task_abort */
|
|
||||||
/* Add to the aborted list. */
|
|
||||||
list_add(&request->completed_node,
|
|
||||||
&host->requests_to_errorback);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Add to the error to libsas list. */
|
|
||||||
list_add(&request->completed_node,
|
|
||||||
&host->requests_to_errorback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dev_dbg(&host->pdev->dev,
|
|
||||||
"%s: %d - task = %p, response=%d (%d), status=%d (%d)\n",
|
|
||||||
__func__, task_notification_selection, task,
|
|
||||||
(task) ? task->task_status.resp : 0, response,
|
|
||||||
(task) ? task->task_status.stat : 0, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis)
|
static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis)
|
||||||
{
|
{
|
||||||
struct task_status_struct *ts = &task->task_status;
|
struct task_status_struct *ts = &task->task_status;
|
||||||
|
@ -2844,9 +2744,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
struct isci_remote_device *idev = request->target_device;
|
struct isci_remote_device *idev = request->target_device;
|
||||||
enum service_response response = SAS_TASK_UNDELIVERED;
|
enum service_response response = SAS_TASK_UNDELIVERED;
|
||||||
enum exec_status status = SAS_ABORTED_TASK;
|
enum exec_status status = SAS_ABORTED_TASK;
|
||||||
enum isci_request_status request_status;
|
|
||||||
enum isci_completion_selection complete_to_host
|
|
||||||
= isci_perform_normal_io_completion;
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: request = %p, task = %p,\n"
|
"%s: request = %p, task = %p,\n"
|
||||||
|
@ -2857,113 +2754,7 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
task->data_dir,
|
task->data_dir,
|
||||||
completion_status);
|
completion_status);
|
||||||
|
|
||||||
spin_lock(&request->state_lock);
|
|
||||||
request_status = request->status;
|
|
||||||
|
|
||||||
/* Decode the request status. Note that if the request has been
|
|
||||||
* aborted by a task management function, we don't care
|
|
||||||
* what the status is.
|
|
||||||
*/
|
|
||||||
switch (request_status) {
|
|
||||||
|
|
||||||
case aborted:
|
|
||||||
/* "aborted" indicates that the request was aborted by a task
|
|
||||||
* management function, since once a task management request is
|
|
||||||
* perfomed by the device, the request only completes because
|
|
||||||
* of the subsequent driver terminate.
|
|
||||||
*
|
|
||||||
* Aborted also means an external thread is explicitly managing
|
|
||||||
* this request, so that we do not complete it up the stack.
|
|
||||||
*
|
|
||||||
* The target is still there (since the TMF was successful).
|
|
||||||
*/
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
|
||||||
response = SAS_TASK_COMPLETE;
|
|
||||||
|
|
||||||
/* See if the device has been/is being stopped. Note
|
|
||||||
* that we ignore the quiesce state, since we are
|
|
||||||
* concerned about the actual device state.
|
|
||||||
*/
|
|
||||||
if (!idev)
|
|
||||||
status = SAS_DEVICE_UNKNOWN;
|
|
||||||
else
|
|
||||||
status = SAS_ABORTED_TASK;
|
|
||||||
|
|
||||||
complete_to_host = isci_perform_aborted_io_completion;
|
|
||||||
/* This was an aborted request. */
|
|
||||||
|
|
||||||
spin_unlock(&request->state_lock);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case aborting:
|
|
||||||
/* aborting means that the task management function tried and
|
|
||||||
* failed to abort the request. We need to note the request
|
|
||||||
* as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the
|
|
||||||
* target as down.
|
|
||||||
*
|
|
||||||
* Aborting also means an external thread is explicitly managing
|
|
||||||
* this request, so that we do not complete it up the stack.
|
|
||||||
*/
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
|
||||||
response = SAS_TASK_UNDELIVERED;
|
|
||||||
|
|
||||||
if (!idev)
|
|
||||||
/* The device has been /is being stopped. Note that
|
|
||||||
* we ignore the quiesce state, since we are
|
|
||||||
* concerned about the actual device state.
|
|
||||||
*/
|
|
||||||
status = SAS_DEVICE_UNKNOWN;
|
|
||||||
else
|
|
||||||
status = SAS_PHY_DOWN;
|
|
||||||
|
|
||||||
complete_to_host = isci_perform_aborted_io_completion;
|
|
||||||
|
|
||||||
/* This was an aborted request. */
|
|
||||||
|
|
||||||
spin_unlock(&request->state_lock);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case terminating:
|
|
||||||
|
|
||||||
/* This was an terminated request. This happens when
|
|
||||||
* the I/O is being terminated because of an action on
|
|
||||||
* the device (reset, tear down, etc.), and the I/O needs
|
|
||||||
* to be completed up the stack.
|
|
||||||
*/
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
|
||||||
response = SAS_TASK_UNDELIVERED;
|
|
||||||
|
|
||||||
/* See if the device has been/is being stopped. Note
|
|
||||||
* that we ignore the quiesce state, since we are
|
|
||||||
* concerned about the actual device state.
|
|
||||||
*/
|
|
||||||
if (!idev)
|
|
||||||
status = SAS_DEVICE_UNKNOWN;
|
|
||||||
else
|
|
||||||
status = SAS_ABORTED_TASK;
|
|
||||||
|
|
||||||
complete_to_host = isci_perform_aborted_io_completion;
|
|
||||||
|
|
||||||
/* This was a terminated request. */
|
|
||||||
|
|
||||||
spin_unlock(&request->state_lock);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case dead:
|
|
||||||
/* This was a terminated request that timed-out during the
|
|
||||||
* termination process. There is no task to complete to
|
|
||||||
* libsas.
|
|
||||||
*/
|
|
||||||
complete_to_host = isci_perform_normal_io_completion;
|
|
||||||
spin_unlock(&request->state_lock);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
/* The request is done from an SCU HW perspective. */
|
/* 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. */
|
/* This is an active request being completed from the core. */
|
||||||
switch (completion_status) {
|
switch (completion_status) {
|
||||||
|
@ -2971,9 +2762,7 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
case SCI_IO_FAILURE_RESPONSE_VALID:
|
case SCI_IO_FAILURE_RESPONSE_VALID:
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n",
|
"%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n",
|
||||||
__func__,
|
__func__, request, task);
|
||||||
request,
|
|
||||||
task);
|
|
||||||
|
|
||||||
if (sas_protocol_ata(task->task_proto)) {
|
if (sas_protocol_ata(task->task_proto)) {
|
||||||
isci_process_stp_response(task, &request->stp.rsp);
|
isci_process_stp_response(task, &request->stp.rsp);
|
||||||
|
@ -3029,27 +2818,21 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n",
|
"%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n",
|
||||||
__func__,
|
__func__, status);
|
||||||
status);
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev, "%s: SCI_IO_SUCCESS\n",
|
||||||
"%s: SCI_IO_SUCCESS\n",
|
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCI_IO_FAILURE_TERMINATED:
|
case SCI_IO_FAILURE_TERMINATED:
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n",
|
"%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n",
|
||||||
__func__,
|
__func__, request, task);
|
||||||
request,
|
|
||||||
task);
|
|
||||||
|
|
||||||
/* The request was terminated explicitly. No handling
|
/* The request was terminated explicitly. */
|
||||||
* is needed in the SCSI error handler path.
|
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
*/
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
|
||||||
response = SAS_TASK_UNDELIVERED;
|
response = SAS_TASK_UNDELIVERED;
|
||||||
|
|
||||||
/* See if the device has been/is being stopped. Note
|
/* See if the device has been/is being stopped. Note
|
||||||
|
@ -3060,16 +2843,13 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
status = SAS_DEVICE_UNKNOWN;
|
status = SAS_DEVICE_UNKNOWN;
|
||||||
else
|
else
|
||||||
status = SAS_ABORTED_TASK;
|
status = SAS_ABORTED_TASK;
|
||||||
|
|
||||||
complete_to_host = isci_perform_normal_io_completion;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR:
|
case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR:
|
||||||
|
|
||||||
isci_request_handle_controller_specific_errors(
|
isci_request_handle_controller_specific_errors(idev, request,
|
||||||
idev, request, task, &response, &status,
|
task, &response,
|
||||||
&complete_to_host);
|
&status);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED:
|
case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED:
|
||||||
|
@ -3088,7 +2868,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
response = SAS_TASK_UNDELIVERED;
|
response = SAS_TASK_UNDELIVERED;
|
||||||
status = SAM_STAT_TASK_ABORTED;
|
status = SAM_STAT_TASK_ABORTED;
|
||||||
|
|
||||||
complete_to_host = isci_perform_error_io_completion;
|
|
||||||
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3101,7 +2880,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
else
|
else
|
||||||
status = SAS_ABORTED_TASK;
|
status = SAS_ABORTED_TASK;
|
||||||
|
|
||||||
complete_to_host = isci_perform_normal_io_completion;
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -3124,15 +2902,10 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
else
|
else
|
||||||
status = SAS_ABORTED_TASK;
|
status = SAS_ABORTED_TASK;
|
||||||
|
|
||||||
if (SAS_PROTOCOL_SMP == task->task_proto) {
|
if (SAS_PROTOCOL_SMP == task->task_proto)
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
complete_to_host = isci_perform_normal_io_completion;
|
else
|
||||||
} else {
|
|
||||||
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags);
|
||||||
complete_to_host = isci_perform_error_io_completion;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3167,10 +2940,24 @@ static void isci_request_io_request_complete(struct isci_host *ihost,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Put the completed request on the correct list */
|
spin_lock_irqsave(&task->task_state_lock, task_flags);
|
||||||
isci_task_save_for_upper_layer_completion(ihost, request, response,
|
|
||||||
status, complete_to_host
|
task->task_status.resp = response;
|
||||||
);
|
task->task_status.stat = status;
|
||||||
|
|
||||||
|
if (test_bit(IREQ_COMPLETE_IN_TARGET, &request->flags)) {
|
||||||
|
/* Normal notification (task_done) */
|
||||||
|
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, task_flags);
|
||||||
|
|
||||||
|
/* Add to the completed list. */
|
||||||
|
list_add(&request->completed_node, &ihost->requests_to_complete);
|
||||||
|
|
||||||
|
/* Take the request off the device's pending request list. */
|
||||||
|
list_del_init(&request->dev_node);
|
||||||
|
|
||||||
/* complete the io request to the core. */
|
/* complete the io request to the core. */
|
||||||
sci_controller_complete_io(ihost, request->target_device, request);
|
sci_controller_complete_io(ihost, request->target_device, request);
|
||||||
|
@ -3626,7 +3413,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t
|
||||||
ireq->num_sg_entries = 0;
|
ireq->num_sg_entries = 0;
|
||||||
INIT_LIST_HEAD(&ireq->completed_node);
|
INIT_LIST_HEAD(&ireq->completed_node);
|
||||||
INIT_LIST_HEAD(&ireq->dev_node);
|
INIT_LIST_HEAD(&ireq->dev_node);
|
||||||
isci_request_change_state(ireq, allocated);
|
|
||||||
|
|
||||||
return ireq;
|
return ireq;
|
||||||
}
|
}
|
||||||
|
@ -3721,15 +3507,12 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide
|
||||||
*/
|
*/
|
||||||
list_add(&ireq->dev_node, &idev->reqs_in_process);
|
list_add(&ireq->dev_node, &idev->reqs_in_process);
|
||||||
|
|
||||||
if (status == SCI_SUCCESS) {
|
if (status != SCI_SUCCESS) {
|
||||||
isci_request_change_state(ireq, started);
|
|
||||||
} else {
|
|
||||||
/* The request did not really start in the
|
/* The request did not really start in the
|
||||||
* hardware, so clear the request handle
|
* hardware, so clear the request handle
|
||||||
* here so no terminations will be done.
|
* here so no terminations will be done.
|
||||||
*/
|
*/
|
||||||
set_bit(IREQ_TERMINATED, &ireq->flags);
|
set_bit(IREQ_TERMINATED, &ireq->flags);
|
||||||
isci_request_change_state(ireq, completed);
|
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
|
|
|
@ -60,23 +60,6 @@
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "scu_task_context.h"
|
#include "scu_task_context.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* struct isci_request_status - This enum defines the possible states of an I/O
|
|
||||||
* request.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum isci_request_status {
|
|
||||||
unallocated = 0x00,
|
|
||||||
allocated = 0x01,
|
|
||||||
started = 0x02,
|
|
||||||
completed = 0x03,
|
|
||||||
aborting = 0x04,
|
|
||||||
aborted = 0x05,
|
|
||||||
terminating = 0x06,
|
|
||||||
dead = 0x07
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isci_stp_request - extra request infrastructure to handle pio/atapi protocol
|
* isci_stp_request - extra request infrastructure to handle pio/atapi protocol
|
||||||
* @pio_len - number of bytes requested at PIO setup
|
* @pio_len - number of bytes requested at PIO setup
|
||||||
|
@ -97,13 +80,13 @@ struct isci_stp_request {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct isci_request {
|
struct isci_request {
|
||||||
enum isci_request_status status;
|
|
||||||
#define IREQ_COMPLETE_IN_TARGET 0
|
#define IREQ_COMPLETE_IN_TARGET 0
|
||||||
#define IREQ_TERMINATED 1
|
#define IREQ_TERMINATED 1
|
||||||
#define IREQ_TMF 2
|
#define IREQ_TMF 2
|
||||||
#define IREQ_ACTIVE 3
|
#define IREQ_ACTIVE 3
|
||||||
#define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */
|
#define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */
|
||||||
#define IREQ_TC_ABORT_POSTED 5
|
#define IREQ_TC_ABORT_POSTED 5
|
||||||
|
#define IREQ_ABORT_PATH_ACTIVE 6
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
|
/* XXX kill ttype and ttype_ptr, allocate full sas_task */
|
||||||
union ttype_ptr_union {
|
union ttype_ptr_union {
|
||||||
|
@ -115,7 +98,6 @@ struct isci_request {
|
||||||
struct list_head completed_node;
|
struct list_head completed_node;
|
||||||
/* For use in the reqs_in_process list: */
|
/* For use in the reqs_in_process list: */
|
||||||
struct list_head dev_node;
|
struct list_head dev_node;
|
||||||
spinlock_t state_lock;
|
|
||||||
dma_addr_t request_daddr;
|
dma_addr_t request_daddr;
|
||||||
dma_addr_t zero_scatter_daddr;
|
dma_addr_t zero_scatter_daddr;
|
||||||
unsigned int num_sg_entries;
|
unsigned int num_sg_entries;
|
||||||
|
@ -304,92 +286,6 @@ sci_io_request_get_dma_addr(struct isci_request *ireq, void *virt_addr)
|
||||||
return ireq->request_daddr + (requested_addr - base_addr);
|
return ireq->request_daddr + (requested_addr - base_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_request_change_state() - This function sets the status of the request
|
|
||||||
* object.
|
|
||||||
* @request: This parameter points to the isci_request object
|
|
||||||
* @status: This Parameter is the new status of the object
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline enum isci_request_status
|
|
||||||
isci_request_change_state(struct isci_request *isci_request,
|
|
||||||
enum isci_request_status status)
|
|
||||||
{
|
|
||||||
enum isci_request_status old_state;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
dev_dbg(&isci_request->isci_host->pdev->dev,
|
|
||||||
"%s: isci_request = %p, state = 0x%x\n",
|
|
||||||
__func__,
|
|
||||||
isci_request,
|
|
||||||
status);
|
|
||||||
|
|
||||||
BUG_ON(isci_request == NULL);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&isci_request->state_lock, flags);
|
|
||||||
old_state = isci_request->status;
|
|
||||||
isci_request->status = status;
|
|
||||||
spin_unlock_irqrestore(&isci_request->state_lock, flags);
|
|
||||||
|
|
||||||
return old_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_request_change_started_to_newstate() - This function sets the status of
|
|
||||||
* the request object.
|
|
||||||
* @request: This parameter points to the isci_request object
|
|
||||||
* @status: This Parameter is the new status of the object
|
|
||||||
*
|
|
||||||
* state previous to any change.
|
|
||||||
*/
|
|
||||||
static inline enum isci_request_status
|
|
||||||
isci_request_change_started_to_newstate(struct isci_request *isci_request,
|
|
||||||
struct completion *completion_ptr,
|
|
||||||
enum isci_request_status newstate)
|
|
||||||
{
|
|
||||||
enum isci_request_status old_state;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&isci_request->state_lock, flags);
|
|
||||||
|
|
||||||
old_state = isci_request->status;
|
|
||||||
|
|
||||||
if (old_state == started || old_state == aborting) {
|
|
||||||
BUG_ON(isci_request->io_request_completion != NULL);
|
|
||||||
|
|
||||||
isci_request->io_request_completion = completion_ptr;
|
|
||||||
isci_request->status = newstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&isci_request->state_lock, flags);
|
|
||||||
|
|
||||||
dev_dbg(&isci_request->isci_host->pdev->dev,
|
|
||||||
"%s: isci_request = %p, old_state = 0x%x\n",
|
|
||||||
__func__,
|
|
||||||
isci_request,
|
|
||||||
old_state);
|
|
||||||
|
|
||||||
return old_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_request_change_started_to_aborted() - This function sets the status of
|
|
||||||
* the request object.
|
|
||||||
* @request: This parameter points to the isci_request object
|
|
||||||
* @completion_ptr: This parameter is saved as the kernel completion structure
|
|
||||||
* signalled when the old request completes.
|
|
||||||
*
|
|
||||||
* state previous to any change.
|
|
||||||
*/
|
|
||||||
static inline enum isci_request_status
|
|
||||||
isci_request_change_started_to_aborted(struct isci_request *isci_request,
|
|
||||||
struct completion *completion_ptr)
|
|
||||||
{
|
|
||||||
return isci_request_change_started_to_newstate(isci_request,
|
|
||||||
completion_ptr,
|
|
||||||
aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr)
|
#define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr)
|
||||||
|
|
||||||
#define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr)
|
#define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr)
|
||||||
|
@ -399,8 +295,6 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost,
|
||||||
u16 tag);
|
u16 tag);
|
||||||
int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev,
|
int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev,
|
||||||
struct sas_task *task, u16 tag);
|
struct sas_task *task, u16 tag);
|
||||||
void isci_terminate_pending_requests(struct isci_host *ihost,
|
|
||||||
struct isci_remote_device *idev);
|
|
||||||
enum sci_status
|
enum sci_status
|
||||||
sci_task_request_construct(struct isci_host *ihost,
|
sci_task_request_construct(struct isci_host *ihost,
|
||||||
struct isci_remote_device *idev,
|
struct isci_remote_device *idev,
|
||||||
|
|
|
@ -78,54 +78,25 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
|
||||||
enum exec_status status)
|
enum exec_status status)
|
||||||
|
|
||||||
{
|
{
|
||||||
enum isci_completion_selection disposition;
|
unsigned long flags;
|
||||||
|
|
||||||
disposition = isci_perform_normal_io_completion;
|
|
||||||
disposition = isci_task_set_completion_status(task, response, status,
|
|
||||||
disposition);
|
|
||||||
|
|
||||||
/* Tasks aborted specifically by a call to the lldd_abort_task
|
|
||||||
* function should not be completed to the host in the regular path.
|
|
||||||
*/
|
|
||||||
switch (disposition) {
|
|
||||||
case isci_perform_normal_io_completion:
|
|
||||||
/* Normal notification (task_done) */
|
/* Normal notification (task_done) */
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n",
|
||||||
"%s: Normal - task = %p, response=%d, "
|
|
||||||
"status=%d\n",
|
|
||||||
__func__, task, response, status);
|
__func__, task, response, status);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||||
|
|
||||||
|
task->task_status.resp = response;
|
||||||
|
task->task_status.stat = status;
|
||||||
|
|
||||||
|
/* Normal notification (task_done) */
|
||||||
|
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||||
|
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
||||||
|
SAS_TASK_STATE_PENDING);
|
||||||
task->lldd_task = NULL;
|
task->lldd_task = NULL;
|
||||||
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||||
|
|
||||||
task->task_done(task);
|
task->task_done(task);
|
||||||
break;
|
|
||||||
|
|
||||||
case isci_perform_aborted_io_completion:
|
|
||||||
/*
|
|
||||||
* No notification because this request is already in the
|
|
||||||
* abort path.
|
|
||||||
*/
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: Aborted - task = %p, response=%d, "
|
|
||||||
"status=%d\n",
|
|
||||||
__func__, task, response, status);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case isci_perform_error_io_completion:
|
|
||||||
/* Use sas_task_abort */
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: Error - task = %p, response=%d, "
|
|
||||||
"status=%d\n",
|
|
||||||
__func__, task, response, status);
|
|
||||||
sas_task_abort(task);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: isci task notification default case!",
|
|
||||||
__func__);
|
|
||||||
sas_task_abort(task);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define for_each_sas_task(num, task) \
|
#define for_each_sas_task(num, task) \
|
||||||
|
@ -289,60 +260,6 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost,
|
||||||
return ireq;
|
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 (test_bit(IREQ_TMF, &ireq->flags)) {
|
|
||||||
/* 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);
|
|
||||||
} else {
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
static int isci_task_execute_tmf(struct isci_host *ihost,
|
||||||
struct isci_remote_device *idev,
|
struct isci_remote_device *idev,
|
||||||
struct isci_tmf *tmf, unsigned long timeout_ms)
|
struct isci_tmf *tmf, unsigned long timeout_ms)
|
||||||
|
@ -400,15 +317,12 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
goto err_tci;
|
goto err_tci;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmf->cb_state_func != NULL)
|
|
||||||
tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data);
|
|
||||||
|
|
||||||
isci_request_change_state(ireq, started);
|
|
||||||
|
|
||||||
/* add the request to the remote device request list. */
|
/* add the request to the remote device request list. */
|
||||||
list_add(&ireq->dev_node, &idev->reqs_in_process);
|
list_add(&ireq->dev_node, &idev->reqs_in_process);
|
||||||
|
|
||||||
|
/* The RNC must be unsuspended before the TMF can get a response. */
|
||||||
|
sci_remote_device_resume(idev, NULL, NULL);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
/* Wait for the TMF to complete, or a timeout. */
|
/* Wait for the TMF to complete, or a timeout. */
|
||||||
|
@ -419,32 +333,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
|
||||||
/* The TMF did not complete - this could be because
|
/* The TMF did not complete - this could be because
|
||||||
* of an unplug. Terminate the TMF request now.
|
* of an unplug. Terminate the TMF request now.
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
isci_remote_device_suspend_terminate(ihost, idev, ireq);
|
||||||
|
|
||||||
if (tmf->cb_state_func != NULL)
|
|
||||||
tmf->cb_state_func(isci_tmf_timed_out, tmf,
|
|
||||||
tmf->cb_data);
|
|
||||||
|
|
||||||
sci_controller_terminate_request(ihost, idev, ireq);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
||||||
|
|
||||||
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(ihost, tmf);
|
isci_print_tmf(ihost, tmf);
|
||||||
|
@ -476,316 +365,20 @@ static int isci_task_execute_tmf(struct isci_host *ihost,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void isci_task_build_tmf(struct isci_tmf *tmf,
|
static void isci_task_build_tmf(struct isci_tmf *tmf,
|
||||||
enum isci_tmf_function_codes code,
|
enum isci_tmf_function_codes code)
|
||||||
void (*tmf_sent_cb)(enum isci_tmf_cb_state,
|
|
||||||
struct isci_tmf *,
|
|
||||||
void *),
|
|
||||||
void *cb_data)
|
|
||||||
{
|
{
|
||||||
memset(tmf, 0, sizeof(*tmf));
|
memset(tmf, 0, sizeof(*tmf));
|
||||||
|
|
||||||
tmf->tmf_code = code;
|
tmf->tmf_code = code;
|
||||||
tmf->cb_state_func = tmf_sent_cb;
|
|
||||||
tmf->cb_data = cb_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf,
|
static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf,
|
||||||
enum isci_tmf_function_codes code,
|
enum isci_tmf_function_codes code,
|
||||||
void (*tmf_sent_cb)(enum isci_tmf_cb_state,
|
|
||||||
struct isci_tmf *,
|
|
||||||
void *),
|
|
||||||
struct isci_request *old_request)
|
struct isci_request *old_request)
|
||||||
{
|
{
|
||||||
isci_task_build_tmf(tmf, code, tmf_sent_cb, old_request);
|
isci_task_build_tmf(tmf, code);
|
||||||
tmf->io_tag = old_request->io_tag;
|
tmf->io_tag = old_request->io_tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_task_validate_request_to_abort() - This function checks the given I/O
|
|
||||||
* against the "started" state. If the request is still "started", it's
|
|
||||||
* state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD
|
|
||||||
* BEFORE CALLING THIS FUNCTION.
|
|
||||||
* @isci_request: This parameter specifies the request object to control.
|
|
||||||
* @isci_host: This parameter specifies the ISCI host object
|
|
||||||
* @isci_device: This is the device to which the request is pending.
|
|
||||||
* @aborted_io_completion: This is a completion structure that will be added to
|
|
||||||
* the request in case it is changed to aborting; this completion is
|
|
||||||
* triggered when the request is fully completed.
|
|
||||||
*
|
|
||||||
* Either "started" on successful change of the task status to "aborted", or
|
|
||||||
* "unallocated" if the task cannot be controlled.
|
|
||||||
*/
|
|
||||||
static enum isci_request_status isci_task_validate_request_to_abort(
|
|
||||||
struct isci_request *isci_request,
|
|
||||||
struct isci_host *isci_host,
|
|
||||||
struct isci_remote_device *isci_device,
|
|
||||||
struct completion *aborted_io_completion)
|
|
||||||
{
|
|
||||||
enum isci_request_status old_state = unallocated;
|
|
||||||
|
|
||||||
/* Only abort the task if it's in the
|
|
||||||
* device's request_in_process list
|
|
||||||
*/
|
|
||||||
if (isci_request && !list_empty(&isci_request->dev_node)) {
|
|
||||||
old_state = isci_request_change_started_to_aborted(
|
|
||||||
isci_request, aborted_io_completion);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return old_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int isci_request_is_dealloc_managed(enum isci_request_status stat)
|
|
||||||
{
|
|
||||||
switch (stat) {
|
|
||||||
case aborted:
|
|
||||||
case aborting:
|
|
||||||
case terminating:
|
|
||||||
case completed:
|
|
||||||
case dead:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_terminate_request_core() - This function will terminate the given
|
|
||||||
* request, and wait for it to complete. This function must only be called
|
|
||||||
* from a thread that can wait. Note that the request is terminated and
|
|
||||||
* completed (back to the host, if started there).
|
|
||||||
* @ihost: This SCU.
|
|
||||||
* @idev: The target.
|
|
||||||
* @isci_request: The I/O request to be terminated.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void isci_terminate_request_core(struct isci_host *ihost,
|
|
||||||
struct isci_remote_device *idev,
|
|
||||||
struct isci_request *isci_request)
|
|
||||||
{
|
|
||||||
enum sci_status status = SCI_SUCCESS;
|
|
||||||
bool was_terminated = false;
|
|
||||||
bool needs_cleanup_handling = false;
|
|
||||||
unsigned long flags;
|
|
||||||
unsigned long termination_completed = 1;
|
|
||||||
struct completion *io_request_completion;
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: device = %p; request = %p\n",
|
|
||||||
__func__, idev, isci_request);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
||||||
|
|
||||||
io_request_completion = isci_request->io_request_completion;
|
|
||||||
|
|
||||||
/* Note that we are not going to control
|
|
||||||
* the target to abort the request.
|
|
||||||
*/
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &isci_request->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).
|
|
||||||
*/
|
|
||||||
if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) {
|
|
||||||
was_terminated = true;
|
|
||||||
needs_cleanup_handling = true;
|
|
||||||
status = sci_controller_terminate_request(ihost,
|
|
||||||
idev,
|
|
||||||
isci_request);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The only time the request to terminate will
|
|
||||||
* fail is when the io request is completed and
|
|
||||||
* being aborted.
|
|
||||||
*/
|
|
||||||
if (status != SCI_SUCCESS) {
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: sci_controller_terminate_request"
|
|
||||||
" returned = 0x%x\n",
|
|
||||||
__func__, status);
|
|
||||||
|
|
||||||
isci_request->io_request_completion = NULL;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (was_terminated) {
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: before completion wait (%p/%p)\n",
|
|
||||||
__func__, isci_request, io_request_completion);
|
|
||||||
|
|
||||||
/* Wait here for the request to complete. */
|
|
||||||
termination_completed
|
|
||||||
= wait_for_completion_timeout(
|
|
||||||
io_request_completion,
|
|
||||||
msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC));
|
|
||||||
|
|
||||||
if (!termination_completed) {
|
|
||||||
|
|
||||||
/* The request to terminate has timed out. */
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
||||||
|
|
||||||
/* Check for state changes. */
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
isci_request_mark_zombie(ihost,
|
|
||||||
isci_request);
|
|
||||||
needs_cleanup_handling = true;
|
|
||||||
} else
|
|
||||||
termination_completed = 1;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock,
|
|
||||||
flags);
|
|
||||||
|
|
||||||
if (!termination_completed) {
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: *** Timeout waiting for "
|
|
||||||
"termination(%p/%p)\n",
|
|
||||||
__func__, io_request_completion,
|
|
||||||
isci_request);
|
|
||||||
|
|
||||||
/* The request can no longer be referenced
|
|
||||||
* safely since it may go away if the
|
|
||||||
* termination every really does complete.
|
|
||||||
*/
|
|
||||||
isci_request = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (termination_completed)
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: after completion wait (%p/%p)\n",
|
|
||||||
__func__, isci_request, io_request_completion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (termination_completed) {
|
|
||||||
|
|
||||||
isci_request->io_request_completion = NULL;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
needs_cleanup_handling
|
|
||||||
= isci_request_is_dealloc_managed(
|
|
||||||
isci_request->status);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&isci_request->state_lock, flags);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (needs_cleanup_handling) {
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: cleanup isci_device=%p, request=%p\n",
|
|
||||||
__func__, idev, isci_request);
|
|
||||||
|
|
||||||
if (isci_request != NULL) {
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
||||||
isci_free_tag(ihost, isci_request->io_tag);
|
|
||||||
isci_request_change_state(isci_request, unallocated);
|
|
||||||
list_del_init(&isci_request->dev_node);
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_terminate_pending_requests() - This function will change the all of the
|
|
||||||
* requests on the given device's state to "aborting", will terminate the
|
|
||||||
* requests, and wait for them to complete. This function must only be
|
|
||||||
* called from a thread that can wait. Note that the requests are all
|
|
||||||
* terminated and completed (back to the host, if started there).
|
|
||||||
* @isci_host: This parameter specifies SCU.
|
|
||||||
* @idev: This parameter specifies the target.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void isci_terminate_pending_requests(struct isci_host *ihost,
|
|
||||||
struct isci_remote_device *idev)
|
|
||||||
{
|
|
||||||
struct completion request_completion;
|
|
||||||
enum isci_request_status old_state;
|
|
||||||
unsigned long flags;
|
|
||||||
LIST_HEAD(list);
|
|
||||||
|
|
||||||
isci_remote_device_suspend(ihost, idev);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
||||||
list_splice_init(&idev->reqs_in_process, &list);
|
|
||||||
|
|
||||||
/* assumes that isci_terminate_request_core deletes from the list */
|
|
||||||
while (!list_empty(&list)) {
|
|
||||||
struct isci_request *ireq = list_entry(list.next, typeof(*ireq), dev_node);
|
|
||||||
|
|
||||||
/* Change state to "terminating" if it is currently
|
|
||||||
* "started".
|
|
||||||
*/
|
|
||||||
old_state = isci_request_change_started_to_newstate(ireq,
|
|
||||||
&request_completion,
|
|
||||||
terminating);
|
|
||||||
switch (old_state) {
|
|
||||||
case started:
|
|
||||||
case completed:
|
|
||||||
case aborting:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* termination in progress, or otherwise dispositioned.
|
|
||||||
* We know the request was on 'list' so should be safe
|
|
||||||
* to move it back to reqs_in_process
|
|
||||||
*/
|
|
||||||
list_move(&ireq->dev_node, &idev->reqs_in_process);
|
|
||||||
ireq = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ireq)
|
|
||||||
continue;
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
||||||
|
|
||||||
init_completion(&request_completion);
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: idev=%p request=%p; task=%p old_state=%d\n",
|
|
||||||
__func__, idev, ireq,
|
|
||||||
(!test_bit(IREQ_TMF, &ireq->flags)
|
|
||||||
? isci_request_access_task(ireq)
|
|
||||||
: NULL),
|
|
||||||
old_state);
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
* "complete()" does not happen until the pending I/O request
|
|
||||||
* terminate fully completes, we do not have to implement a
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* If old_state == aborting:
|
|
||||||
* This request has already gone through a TMF timeout, but may
|
|
||||||
* not have been terminated; needs cleaning up at least.
|
|
||||||
*/
|
|
||||||
isci_terminate_request_core(ihost, idev, ireq);
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
|
* isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain
|
||||||
* Template functions.
|
* Template functions.
|
||||||
|
@ -809,7 +402,7 @@ static int isci_task_send_lu_reset_sas(
|
||||||
* value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
|
* value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or
|
||||||
* was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
|
* was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED").
|
||||||
*/
|
*/
|
||||||
isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset, NULL, NULL);
|
isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset);
|
||||||
|
|
||||||
#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
|
#define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */
|
||||||
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
|
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS);
|
||||||
|
@ -829,48 +422,41 @@ static int isci_task_send_lu_reset_sas(
|
||||||
int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
|
int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
|
||||||
{
|
{
|
||||||
struct isci_host *ihost = dev_to_ihost(dev);
|
struct isci_host *ihost = dev_to_ihost(dev);
|
||||||
struct isci_remote_device *isci_device;
|
struct isci_remote_device *idev;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
isci_device = isci_lookup_device(dev);
|
idev = isci_lookup_device(dev);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
|
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
|
||||||
__func__, dev, ihost, isci_device);
|
__func__, dev, ihost, idev);
|
||||||
|
|
||||||
if (!isci_device) {
|
if (!idev) {
|
||||||
/* If the device is gone, escalate to I_T_Nexus_Reset. */
|
/* If the device is gone, escalate to I_T_Nexus_Reset. */
|
||||||
dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
|
dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__);
|
||||||
|
|
||||||
ret = TMF_RESP_FUNC_FAILED;
|
ret = TMF_RESP_FUNC_FAILED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) {
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: device = %p; failed to suspend\n",
|
|
||||||
__func__, isci_device);
|
|
||||||
ret = TMF_RESP_FUNC_FAILED;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the task management part of the reset. */
|
|
||||||
if (dev_is_sata(dev)) {
|
if (dev_is_sata(dev)) {
|
||||||
sas_ata_schedule_reset(dev);
|
sas_ata_schedule_reset(dev);
|
||||||
ret = TMF_RESP_FUNC_COMPLETE;
|
ret = TMF_RESP_FUNC_COMPLETE;
|
||||||
} else
|
} else {
|
||||||
ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun);
|
/* Suspend the RNC, kill all TCs */
|
||||||
|
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
||||||
/* If the LUN reset worked, all the I/O can now be terminated. */
|
!= SCI_SUCCESS) {
|
||||||
if (ret == TMF_RESP_FUNC_COMPLETE) {
|
ret = TMF_RESP_FUNC_FAILED;
|
||||||
/* Terminate all I/O now. */
|
goto out;
|
||||||
isci_terminate_pending_requests(ihost, isci_device);
|
}
|
||||||
isci_remote_device_resume(ihost, isci_device, NULL, NULL);
|
/* Send the task management part of the reset. */
|
||||||
|
ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
isci_put_device(isci_device);
|
isci_put_device(idev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,63 +476,6 @@ int isci_task_clear_nexus_ha(struct sas_ha_struct *ha)
|
||||||
|
|
||||||
/* Task Management Functions. Must be called from process context. */
|
/* Task Management Functions. Must be called from process context. */
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_abort_task_process_cb() - This is a helper function for the abort task
|
|
||||||
* TMF command. It manages the request state with respect to the successful
|
|
||||||
* transmission / completion of the abort task request.
|
|
||||||
* @cb_state: This parameter specifies when this function was called - after
|
|
||||||
* the TMF request has been started and after it has timed-out.
|
|
||||||
* @tmf: This parameter specifies the TMF in progress.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void isci_abort_task_process_cb(
|
|
||||||
enum isci_tmf_cb_state cb_state,
|
|
||||||
struct isci_tmf *tmf,
|
|
||||||
void *cb_data)
|
|
||||||
{
|
|
||||||
struct isci_request *old_request;
|
|
||||||
|
|
||||||
old_request = (struct isci_request *)cb_data;
|
|
||||||
|
|
||||||
dev_dbg(&old_request->isci_host->pdev->dev,
|
|
||||||
"%s: tmf=%p, old_request=%p\n",
|
|
||||||
__func__, tmf, old_request);
|
|
||||||
|
|
||||||
switch (cb_state) {
|
|
||||||
|
|
||||||
case isci_tmf_started:
|
|
||||||
/* The TMF has been started. Nothing to do here, since the
|
|
||||||
* request state was already set to "aborted" by the abort
|
|
||||||
* task function.
|
|
||||||
*/
|
|
||||||
if ((old_request->status != aborted)
|
|
||||||
&& (old_request->status != completed))
|
|
||||||
dev_dbg(&old_request->isci_host->pdev->dev,
|
|
||||||
"%s: Bad request status (%d): tmf=%p, old_request=%p\n",
|
|
||||||
__func__, old_request->status, tmf, old_request);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case isci_tmf_timed_out:
|
|
||||||
|
|
||||||
/* Set the task's state to "aborting", since the abort task
|
|
||||||
* function thread set it to "aborted" (above) in anticipation
|
|
||||||
* of the task management request working correctly. Since the
|
|
||||||
* timeout has now fired, the TMF request failed. We set the
|
|
||||||
* state such that the request completion will indicate the
|
|
||||||
* device is no longer present.
|
|
||||||
*/
|
|
||||||
isci_request_change_state(old_request, aborting);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
dev_dbg(&old_request->isci_host->pdev->dev,
|
|
||||||
"%s: Bad cb_state (%d): tmf=%p, old_request=%p\n",
|
|
||||||
__func__, cb_state, tmf, old_request);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isci_task_abort_task() - This function is one of the SAS Domain Template
|
* isci_task_abort_task() - This function is one of the SAS Domain Template
|
||||||
* functions. This function is called by libsas to abort a specified task.
|
* functions. This function is called by libsas to abort a specified task.
|
||||||
|
@ -956,22 +485,20 @@ static void isci_abort_task_process_cb(
|
||||||
*/
|
*/
|
||||||
int isci_task_abort_task(struct sas_task *task)
|
int isci_task_abort_task(struct sas_task *task)
|
||||||
{
|
{
|
||||||
struct isci_host *isci_host = dev_to_ihost(task->dev);
|
struct isci_host *ihost = dev_to_ihost(task->dev);
|
||||||
DECLARE_COMPLETION_ONSTACK(aborted_io_completion);
|
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 *idev = NULL;
|
||||||
struct isci_remote_device *isci_device = NULL;
|
|
||||||
struct isci_tmf tmf;
|
struct isci_tmf tmf;
|
||||||
int ret = TMF_RESP_FUNC_FAILED;
|
int ret = TMF_RESP_FUNC_FAILED;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int perform_termination = 0;
|
|
||||||
|
|
||||||
/* Get the isci_request reference from the task. Note that
|
/* Get the isci_request reference from the task. Note that
|
||||||
* this check does not depend on the pending request list
|
* this check does not depend on the pending request list
|
||||||
* in the device, because tasks driving resets may land here
|
* in the device, because tasks driving resets may land here
|
||||||
* after completion in the core.
|
* after completion in the core.
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&isci_host->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
spin_lock(&task->task_state_lock);
|
spin_lock(&task->task_state_lock);
|
||||||
|
|
||||||
old_request = task->lldd_task;
|
old_request = task->lldd_task;
|
||||||
|
@ -980,20 +507,20 @@ int isci_task_abort_task(struct sas_task *task)
|
||||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
|
if (!(task->task_state_flags & SAS_TASK_STATE_DONE) &&
|
||||||
(task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
|
(task->task_state_flags & SAS_TASK_AT_INITIATOR) &&
|
||||||
old_request)
|
old_request)
|
||||||
isci_device = isci_lookup_device(task->dev);
|
idev = isci_lookup_device(task->dev);
|
||||||
|
|
||||||
spin_unlock(&task->task_state_lock);
|
spin_unlock(&task->task_state_lock);
|
||||||
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
dev_warn(&ihost->pdev->dev,
|
||||||
"%s: dev = %p, task = %p, old_request == %p\n",
|
"%s: dev = %p, task = %p, old_request == %p\n",
|
||||||
__func__, isci_device, task, old_request);
|
__func__, idev, task, old_request);
|
||||||
|
|
||||||
/* Device reset conditions signalled in task_state_flags are the
|
/* Device reset conditions signalled in task_state_flags are the
|
||||||
* responsbility of libsas to observe at the start of the error
|
* responsbility of libsas to observe at the start of the error
|
||||||
* handler thread.
|
* handler thread.
|
||||||
*/
|
*/
|
||||||
if (!isci_device || !old_request) {
|
if (!idev || !old_request) {
|
||||||
/* The request has already completed and there
|
/* The request has already completed and there
|
||||||
* is nothing to do here other than to set the task
|
* is nothing to do here other than to set the task
|
||||||
* done bit, and indicate that the task abort function
|
* done bit, and indicate that the task abort function
|
||||||
|
@ -1007,44 +534,33 @@ int isci_task_abort_task(struct sas_task *task)
|
||||||
|
|
||||||
ret = TMF_RESP_FUNC_COMPLETE;
|
ret = TMF_RESP_FUNC_COMPLETE;
|
||||||
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
dev_warn(&ihost->pdev->dev,
|
||||||
"%s: abort task not needed for %p\n",
|
"%s: abort task not needed for %p\n",
|
||||||
__func__, task);
|
__func__, task);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
/* Suspend the RNC, kill the TC */
|
||||||
spin_lock_irqsave(&isci_host->scic_lock, flags);
|
if (isci_remote_device_suspend_terminate(ihost, idev, old_request)
|
||||||
|
!= SCI_SUCCESS) {
|
||||||
/* Check the request status and change to "aborted" if currently
|
dev_warn(&ihost->pdev->dev,
|
||||||
* "starting"; if true then set the I/O kernel completion
|
"%s: isci_remote_device_reset_terminate(dev=%p, "
|
||||||
* struct that will be triggered when the request completes.
|
"req=%p, task=%p) failed\n",
|
||||||
*/
|
__func__, idev, old_request, task);
|
||||||
old_state = isci_task_validate_request_to_abort(
|
ret = TMF_RESP_FUNC_FAILED;
|
||||||
old_request, isci_host, isci_device,
|
|
||||||
&aborted_io_completion);
|
|
||||||
if ((old_state != started) &&
|
|
||||||
(old_state != completed) &&
|
|
||||||
(old_state != aborting)) {
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
|
|
||||||
|
|
||||||
/* The request was already being handled by someone else (because
|
|
||||||
* they got to set the state away from started).
|
|
||||||
*/
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
|
||||||
"%s: device = %p; old_request %p already being aborted\n",
|
|
||||||
__func__,
|
|
||||||
isci_device, old_request);
|
|
||||||
ret = TMF_RESP_FUNC_COMPLETE;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
if (task->task_proto == SAS_PROTOCOL_SMP ||
|
if (task->task_proto == SAS_PROTOCOL_SMP ||
|
||||||
sas_protocol_ata(task->task_proto) ||
|
sas_protocol_ata(task->task_proto) ||
|
||||||
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
|
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
|
||||||
|
|
||||||
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
|
/* No task to send, so explicitly resume the device here */
|
||||||
|
sci_remote_device_resume(idev, NULL, NULL);
|
||||||
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
|
dev_warn(&ihost->pdev->dev,
|
||||||
"%s: %s request"
|
"%s: %s request"
|
||||||
" or complete_in_target (%d), thus no TMF\n",
|
" or complete_in_target (%d), thus no TMF\n",
|
||||||
__func__,
|
__func__,
|
||||||
|
@ -1054,79 +570,30 @@ int isci_task_abort_task(struct sas_task *task)
|
||||||
? "SATA/STP"
|
? "SATA/STP"
|
||||||
: "<other>")
|
: "<other>")
|
||||||
),
|
),
|
||||||
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags));
|
test_bit(IREQ_COMPLETE_IN_TARGET,
|
||||||
|
&old_request->flags));
|
||||||
|
|
||||||
if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
|
|
||||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||||
|
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
|
||||||
|
SAS_TASK_STATE_PENDING);
|
||||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
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);
|
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||||
|
|
||||||
ret = TMF_RESP_FUNC_COMPLETE;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
if (isci_device && !test_bit(IDEV_GONE, &isci_device->flags) &&
|
|
||||||
(isci_remote_device_suspend(isci_host, isci_device)
|
|
||||||
!= SCI_SUCCESS)) {
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
|
||||||
"%s: device = %p; failed to suspend\n",
|
|
||||||
__func__, isci_device);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Fill in the tmf stucture */
|
/* Fill in the tmf stucture */
|
||||||
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
|
isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort,
|
||||||
isci_abort_task_process_cb,
|
|
||||||
old_request);
|
old_request);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
if (isci_remote_device_suspend(isci_host, isci_device)
|
|
||||||
!= SCI_SUCCESS) {
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
|
||||||
"%s: device = %p; failed to suspend\n",
|
|
||||||
__func__, isci_device);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Send the task management request. */
|
||||||
#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
|
#define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */
|
||||||
ret = isci_task_execute_tmf(isci_host, isci_device, &tmf,
|
ret = isci_task_execute_tmf(ihost, idev, &tmf,
|
||||||
ISCI_ABORT_TASK_TIMEOUT_MS);
|
ISCI_ABORT_TASK_TIMEOUT_MS);
|
||||||
|
|
||||||
if (ret == TMF_RESP_FUNC_COMPLETE)
|
|
||||||
perform_termination = 1;
|
|
||||||
else
|
|
||||||
dev_warn(&isci_host->pdev->dev,
|
|
||||||
"%s: isci_task_send_tmf failed\n", __func__);
|
|
||||||
}
|
}
|
||||||
if (perform_termination) {
|
out:
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags);
|
isci_put_device(idev);
|
||||||
|
|
||||||
/* 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_remote_device_resume(isci_host, isci_device, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure we do not leave a reference to aborted_io_completion */
|
|
||||||
old_request->io_request_completion = NULL;
|
|
||||||
out:
|
|
||||||
isci_put_device(isci_device);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1222,14 +689,11 @@ isci_task_request_complete(struct isci_host *ihost,
|
||||||
{
|
{
|
||||||
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
|
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
|
||||||
struct completion *tmf_complete = NULL;
|
struct completion *tmf_complete = NULL;
|
||||||
struct completion *request_complete = ireq->io_request_completion;
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
dev_dbg(&ihost->pdev->dev,
|
||||||
"%s: request = %p, status=%d\n",
|
"%s: request = %p, status=%d\n",
|
||||||
__func__, ireq, completion_status);
|
__func__, ireq, completion_status);
|
||||||
|
|
||||||
isci_request_change_state(ireq, completed);
|
|
||||||
|
|
||||||
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
|
set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags);
|
||||||
|
|
||||||
if (tmf) {
|
if (tmf) {
|
||||||
|
@ -1253,20 +717,8 @@ isci_task_request_complete(struct isci_host *ihost,
|
||||||
*/
|
*/
|
||||||
set_bit(IREQ_TERMINATED, &ireq->flags);
|
set_bit(IREQ_TERMINATED, &ireq->flags);
|
||||||
|
|
||||||
/* As soon as something is in the terminate path, deallocation is
|
|
||||||
* managed there. Note that the final non-managed state of a task
|
|
||||||
* request is "completed".
|
|
||||||
*/
|
|
||||||
if ((ireq->status == completed) ||
|
|
||||||
!isci_request_is_dealloc_managed(ireq->status)) {
|
|
||||||
isci_request_change_state(ireq, unallocated);
|
|
||||||
isci_free_tag(ihost, ireq->io_tag);
|
isci_free_tag(ihost, ireq->io_tag);
|
||||||
list_del_init(&ireq->dev_node);
|
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. */
|
/* The task management part completes last. */
|
||||||
if (tmf_complete)
|
if (tmf_complete)
|
||||||
|
@ -1277,37 +729,37 @@ static int isci_reset_device(struct isci_host *ihost,
|
||||||
struct domain_device *dev,
|
struct domain_device *dev,
|
||||||
struct isci_remote_device *idev)
|
struct isci_remote_device *idev)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc = TMF_RESP_FUNC_COMPLETE, reset_stat;
|
||||||
enum sci_status status;
|
|
||||||
struct sas_phy *phy = sas_get_local_phy(dev);
|
struct sas_phy *phy = sas_get_local_phy(dev);
|
||||||
struct isci_port *iport = dev->port->lldd_port;
|
struct isci_port *iport = dev->port->lldd_port;
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
|
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
|
||||||
|
|
||||||
if (isci_remote_device_reset(ihost, idev) != SCI_SUCCESS) {
|
/* Suspend the RNC, terminate all outstanding TCs. */
|
||||||
|
if (isci_remote_device_suspend_terminate(ihost, idev, NULL)
|
||||||
|
!= SCI_SUCCESS) {
|
||||||
rc = TMF_RESP_FUNC_FAILED;
|
rc = TMF_RESP_FUNC_FAILED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
/* Note that since the termination for outstanding requests succeeded,
|
||||||
|
* this function will return success. This is because the resets will
|
||||||
|
* only fail if the device has been removed (ie. hotplug), and the
|
||||||
|
* primary duty of this function is to cleanup tasks, so that is the
|
||||||
|
* relevant status.
|
||||||
|
*/
|
||||||
|
|
||||||
if (scsi_is_sas_phy_local(phy)) {
|
if (scsi_is_sas_phy_local(phy)) {
|
||||||
struct isci_phy *iphy = &ihost->phys[phy->number];
|
struct isci_phy *iphy = &ihost->phys[phy->number];
|
||||||
|
|
||||||
rc = isci_port_perform_hard_reset(ihost, iport, iphy);
|
reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy);
|
||||||
} else
|
} else
|
||||||
rc = sas_phy_reset(phy, !dev_is_sata(dev));
|
reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
|
||||||
|
|
||||||
/* Terminate in-progress I/O now. */
|
/* Explicitly resume the RNC here, since there was no task sent. */
|
||||||
isci_remote_device_nuke_requests(ihost, idev);
|
isci_remote_device_resume(ihost, idev, NULL, NULL);
|
||||||
|
|
||||||
/* Since all pending TCs have been cleaned, resume the RNC. */
|
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n",
|
||||||
status = isci_remote_device_reset_complete(ihost, idev);
|
__func__, idev, reset_stat);
|
||||||
|
|
||||||
if (status != SCI_SUCCESS)
|
|
||||||
dev_dbg(&ihost->pdev->dev,
|
|
||||||
"%s: isci_remote_device_reset_complete(%p) "
|
|
||||||
"returned %d!\n", __func__, idev, status);
|
|
||||||
|
|
||||||
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev);
|
|
||||||
out:
|
out:
|
||||||
sas_put_local_phy(phy);
|
sas_put_local_phy(phy);
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1321,7 +773,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
idev = isci_get_device(dev);
|
idev = isci_get_device(dev->lldd_dev);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
if (!idev) {
|
if (!idev) {
|
||||||
|
|
|
@ -62,19 +62,6 @@
|
||||||
|
|
||||||
struct isci_request;
|
struct isci_request;
|
||||||
|
|
||||||
/**
|
|
||||||
* enum isci_tmf_cb_state - This enum defines the possible states in which the
|
|
||||||
* TMF callback function is invoked during the TMF execution process.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum isci_tmf_cb_state {
|
|
||||||
|
|
||||||
isci_tmf_init_state = 0,
|
|
||||||
isci_tmf_started,
|
|
||||||
isci_tmf_timed_out
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum isci_tmf_function_codes - This enum defines the possible preparations
|
* enum isci_tmf_function_codes - This enum defines the possible preparations
|
||||||
* of task management requests.
|
* of task management requests.
|
||||||
|
@ -87,6 +74,7 @@ enum isci_tmf_function_codes {
|
||||||
isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
|
isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
|
||||||
isci_tmf_ssp_lun_reset = TMF_LU_RESET,
|
isci_tmf_ssp_lun_reset = TMF_LU_RESET,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct isci_tmf - This class represents the task management object which
|
* struct isci_tmf - This class represents the task management object which
|
||||||
* acts as an interface to libsas for processing task management requests
|
* acts as an interface to libsas for processing task management requests
|
||||||
|
@ -106,15 +94,6 @@ struct isci_tmf {
|
||||||
u16 io_tag;
|
u16 io_tag;
|
||||||
enum isci_tmf_function_codes tmf_code;
|
enum isci_tmf_function_codes tmf_code;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
/* The optional callback function allows the user process to
|
|
||||||
* track the TMF transmit / timeout conditions.
|
|
||||||
*/
|
|
||||||
void (*cb_state_func)(
|
|
||||||
enum isci_tmf_cb_state,
|
|
||||||
struct isci_tmf *, void *);
|
|
||||||
void *cb_data;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf)
|
static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf)
|
||||||
|
@ -208,113 +187,4 @@ int isci_queuecommand(
|
||||||
struct scsi_cmnd *scsi_cmd,
|
struct scsi_cmnd *scsi_cmd,
|
||||||
void (*donefunc)(struct scsi_cmnd *));
|
void (*donefunc)(struct scsi_cmnd *));
|
||||||
|
|
||||||
/**
|
|
||||||
* enum isci_completion_selection - This enum defines the possible actions to
|
|
||||||
* take with respect to a given request's notification back to libsas.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
enum isci_completion_selection {
|
|
||||||
|
|
||||||
isci_perform_normal_io_completion, /* Normal notify (task_done) */
|
|
||||||
isci_perform_aborted_io_completion, /* No notification. */
|
|
||||||
isci_perform_error_io_completion /* Use sas_task_abort */
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isci_task_set_completion_status() - This function sets the completion status
|
|
||||||
* for the request.
|
|
||||||
* @task: This parameter is the completed request.
|
|
||||||
* @response: This parameter is the response code for the completed task.
|
|
||||||
* @status: This parameter is the status code for the completed task.
|
|
||||||
*
|
|
||||||
* @return The new notification mode for the request.
|
|
||||||
*/
|
|
||||||
static inline enum isci_completion_selection
|
|
||||||
isci_task_set_completion_status(
|
|
||||||
struct sas_task *task,
|
|
||||||
enum service_response response,
|
|
||||||
enum exec_status status,
|
|
||||||
enum isci_completion_selection task_notification_selection)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
||||||
|
|
||||||
/* If a device reset is being indicated, make sure the I/O
|
|
||||||
* is in the error path.
|
|
||||||
*/
|
|
||||||
if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) {
|
|
||||||
/* Fail the I/O to make sure it goes into the error path. */
|
|
||||||
response = SAS_TASK_UNDELIVERED;
|
|
||||||
status = SAM_STAT_TASK_ABORTED;
|
|
||||||
|
|
||||||
task_notification_selection = isci_perform_error_io_completion;
|
|
||||||
}
|
|
||||||
task->task_status.resp = response;
|
|
||||||
task->task_status.stat = status;
|
|
||||||
|
|
||||||
switch (task->task_proto) {
|
|
||||||
|
|
||||||
case SAS_PROTOCOL_SATA:
|
|
||||||
case SAS_PROTOCOL_STP:
|
|
||||||
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
|
|
||||||
|
|
||||||
if (task_notification_selection
|
|
||||||
== isci_perform_error_io_completion) {
|
|
||||||
/* SATA/STP I/O has it's own means of scheduling device
|
|
||||||
* error handling on the normal path.
|
|
||||||
*/
|
|
||||||
task_notification_selection
|
|
||||||
= isci_perform_normal_io_completion;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (task_notification_selection) {
|
|
||||||
|
|
||||||
case isci_perform_error_io_completion:
|
|
||||||
|
|
||||||
if (task->task_proto == SAS_PROTOCOL_SMP) {
|
|
||||||
/* There is no error escalation in the SMP case.
|
|
||||||
* Convert to a normal completion to avoid the
|
|
||||||
* timeout in the discovery path and to let the
|
|
||||||
* next action take place quickly.
|
|
||||||
*/
|
|
||||||
task_notification_selection
|
|
||||||
= isci_perform_normal_io_completion;
|
|
||||||
|
|
||||||
/* Fall through to the normal case... */
|
|
||||||
} else {
|
|
||||||
/* Use sas_task_abort */
|
|
||||||
/* Leave SAS_TASK_STATE_DONE clear
|
|
||||||
* Leave SAS_TASK_AT_INITIATOR set.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case isci_perform_aborted_io_completion:
|
|
||||||
/* This path can occur with task-managed requests as well as
|
|
||||||
* requests terminated because of LUN or device resets.
|
|
||||||
*/
|
|
||||||
/* Fall through to the normal case... */
|
|
||||||
case isci_perform_normal_io_completion:
|
|
||||||
/* Normal notification (task_done) */
|
|
||||||
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",
|
|
||||||
task_notification_selection);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
||||||
|
|
||||||
return task_notification_selection;
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif /* !defined(_SCI_TASK_H_) */
|
#endif /* !defined(_SCI_TASK_H_) */
|
||||||
|
|
Reference in a new issue