From 5b6bf225e7fc249c703e19bf2c983d1a59178874 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:51 -0800 Subject: isci: Manage device suspensions during TC terminations. TCs must be terminated only while the RNC is suspended. This commit adds remote device suspensions and resumptions in the abort, reset and termination paths. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 80 +++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 32 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 374254ede9d4..9b8632f33dd9 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -716,6 +716,8 @@ void isci_terminate_pending_requests(struct isci_host *ihost, 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); @@ -826,40 +828,47 @@ static int isci_task_send_lu_reset_sas( int isci_task_lu_reset(struct domain_device *dev, u8 *lun) { - struct isci_host *isci_host = dev_to_ihost(dev); + struct isci_host *ihost = dev_to_ihost(dev); struct isci_remote_device *isci_device; unsigned long flags; int ret; - spin_lock_irqsave(&isci_host->scic_lock, flags); + spin_lock_irqsave(&ihost->scic_lock, flags); isci_device = isci_lookup_device(dev); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_dbg(&ihost->pdev->dev, "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", - __func__, dev, isci_host, isci_device); + __func__, dev, ihost, isci_device); if (!isci_device) { /* If the device is gone, stop the escalations. */ - dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__); + dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); ret = TMF_RESP_FUNC_COMPLETE; 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)) { sas_ata_schedule_reset(dev); ret = TMF_RESP_FUNC_COMPLETE; } else - ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun); + ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun); /* If the LUN reset worked, all the I/O can now be terminated. */ - if (ret == TMF_RESP_FUNC_COMPLETE) + if (ret == TMF_RESP_FUNC_COMPLETE) { /* Terminate all I/O now. */ - isci_terminate_pending_requests(isci_host, - isci_device); - + isci_terminate_pending_requests(ihost, isci_device); + isci_remote_device_resume(ihost, isci_device, NULL, NULL); + } out: isci_put_device(isci_device); return ret; @@ -976,7 +985,7 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock(&task->task_state_lock); spin_unlock_irqrestore(&isci_host->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: dev = %p, task = %p, old_request == %p\n", __func__, isci_device, task, old_request); @@ -998,7 +1007,7 @@ int isci_task_abort_task(struct sas_task *task) ret = TMF_RESP_FUNC_COMPLETE; - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: abort task not needed for %p\n", __func__, task); goto out; @@ -1022,7 +1031,7 @@ int isci_task_abort_task(struct sas_task *task) /* The request was already being handled by someone else (because * they got to set the state away from started). */ - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: device = %p; old_request %p already being aborted\n", __func__, isci_device, old_request); @@ -1035,7 +1044,7 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: %s request" " or complete_in_target (%d), thus no TMF\n", __func__, @@ -1068,6 +1077,15 @@ int isci_task_abort_task(struct sas_task *task) */ 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 { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, @@ -1076,6 +1094,14 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->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; + } + #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); @@ -1083,7 +1109,7 @@ int isci_task_abort_task(struct sas_task *task) if (ret == TMF_RESP_FUNC_COMPLETE) perform_termination = 1; else - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: isci_task_send_tmf failed\n", __func__); } if (perform_termination) { @@ -1094,6 +1120,7 @@ int isci_task_abort_task(struct sas_task *task) */ 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 */ @@ -1251,21 +1278,13 @@ static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { int rc; - unsigned long flags; enum sci_status status; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset(%p) returned %d!\n", - __func__, idev, status); + if (isci_remote_device_reset(ihost, idev) != SCI_SUCCESS) { rc = TMF_RESP_FUNC_FAILED; goto out; } @@ -1281,15 +1300,12 @@ static int isci_reset_device(struct isci_host *ihost, isci_remote_device_nuke_requests(ihost, idev); /* Since all pending TCs have been cleaned, resume the RNC. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset_complete(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + status = isci_remote_device_reset_complete(ihost, idev); - if (status != SCI_SUCCESS) { + if (status != SCI_SUCCESS) dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset_complete(%p) " + "%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: @@ -1305,7 +1321,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(dev); + idev = isci_get_device(dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!idev) { -- cgit v1.2.3 From d80ecd5726ce49b5da457d562804b40f0183e8f7 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:53 -0800 Subject: isci: Escalate to I_T_Nexus_Reset when the device is gone. If LUN reset sees that the device is gone, it returns TMF_RESP_FUNC_FAILED to cause libsas to escalate to an I_T_Nexus_Reset. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 9b8632f33dd9..26de06ef688e 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -842,10 +842,10 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) __func__, dev, ihost, isci_device); if (!isci_device) { - /* If the device is gone, stop the escalations. */ + /* If the device is gone, escalate to I_T_Nexus_Reset. */ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); - ret = TMF_RESP_FUNC_COMPLETE; + ret = TMF_RESP_FUNC_FAILED; goto out; } if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) { -- cgit v1.2.3 From 14aaa9f0a318bd04cbb9d822524b817e95d8b343 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:54 -0800 Subject: 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 Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 86 ++-- drivers/scsi/isci/host.h | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/isci/remote_device.c | 255 +++++------ drivers/scsi/isci/remote_device.h | 15 +- drivers/scsi/isci/remote_node_context.c | 23 +- drivers/scsi/isci/remote_node_context.h | 4 +- drivers/scsi/isci/request.c | 509 ++++++--------------- drivers/scsi/isci/request.h | 108 +---- drivers/scsi/isci/task.c | 758 +++++--------------------------- drivers/scsi/isci/task.h | 132 +----- 11 files changed, 418 insertions(+), 1474 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d241b5722eb3..25d537e2f5c4 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1089,33 +1089,25 @@ void isci_host_completion_routine(unsigned long data) { struct isci_host *ihost = (struct isci_host *)data; struct list_head completed_request_list; - struct list_head errored_request_list; struct list_head *current_position; struct list_head *next_position; struct isci_request *request; - struct isci_request *next_request; struct sas_task *task; u16 active; INIT_LIST_HEAD(&completed_request_list); - INIT_LIST_HEAD(&errored_request_list); spin_lock_irq(&ihost->scic_lock); sci_controller_completion_handler(ihost); /* Take the lists of completed I/Os from the host. */ - list_splice_init(&ihost->requests_to_complete, &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); - /* Process any completions in the lists. */ + /* Process any completions in the list. */ list_for_each_safe(current_position, next_position, &completed_request_list) { @@ -1123,23 +1115,30 @@ void isci_host_completion_routine(unsigned long data) completed_node); 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 */ if (task != NULL) { task->lldd_task = NULL; - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - - /* If the task is already in the abort path, - * the task_done callback cannot be called. - */ - task->task_done(task); + 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); + + task->task_done(task); + } else { + dev_warn(&ihost->pdev->dev, + "%s: Error - request/task" + " = %p/%p\n", + __func__, request, task); + + sas_task_abort(task); + } } } @@ -1147,44 +1146,6 @@ void isci_host_completion_routine(unsigned long data) 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 { - /* This is a case where the request has completed with a - * status such that it needed further target servicing, - * but the sas_task reference has already been removed - * from the request. Since it was errored, it was not - * being aborted, so there is nothing to do except free - * it. - */ - - 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); - spin_unlock_irq(&ihost->scic_lock); - } - } /* the coalesence timeout doubles at each encoding step, so * 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->owning_controller = ihost; - spin_lock_init(&ireq->state_lock); ireq->request_daddr = dma; ireq->isci_host = ihost; ihost->reqs[i] = ireq; @@ -2697,6 +2657,10 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, return SCI_FAILURE_INVALID_STATE; } 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) && !test_bit(IREQ_PENDING_ABORT, &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); clear_bit(IREQ_ACTIVE, &ireq->flags); + if (test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); return SCI_SUCCESS; default: dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 7272a0a375f2..eaa13c0be09a 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -205,7 +205,6 @@ struct isci_host { wait_queue_head_t eventq; struct tasklet_struct completion_tasklet; struct list_head requests_to_complete; - struct list_head requests_to_errorback; spinlock_t scic_lock; struct isci_request *reqs[SCI_MAX_IO_REQUESTS]; struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES]; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 9e1c83e425ed..39f12703b893 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -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_errorback); for (i = 0; i < SCI_MAX_PORTS; i++) { struct isci_port *iport = &ihost->ports[i]; diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 3048e02aeb7b..c47304cea45d 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -80,49 +80,6 @@ static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev 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 * 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); } -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) +{ + dev_dbg(&ihost->pdev->dev, + "%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); +} + +static enum sci_status sci_remote_device_terminate_reqs_checkabort( + struct isci_remote_device *idev, + int chk) { - return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) - || !test_bit(IDEV_ALLOCATED, &idev->flags); + 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_suspend( +enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, - struct isci_remote_device *idev) + struct isci_remote_device *idev, + struct isci_request *ireq) { - enum sci_status status; + enum sci_status status = SCI_SUCCESS; unsigned long 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); status = SCI_FAILURE; } else { - status = sci_remote_device_suspend(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - if (status == SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%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) - ? "" : ""); + 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); + wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE, + &ireq->flags)); - } else - dev_dbg(scirdev_to_dev(idev), - "%s: sci_remote_device_suspend failed, " - "status = %d\n", __func__, status); + } else { + /* Terminate all TCs. */ + sci_remote_device_terminate_requests(idev); + 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); } 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. * 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); } -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( 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, @@ -771,10 +774,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, if (status != SCI_SUCCESS) return status; - status = sci_remote_node_context_start_task(&idev->rnc, ireq); - if (status != SCI_SUCCESS) - goto out; - status = sci_request_start(ireq); if (status != SCI_SUCCESS) goto out; @@ -796,8 +795,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, sci_remote_node_context_suspend( &idev->rnc, SCI_SOFTWARE_SUSPENSION, 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: 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) 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) break; @@ -1322,20 +1324,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport, 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 * is received. @@ -1495,32 +1483,28 @@ int isci_remote_device_found(struct domain_device *dev) 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_remote_device *idev) + struct isci_remote_device *idev, + struct isci_request *ireq) { unsigned long flags; enum sci_status status; - /* Put the device into a reset state so the suspension will not - * automatically resume. - */ + /* Put the device into suspension. */ 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); - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset(%p) returned %d!\n", - __func__, idev, status); - return status; - } - /* Wait for the device suspend. */ - status = isci_remote_device_suspend(ihost, idev); - 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, - "%s: isci_remote_device_suspend(%p) returned %d!\n", + "%s: isci_remote_device_terminate_requests(%p) " + "returned %d!\n", __func__, idev, status); - } + + /* NOTE: RNC resumption is left to the caller! */ return status; } @@ -1533,7 +1517,7 @@ int isci_remote_device_is_safe_to_abort( enum sci_status sci_remote_device_abort_requests_pending_abort( 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( @@ -1545,7 +1529,6 @@ enum sci_status isci_remote_device_reset_complete( spin_lock_irqsave(&ihost->scic_lock, flags); status = sci_remote_device_reset_complete(idev); - sci_remote_device_resume(idev, NULL, NULL); spin_unlock_irqrestore(&ihost->scic_lock, flags); return status; diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index a6a376e200ef..da43698e9eba 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -85,7 +85,6 @@ struct isci_remote_device { #define IDEV_GONE 3 #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 - #define IDEV_TXRX_SUSPENDED 6 unsigned long flags; struct kref kref; struct isci_port *isci_port; @@ -107,10 +106,8 @@ struct isci_remote_device { /* device reference routines must be called under sci_lock */ 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) kref_get(&idev->kref); return idev; @@ -378,4 +375,14 @@ enum sci_status isci_remote_device_reset( enum sci_status isci_remote_device_reset_complete( struct isci_host *ihost, 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_) */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 7a8347e51767..feeca17f0f13 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -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_host *ihost = idev->owning_port->owning_controller; - set_bit(IDEV_TXRX_SUSPENDED, &idev->flags); - /* Terminate outstanding requests pending abort. */ 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); } -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( 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] = { .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] = { .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, - struct isci_request *ireq) +enum sci_status sci_remote_node_context_start_task( + 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; @@ -684,7 +673,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex return SCI_SUCCESS; case SCI_RNC_TX_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; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 5ddf88b53133..2870af14edab 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -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, void *cb_p); 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, struct isci_request *ireq); int sci_remote_node_context_is_safe_to_abort( diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 1f314d0d71d5..f4e80f31423c 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2491,9 +2491,6 @@ static void isci_request_process_response_iu( * @request: This parameter is the completed isci_request object. * @response_ptr: This parameter specifies the service response 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 * abandon-class reject. * @@ -2504,14 +2501,12 @@ static void isci_request_set_open_reject_status( struct sas_task *task, enum service_response *response_ptr, enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr, enum sas_open_rej_reason open_rej_reason) { /* Task in the target is done. */ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); *response_ptr = SAS_TASK_UNDELIVERED; *status_ptr = SAS_OPEN_REJECT; - *complete_to_host_ptr = isci_perform_normal_io_completion; 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. * @response_ptr: This parameter specifies the service response 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. */ @@ -2532,8 +2524,7 @@ static void isci_request_handle_controller_specific_errors( struct isci_request *request, struct sas_task *task, enum service_response *response_ptr, - enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr) + enum exec_status *status_ptr) { unsigned int cstatus; @@ -2574,9 +2565,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_normal_io_completion; } else { /* Task in the target is not done. */ *response_ptr = SAS_TASK_UNDELIVERED; @@ -2587,9 +2575,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAM_STAT_TASK_ABORTED; clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_error_io_completion; } break; @@ -2618,8 +2603,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_normal_io_completion; break; @@ -2630,7 +2613,7 @@ static void isci_request_handle_controller_specific_errors( isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_WRONG_DEST); + SAS_OREJ_WRONG_DEST); break; 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( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB0); + SAS_OREJ_RESV_AB0); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB1); + SAS_OREJ_RESV_AB1); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB2); + SAS_OREJ_RESV_AB2); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB3); + SAS_OREJ_RESV_AB3); break; case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_BAD_DEST); + SAS_OREJ_BAD_DEST); break; case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_STP_NORES); + SAS_OREJ_STP_NORES); break; case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_EPROTO); + SAS_OREJ_EPROTO); break; case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_CONN_RATE); + SAS_OREJ_CONN_RATE); break; case SCU_TASK_DONE_LL_R_ERR: @@ -2721,95 +2704,12 @@ static void isci_request_handle_controller_specific_errors( *response_ptr = SAS_TASK_UNDELIVERED; *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); - - *complete_to_host_ptr = isci_perform_normal_io_completion; - } else { + else clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_error_io_completion; - } - 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) @@ -2844,9 +2744,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, struct isci_remote_device *idev = request->target_device; enum service_response response = SAS_TASK_UNDELIVERED; 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, "%s: request = %p, task = %p,\n" @@ -2857,282 +2754,158 @@ static void isci_request_io_request_complete(struct isci_host *ihost, task->data_dir, 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; + /* The request is done from an SCU HW perspective. */ - /* 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; + /* This is an active request being completed from the core. */ + switch (completion_status) { - complete_to_host = isci_perform_aborted_io_completion; - /* This was an aborted request. */ + case SCI_IO_FAILURE_RESPONSE_VALID: + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", + __func__, request, task); - spin_unlock(&request->state_lock); - break; + if (sas_protocol_ata(task->task_proto)) { + isci_process_stp_response(task, &request->stp.rsp); + } else if (SAS_PROTOCOL_SSP == task->task_proto) { - 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; + /* crack the iu response buffer. */ + resp_iu = &request->ssp.rsp; + isci_request_process_response_iu(task, resp_iu, + &ihost->pdev->dev); - 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; + } else if (SAS_PROTOCOL_SMP == task->task_proto) { - complete_to_host = isci_perform_aborted_io_completion; + dev_err(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID: " + "SAS_PROTOCOL_SMP protocol\n", + __func__); - /* This was an aborted request. */ + } else + dev_err(&ihost->pdev->dev, + "%s: unknown protocol\n", __func__); - spin_unlock(&request->state_lock); + /* use the task status set in the task struct by the + * isci_request_process_response_iu call. + */ + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + response = task->task_status.resp; + status = task->task_status.stat; break; - case terminating: + case SCI_IO_SUCCESS: + case SCI_IO_SUCCESS_IO_DONE_EARLY: - /* 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. - */ + response = SAS_TASK_COMPLETE; + status = SAM_STAT_GOOD; 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; + if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { - 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. */ - request->status = completed; + /* This was an SSP / STP / SATA transfer. + * There is a possibility that less data than + * the maximum was transferred. + */ + u32 transferred_length = sci_req_tx_bytes(request); - spin_unlock(&request->state_lock); + task->task_status.residual + = task->total_xfer_len - transferred_length; - /* This is an active request being completed from the core. */ - switch (completion_status) { + /* If there were residual bytes, call this an + * underrun. + */ + if (task->task_status.residual != 0) + status = SAS_DATA_UNDERRUN; - case SCI_IO_FAILURE_RESPONSE_VALID: dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", - __func__, - request, - task); - - if (sas_protocol_ata(task->task_proto)) { - isci_process_stp_response(task, &request->stp.rsp); - } else if (SAS_PROTOCOL_SSP == task->task_proto) { - - /* crack the iu response buffer. */ - resp_iu = &request->ssp.rsp; - isci_request_process_response_iu(task, resp_iu, - &ihost->pdev->dev); - - } else if (SAS_PROTOCOL_SMP == task->task_proto) { + "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", + __func__, status); - dev_err(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID: " - "SAS_PROTOCOL_SMP protocol\n", - __func__); - - } else - dev_err(&ihost->pdev->dev, - "%s: unknown protocol\n", __func__); - - /* use the task status set in the task struct by the - * isci_request_process_response_iu call. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = task->task_status.resp; - status = task->task_status.stat; - break; - - case SCI_IO_SUCCESS: - case SCI_IO_SUCCESS_IO_DONE_EARLY: - - response = SAS_TASK_COMPLETE; - status = SAM_STAT_GOOD; - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { - - /* This was an SSP / STP / SATA transfer. - * There is a possibility that less data than - * the maximum was transferred. - */ - u32 transferred_length = sci_req_tx_bytes(request); - - task->task_status.residual - = task->total_xfer_len - transferred_length; + } else + dev_dbg(&ihost->pdev->dev, "%s: SCI_IO_SUCCESS\n", + __func__); + break; - /* If there were residual bytes, call this an - * underrun. - */ - if (task->task_status.residual != 0) - status = SAS_DATA_UNDERRUN; + case SCI_IO_FAILURE_TERMINATED: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", - __func__, - status); + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", + __func__, request, task); - } else - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS\n", - __func__); + /* The request was terminated explicitly. */ + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + response = SAS_TASK_UNDELIVERED; - break; + /* 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; + break; - case SCI_IO_FAILURE_TERMINATED: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", - __func__, - request, - task); + case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: - /* The request was terminated explicitly. No handling - * is needed in the SCSI error handler path. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; + isci_request_handle_controller_specific_errors(idev, request, + task, &response, + &status); + break; - /* 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; + case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: + /* This is a special case, in that the I/O completion + * is telling us that the device needs a reset. + * In order for the device reset condition to be + * noticed, the I/O has to be handled in the error + * handler. Set the reset flag and cause the + * SCSI error thread to be scheduled. + */ + spin_lock_irqsave(&task->task_state_lock, task_flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, task_flags); - complete_to_host = isci_perform_normal_io_completion; - break; + /* Fail the I/O. */ + response = SAS_TASK_UNDELIVERED; + status = SAM_STAT_TASK_ABORTED; - case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - isci_request_handle_controller_specific_errors( - idev, request, task, &response, &status, - &complete_to_host); + case SCI_FAILURE_RETRY_REQUIRED: - break; + /* Fail the I/O so it can be retried. */ + response = SAS_TASK_UNDELIVERED; + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; - case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: - /* This is a special case, in that the I/O completion - * is telling us that the device needs a reset. - * In order for the device reset condition to be - * noticed, the I/O has to be handled in the error - * handler. Set the reset flag and cause the - * SCSI error thread to be scheduled. - */ - spin_lock_irqsave(&task->task_state_lock, task_flags); - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, task_flags); + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - /* Fail the I/O. */ - response = SAS_TASK_UNDELIVERED; - status = SAM_STAT_TASK_ABORTED; - complete_to_host = isci_perform_error_io_completion; - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; + default: + /* Catch any otherwise unhandled error codes here. */ + dev_dbg(&ihost->pdev->dev, + "%s: invalid completion code: 0x%x - " + "isci_request = %p\n", + __func__, completion_status, request); - case SCI_FAILURE_RETRY_REQUIRED: + response = SAS_TASK_UNDELIVERED; - /* Fail the I/O so it can be retried. */ - response = SAS_TASK_UNDELIVERED; - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; + /* 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_normal_io_completion; + if (SAS_PROTOCOL_SMP == task->task_proto) set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; - - - default: - /* Catch any otherwise unhandled error codes here. */ - dev_dbg(&ihost->pdev->dev, - "%s: invalid completion code: 0x%x - " - "isci_request = %p\n", - __func__, completion_status, request); - - 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; - - if (SAS_PROTOCOL_SMP == task->task_proto) { - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_normal_io_completion; - } else { - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_error_io_completion; - } - break; - } + else + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); break; } @@ -3167,10 +2940,24 @@ static void isci_request_io_request_complete(struct isci_host *ihost, break; } - /* Put the completed request on the correct list */ - isci_task_save_for_upper_layer_completion(ihost, request, response, - status, complete_to_host - ); + spin_lock_irqsave(&task->task_state_lock, task_flags); + + 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. */ 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; INIT_LIST_HEAD(&ireq->completed_node); INIT_LIST_HEAD(&ireq->dev_node); - isci_request_change_state(ireq, allocated); 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); - if (status == SCI_SUCCESS) { - isci_request_change_state(ireq, started); - } else { + if (status != SCI_SUCCESS) { /* The request did not really start in the * hardware, so clear the request handle * here so no terminations will be done. */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, completed); } spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 8d55f78010aa..f3116a51235f 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -60,23 +60,6 @@ #include "host.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 * @pio_len - number of bytes requested at PIO setup @@ -97,13 +80,13 @@ struct isci_stp_request { }; struct isci_request { - enum isci_request_status status; #define IREQ_COMPLETE_IN_TARGET 0 #define IREQ_TERMINATED 1 #define IREQ_TMF 2 #define IREQ_ACTIVE 3 #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ #define IREQ_TC_ABORT_POSTED 5 + #define IREQ_ABORT_PATH_ACTIVE 6 unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { @@ -115,7 +98,6 @@ struct isci_request { struct list_head completed_node; /* For use in the reqs_in_process list: */ struct list_head dev_node; - spinlock_t state_lock; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; 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); } -/** - * 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_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); int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev, struct sas_task *task, u16 tag); -void isci_terminate_pending_requests(struct isci_host *ihost, - struct isci_remote_device *idev); enum sci_status sci_task_request_construct(struct isci_host *ihost, struct isci_remote_device *idev, diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 26de06ef688e..29ce8815e799 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -78,54 +78,25 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, 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); + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n", + __func__, task, response, status); - /* 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) */ - dev_dbg(&ihost->pdev->dev, - "%s: Normal - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - - task->lldd_task = NULL; - 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; + spin_lock_irqsave(&task->task_state_lock, flags); - 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; + task->task_status.resp = response; + task->task_status.stat = status; - default: - dev_dbg(&ihost->pdev->dev, - "%s: isci task notification default case!", - __func__); - sas_task_abort(task); - break; - } + /* 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; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + task->task_done(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; } -/** -* 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, struct isci_remote_device *idev, 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); 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. */ 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); /* 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 * of an unplug. Terminate the TMF request now. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - - if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_timed_out, tmf, - tmf->cb_data); - - 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_remote_device_suspend_terminate(ihost, idev, ireq); } 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, - enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), - void *cb_data) + enum isci_tmf_function_codes code) { memset(tmf, 0, sizeof(*tmf)); - - tmf->tmf_code = code; - tmf->cb_state_func = tmf_sent_cb; - tmf->cb_data = cb_data; + tmf->tmf_code = code; } static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf, enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), 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; } -/** - * 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 * 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 * 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. */ 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) { struct isci_host *ihost = dev_to_ihost(dev); - struct isci_remote_device *isci_device; + struct isci_remote_device *idev; unsigned long flags; int ret; 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); dev_dbg(&ihost->pdev->dev, "%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. */ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); ret = TMF_RESP_FUNC_FAILED; 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)) { sas_ata_schedule_reset(dev); ret = TMF_RESP_FUNC_COMPLETE; - } else - ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun); - - /* If the LUN reset worked, all the I/O can now be terminated. */ - if (ret == TMF_RESP_FUNC_COMPLETE) { - /* Terminate all I/O now. */ - isci_terminate_pending_requests(ihost, isci_device); - isci_remote_device_resume(ihost, isci_device, NULL, NULL); + } else { + /* Suspend the RNC, kill all TCs */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { + ret = TMF_RESP_FUNC_FAILED; + goto out; + } + /* Send the task management part of the reset. */ + ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } out: - isci_put_device(isci_device); + isci_put_device(idev); 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. */ -/** - * 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 * 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) { - 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); struct isci_request *old_request = NULL; - enum isci_request_status old_state; - struct isci_remote_device *isci_device = NULL; + struct isci_remote_device *idev = NULL; struct isci_tmf tmf; int ret = TMF_RESP_FUNC_FAILED; unsigned long flags; - int perform_termination = 0; /* Get the isci_request reference from the task. Note that * this check does not depend on the pending request list * in the device, because tasks driving resets may land here * 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); 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) && (task->task_state_flags & SAS_TASK_AT_INITIATOR) && old_request) - isci_device = isci_lookup_device(task->dev); + idev = isci_lookup_device(task->dev); 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, - "%s: dev = %p, task = %p, old_request == %p\n", - __func__, isci_device, task, old_request); + dev_warn(&ihost->pdev->dev, + "%s: dev = %p, task = %p, old_request == %p\n", + __func__, idev, task, old_request); /* Device reset conditions signalled in task_state_flags are the * responsbility of libsas to observe at the start of the error * handler thread. */ - if (!isci_device || !old_request) { + if (!idev || !old_request) { /* The request has already completed and there * is nothing to do here other than to set the task * done bit, and indicate that the task abort function @@ -1007,126 +534,66 @@ int isci_task_abort_task(struct sas_task *task) ret = TMF_RESP_FUNC_COMPLETE; - dev_warn(&isci_host->pdev->dev, - "%s: abort task not needed for %p\n", - __func__, task); + dev_warn(&ihost->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); goto out; } - - spin_lock_irqsave(&isci_host->scic_lock, flags); - - /* Check the request status and change to "aborted" if currently - * "starting"; if true then set the I/O kernel completion - * struct that will be triggered when the request completes. - */ - old_state = isci_task_validate_request_to_abort( - 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; + /* Suspend the RNC, kill the TC */ + if (isci_remote_device_suspend_terminate(ihost, idev, old_request) + != SCI_SUCCESS) { + dev_warn(&ihost->pdev->dev, + "%s: isci_remote_device_reset_terminate(dev=%p, " + "req=%p, task=%p) failed\n", + __func__, idev, old_request, task); + ret = TMF_RESP_FUNC_FAILED; goto out; } + spin_lock_irqsave(&ihost->scic_lock, flags); + if (task->task_proto == SAS_PROTOCOL_SMP || sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + /* No task to send, so explicitly resume the device here */ + sci_remote_device_resume(idev, NULL, NULL); - dev_warn(&isci_host->pdev->dev, - "%s: %s request" - " or complete_in_target (%d), thus no TMF\n", - __func__, - ((task->task_proto == SAS_PROTOCOL_SMP) - ? "SMP" - : (sas_protocol_ata(task->task_proto) - ? "SATA/STP" - : "") - ), - 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); - task->task_state_flags |= SAS_TASK_STATE_DONE; - task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | - SAS_TASK_STATE_PENDING); - spin_unlock_irqrestore(&task->task_state_lock, flags); - ret = TMF_RESP_FUNC_COMPLETE; - } else { - spin_lock_irqsave(&task->task_state_lock, flags); - task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | - SAS_TASK_STATE_PENDING); - spin_unlock_irqrestore(&task->task_state_lock, flags); - } + spin_unlock_irqrestore(&ihost->scic_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; - } + dev_warn(&ihost->pdev->dev, + "%s: %s request" + " or complete_in_target (%d), thus no TMF\n", + __func__, + ((task->task_proto == SAS_PROTOCOL_SMP) + ? "SMP" + : (sas_protocol_ata(task->task_proto) + ? "SATA/STP" + : "") + ), + test_bit(IREQ_COMPLETE_IN_TARGET, + &old_request->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; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + ret = TMF_RESP_FUNC_COMPLETE; } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, - isci_abort_task_process_cb, old_request); - spin_unlock_irqrestore(&isci_host->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; - } + spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* Send the task management request. */ #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); - - 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) { - set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags); - - /* Clean up the request on our side, and wait for the aborted - * I/O to complete. - */ - isci_terminate_request_core(isci_host, isci_device, - old_request); - isci_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); +out: + isci_put_device(idev); return ret; } @@ -1222,14 +689,11 @@ isci_task_request_complete(struct isci_host *ihost, { struct isci_tmf *tmf = isci_request_access_tmf(ireq); struct completion *tmf_complete = NULL; - struct completion *request_complete = ireq->io_request_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, status=%d\n", __func__, ireq, completion_status); - isci_request_change_state(ireq, completed); - set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); if (tmf) { @@ -1253,20 +717,8 @@ isci_task_request_complete(struct isci_host *ihost, */ 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); - list_del_init(&ireq->dev_node); - } - - /* "request_complete" is set if the task was being terminated. */ - if (request_complete) - complete(request_complete); + isci_free_tag(ihost, ireq->io_tag); + list_del_init(&ireq->dev_node); /* The task management part completes last. */ if (tmf_complete) @@ -1277,37 +729,37 @@ static int isci_reset_device(struct isci_host *ihost, struct domain_device *dev, struct isci_remote_device *idev) { - int rc; - enum sci_status status; + int rc = TMF_RESP_FUNC_COMPLETE, reset_stat; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; 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; 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)) { 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 - 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. */ - isci_remote_device_nuke_requests(ihost, idev); - - /* Since all pending TCs have been cleaned, resume the RNC. */ - status = isci_remote_device_reset_complete(ihost, idev); - - if (status != SCI_SUCCESS) - dev_dbg(&ihost->pdev->dev, - "%s: isci_remote_device_reset_complete(%p) " - "returned %d!\n", __func__, idev, status); + /* Explicitly resume the RNC here, since there was no task sent. */ + isci_remote_device_resume(ihost, idev, NULL, NULL); - dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); + dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", + __func__, idev, reset_stat); out: sas_put_local_phy(phy); return rc; @@ -1321,7 +773,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) int ret; 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); if (!idev) { diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 7b6d0e32fd9b..9c06cbad1d26 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -62,19 +62,6 @@ 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 * of task management requests. @@ -87,6 +74,7 @@ enum isci_tmf_function_codes { isci_tmf_ssp_task_abort = TMF_ABORT_TASK, isci_tmf_ssp_lun_reset = TMF_LU_RESET, }; + /** * struct isci_tmf - This class represents the task management object which * acts as an interface to libsas for processing task management requests @@ -106,15 +94,6 @@ struct isci_tmf { u16 io_tag; enum isci_tmf_function_codes tmf_code; 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) @@ -208,113 +187,4 @@ int isci_queuecommand( struct scsi_cmnd *scsi_cmd, 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_) */ -- cgit v1.2.3 From 637325028f3a9e9cf411ede96063997611f976e4 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:55 -0800 Subject: isci: Device access in the error path does not depend on IDEV_GONE. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 29ce8815e799..9d8720d6266c 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -427,7 +427,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(dev); + idev = isci_get_device(dev->lldd_dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); dev_dbg(&ihost->pdev->dev, @@ -507,7 +507,7 @@ int isci_task_abort_task(struct sas_task *task) if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && (task->task_state_flags & SAS_TASK_AT_INITIATOR) && old_request) - idev = isci_lookup_device(task->dev); + idev = isci_get_device(task->dev->lldd_dev); spin_unlock(&task->task_state_lock); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -593,6 +593,9 @@ int isci_task_abort_task(struct sas_task *task) ISCI_ABORT_TASK_TIMEOUT_MS); } out: + dev_warn(&ihost->pdev->dev, + "%s: Done; dev = %p, task = %p , old_request == %p\n", + __func__, idev, task, old_request); isci_put_device(idev); return ret; } -- cgit v1.2.3 From d6b2a0e4a066ea51322e16e66b25028cb0b4ca7e Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:57 -0800 Subject: isci: Remove isci_device reqs_in_process and dev_node from isci_device. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/init.c | 1 - drivers/scsi/isci/remote_device.c | 6 +----- drivers/scsi/isci/remote_device.h | 1 - drivers/scsi/isci/request.c | 12 ------------ drivers/scsi/isci/request.h | 2 -- drivers/scsi/isci/task.c | 4 ---- 6 files changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 39f12703b893..f2a8a3346307 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -569,7 +569,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { struct isci_remote_device *idev = &ihost->devices[i]; - INIT_LIST_HEAD(&idev->reqs_in_process); INIT_LIST_HEAD(&idev->node); } diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b26ab05107dd..b14eff3c76d0 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -919,7 +919,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_ * here should go through isci_remote_device_nuke_requests. * If we hit this condition, we will need a way to complete * io requests in process */ - BUG_ON(!list_empty(&idev->reqs_in_process)); + BUG_ON(idev->started_request_count > 0); sci_remote_device_destruct(idev); list_del_init(&idev->node); @@ -1345,10 +1345,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport) dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__); return NULL; } - - if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n")) - return NULL; - if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n")) return NULL; diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index da43698e9eba..8b7817cf4352 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -90,7 +90,6 @@ struct isci_remote_device { struct isci_port *isci_port; struct domain_device *domain_dev; struct list_head node; - struct list_head reqs_in_process; struct sci_base_state_machine sm; u32 device_port_width; enum sas_linkrate connection_rate; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index f4e80f31423c..662f36de8052 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2956,9 +2956,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, /* 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. */ sci_controller_complete_io(ihost, request->target_device, request); @@ -3412,7 +3409,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t ireq->flags = 0; ireq->num_sg_entries = 0; INIT_LIST_HEAD(&ireq->completed_node); - INIT_LIST_HEAD(&ireq->dev_node); return ireq; } @@ -3496,17 +3492,9 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide spin_unlock_irqrestore(&ihost->scic_lock, flags); return status; } - /* Either I/O started OK, or the core has signaled that * the device needs a target reset. - * - * In either case, hold onto the I/O for later. - * - * Update it's status and add it to the list in the - * remote device object. */ - list_add(&ireq->dev_node, &idev->reqs_in_process); - if (status != SCI_SUCCESS) { /* The request did not really start in the * hardware, so clear the request handle diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index f3116a51235f..d12e97531da8 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -96,8 +96,6 @@ struct isci_request { struct isci_host *isci_host; /* For use in the requests_to_{complete|abort} lists: */ struct list_head completed_node; - /* For use in the reqs_in_process list: */ - struct list_head dev_node; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; unsigned int num_sg_entries; diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 9d8720d6266c..222fb0de4d59 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -317,9 +317,6 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - /* add the request to the remote device request list. */ - 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); @@ -721,7 +718,6 @@ isci_task_request_complete(struct isci_host *ihost, set_bit(IREQ_TERMINATED, &ireq->flags); isci_free_tag(ihost, ireq->io_tag); - list_del_init(&ireq->dev_node); /* The task management part completes last. */ if (tmf_complete) -- cgit v1.2.3 From 08c031e4e3294a66a64074e12482abda846dd39c Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:00 -0800 Subject: isci: Make sure all TCs are terminated and cleaned in LUN reset. In the libsas error path, SATA disks require extra handling in libata to recover operation. However, libsas expects to be able to immediately recover all outstanding I/O once the error handler escalation stops. This patch fixes the condition where the libata error handler is scheduled for operation but libsas has already deleted the outstanding sas_tasks. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 222fb0de4d59..5d738fd5f882 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -439,16 +439,18 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) goto out; } + /* Suspend the RNC, kill all TCs */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { + /* The suspend/terminate only fails if isci_get_device fails */ + ret = TMF_RESP_FUNC_FAILED; + goto out; + } + /* All pending I/Os have been terminated and cleaned up. */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); ret = TMF_RESP_FUNC_COMPLETE; } else { - /* Suspend the RNC, kill all TCs */ - if (isci_remote_device_suspend_terminate(ihost, idev, NULL) - != SCI_SUCCESS) { - ret = TMF_RESP_FUNC_FAILED; - goto out; - } /* Send the task management part of the reset. */ ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } -- cgit v1.2.3 From 31a38ef0a5ad12dbe262ca55d0a905657be55a8d Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:01 -0800 Subject: isci: Implement waiting for suspend in the abort path. In order to prevent a device from receiving an I/O request while still in an RNC suspending or resuming state (and therefore failing that I/O back to libsas with a reset required status) wait for the RNC state change before proceding in the abort path. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 44 ++++++++++++++++++++++++--------- drivers/scsi/isci/remote_device.h | 6 ++--- drivers/scsi/isci/remote_node_context.c | 21 ++++++++++++++++ drivers/scsi/isci/remote_node_context.h | 3 +++ drivers/scsi/isci/task.c | 14 +++++------ 5 files changed, 66 insertions(+), 22 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 86aca11120f3..acc94a454a1f 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -137,6 +137,14 @@ static enum sci_status sci_remote_device_terminate_reqs_checkabort( return status; } +static bool isci_compare_suspendcount( + struct isci_remote_device *idev, + u32 localcount) +{ + smp_rmb(); + return localcount != idev->rnc.suspend_count; +} + enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, struct isci_remote_device *idev, @@ -144,31 +152,44 @@ enum sci_status isci_remote_device_terminate_requests( { enum sci_status status = SCI_SUCCESS; unsigned long flags; + u32 rnc_suspend_count; spin_lock_irqsave(&ihost->scic_lock, flags); + 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); status = SCI_FAILURE; } else { + /* If already suspended, don't wait for another suspension. */ + smp_rmb(); + rnc_suspend_count + = sci_remote_node_context_is_suspended(&idev->rnc) + ? 0 : idev->rnc.suspend_count; + dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ireq=%p; started_request_count=%d, " + "rnc_suspend_count=%d, rnc.suspend_count=%d" "about to wait\n", - __func__, idev, ireq, idev->started_request_count); + __func__, idev, ireq, idev->started_request_count, + rnc_suspend_count, idev->rnc.suspend_count); if (ireq) { /* Terminate a specific TC. */ sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE, - &ireq->flags)); - + wait_event(ihost->eventq, + (isci_compare_suspendcount(idev, + rnc_suspend_count) + && !test_bit(IREQ_ACTIVE, &ireq->flags))); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, - idev->started_request_count == 0); + (isci_compare_suspendcount(idev, + rnc_suspend_count) + && idev->started_request_count == 0)); } dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", __func__, idev); @@ -1234,19 +1255,20 @@ enum sci_status sci_remote_device_resume( return status; } -enum sci_status isci_remote_device_resume( +enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, - struct isci_remote_device *idev, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p) + struct isci_remote_device *idev) { unsigned long flags; enum sci_status status; spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_resume(idev, cb_fn, cb_p); + /* Preserve any current resume callbacks, for instance from other + * resumptions. + */ + status = sci_remote_device_resume(idev, idev->rnc.user_callback, + idev->rnc.user_cookie); spin_unlock_irqrestore(&ihost->scic_lock, flags); - return status; } /** diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index ef563e5360a3..d1d18925fbfc 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -357,11 +357,9 @@ enum sci_status sci_remote_device_resume( scics_sds_remote_node_context_callback cb_fn, void *cb_p); -enum sci_status isci_remote_device_resume( +enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, - struct isci_remote_device *idev, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p); + struct isci_remote_device *idev); enum sci_status isci_remote_device_reset( struct isci_host *ihost, diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 48565de50016..77c8b5138b7e 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -90,6 +90,15 @@ bool sci_remote_node_context_is_ready( return false; } +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc) +{ + u32 current_state = sci_rnc->sm.current_state_id; + + if (current_state == SCI_RNC_TX_RX_SUSPENDED) + return true; + return false; +} + static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id) { if (id < ihost->remote_node_entries && @@ -339,6 +348,13 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); struct isci_remote_device *idev = rnc_to_dev(rnc); struct isci_host *ihost = idev->owning_port->owning_controller; + u32 new_count = rnc->suspend_count + 1; + + if (new_count == 0) + rnc->suspend_count = 1; + else + rnc->suspend_count = new_count; + smp_wmb(); /* Terminate outstanding requests pending abort. */ sci_remote_device_abort_requests_pending_abort(idev); @@ -634,6 +650,11 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s enum scis_sds_remote_node_context_states state; state = sci_rnc->sm.current_state_id; + dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n", + __func__, rnc_state_name(state), cb_fn, cb_p, + sci_rnc->destination_state); + switch (state) { case SCI_RNC_INITIAL: if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 9eee304fdf9a..c61c02e095ad 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -170,6 +170,7 @@ struct sci_remote_node_context { */ u32 suspend_type; enum sci_remote_node_suspension_reasons suspend_reason; + u32 suspend_count; /** * This field is true if the remote node context is resuming from its current @@ -203,6 +204,8 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, bool sci_remote_node_context_is_ready( struct sci_remote_node_context *sci_rnc); +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc); + enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc, u32 event_code); enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc, diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 5d738fd5f882..c1c6dd0473ae 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -317,11 +317,11 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - /* 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); + /* The RNC must be unsuspended before the TMF can get a response. */ + isci_remote_device_resume_from_abort(ihost, idev); + /* Wait for the TMF to complete, or a timeout. */ timeleft = wait_for_completion_timeout(&completion, msecs_to_jiffies(timeout_ms)); @@ -554,11 +554,11 @@ int isci_task_abort_task(struct sas_task *task) sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { - /* No task to send, so explicitly resume the device here */ - sci_remote_device_resume(idev, NULL, NULL); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* No task to send, so explicitly resume the device here */ + isci_remote_device_resume_from_abort(ihost, idev); + dev_warn(&ihost->pdev->dev, "%s: %s request" " or complete_in_target (%d), thus no TMF\n", @@ -757,7 +757,7 @@ static int isci_reset_device(struct isci_host *ihost, reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); /* Explicitly resume the RNC here, since there was no task sent. */ - isci_remote_device_resume(ihost, idev, NULL, NULL); + isci_remote_device_resume_from_abort(ihost, idev); dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", __func__, idev, reset_stat); -- cgit v1.2.3 From 621120ca56850249554996c94efe75f8200a2cc0 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:03 -0800 Subject: isci: Manage tag releases differently when aborting tasks. When an individual request is being terminated, the request's tag is managed in the terminate function. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 3 ++- drivers/scsi/isci/remote_device.c | 11 +++++++---- drivers/scsi/isci/request.h | 1 + drivers/scsi/isci/task.c | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 53c3ad64c998..ef2790faeab8 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1141,7 +1141,8 @@ void isci_host_completion_routine(unsigned long data) if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags)) wake_up_all(&ihost->eventq); - isci_free_tag(ihost, request->io_tag); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &request->flags)) + isci_free_tag(ihost, request->io_tag); } spin_unlock_irq(&ihost->scic_lock); diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 21a9800a9bec..adeda64e512a 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -104,15 +104,15 @@ static enum sci_status sci_remote_device_terminate_req( int check_abort, struct isci_request *ireq) { - dev_dbg(&ihost->pdev->dev, - "%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; + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p; flags=%lx; req=%p; req target=%p\n", + __func__, idev, idev->flags, ireq, ireq->target_device); + set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); return sci_controller_terminate_request(ihost, idev, ireq); @@ -209,11 +209,14 @@ enum sci_status isci_remote_device_terminate_requests( rnc_suspend_count, idev->rnc.suspend_count); if (ireq) { /* Terminate a specific TC. */ + set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, isci_check_reqterm(ihost, idev, ireq, rnc_suspend_count)); + clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); + isci_free_tag(ihost, ireq->io_tag); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index d12e97531da8..1a651579bb33 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -87,6 +87,7 @@ struct isci_request { #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ #define IREQ_TC_ABORT_POSTED 5 #define IREQ_ABORT_PATH_ACTIVE 6 + #define IREQ_NO_AUTO_FREE_TAG 7 /* Set when being explicitly managed */ unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index c1c6dd0473ae..e798c6ae9592 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -719,7 +719,8 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_free_tag(ihost, ireq->io_tag); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); /* The task management part completes last. */ if (tmf_complete) -- cgit v1.2.3 From 1db79b3e784bffe7e00f9462a5c3441746e48632 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:05 -0800 Subject: isci: Directly control IREQ_ABORT_PATH_ACTIVE when completing TMFs. TMF requests, unlike normal I/O requests, need to handle I/O management conditions in the completion function because TMFs are not handled in the completion tasklet. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index e798c6ae9592..084f8f73fade 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -719,6 +719,9 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) isci_free_tag(ihost, ireq->io_tag); -- cgit v1.2.3 From 397497dd61948b0d59d1d21812b93c97b0eeb2dd Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Sat, 10 Mar 2012 05:46:46 +0000 Subject: isci: Check IDEV_GONE before performing abort path operations. In the link fail path, set IDEV_GONE for every device on the domain when the last link in the port fails. In the abort path functions like isci_reset_device, make sure that there has not already been a detected domain failure with the device by checking IDEV_GONE, before performing any kind of hard reset, SMP phy control, or TMF operation. The check for IDEV_GONE makes sure that the device in the abort path really has control of the port with which it is associated. This prevents starting hard resets at incorrect times and scheduling unnecessary LUN resets for SATA devices. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 23 ++++++++++++++++++++ drivers/scsi/isci/task.c | 55 ++++++++++++++++++++++++++++++------------------ 2 files changed, 57 insertions(+), 21 deletions(-) (limited to 'drivers/scsi/isci/task.c') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index da0c4e1b9b30..2fb85bf75449 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -240,9 +240,32 @@ static void isci_port_link_down(struct isci_host *isci_host, struct isci_phy *isci_phy, struct isci_port *isci_port) { + struct isci_remote_device *isci_device; + dev_dbg(&isci_host->pdev->dev, "%s: isci_port = %p\n", __func__, isci_port); + if (isci_port) { + + /* check to see if this is the last phy on this port. */ + if (isci_phy->sas_phy.port && + isci_phy->sas_phy.port->num_phys == 1) { + /* change the state for all devices on this port. The + * next task sent to this device will be returned as + * SAS_TASK_UNDELIVERED, and the scsi mid layer will + * remove the target + */ + list_for_each_entry(isci_device, + &isci_port->remote_dev_list, + node) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", + __func__, isci_device); + set_bit(IDEV_GONE, &isci_device->flags); + } + } + } + /* Notify libsas of the borken link, this will trigger calls to our * isci_port_deformed and isci_dev_gone functions. */ diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 084f8f73fade..6bc74eb012c9 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -421,7 +421,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) struct isci_host *ihost = dev_to_ihost(dev); struct isci_remote_device *idev; unsigned long flags; - int ret; + int ret = TMF_RESP_FUNC_COMPLETE; spin_lock_irqsave(&ihost->scic_lock, flags); idev = isci_get_device(dev->lldd_dev); @@ -447,12 +447,12 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) goto out; } /* All pending I/Os have been terminated and cleaned up. */ - if (dev_is_sata(dev)) { - sas_ata_schedule_reset(dev); - ret = TMF_RESP_FUNC_COMPLETE; - } else { - /* Send the task management part of the reset. */ - ret = isci_task_send_lu_reset_sas(ihost, idev, lun); + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (dev_is_sata(dev)) + sas_ata_schedule_reset(dev); + else + /* Send the task management part of the reset. */ + ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } out: isci_put_device(idev); @@ -512,8 +512,17 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&ihost->scic_lock, flags); dev_warn(&ihost->pdev->dev, - "%s: dev = %p, task = %p, old_request == %p\n", - __func__, idev, task, old_request); + "%s: dev = %p (%s%s), task = %p, old_request == %p\n", + __func__, idev, + (dev_is_sata(task->dev) ? "STP/SATA" + : ((dev_is_expander(task->dev)) + ? "SMP" + : "SSP")), + ((idev) ? ((test_bit(IDEV_GONE, &idev->flags)) + ? " IDEV_GONE" + : "") + : " "), + task, old_request); /* Device reset conditions signalled in task_state_flags are the * responsbility of libsas to observe at the start of the error @@ -552,7 +561,8 @@ int isci_task_abort_task(struct sas_task *task) if (task->task_proto == SAS_PROTOCOL_SMP || sas_protocol_ata(task->task_proto) || - test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { + test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) || + test_bit(IDEV_GONE, &idev->flags)) { spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -561,7 +571,8 @@ int isci_task_abort_task(struct sas_task *task) dev_warn(&ihost->pdev->dev, "%s: %s request" - " or complete_in_target (%d), thus no TMF\n", + " or complete_in_target (%d), " + "or IDEV_GONE (%d), thus no TMF\n", __func__, ((task->task_proto == SAS_PROTOCOL_SMP) ? "SMP" @@ -570,7 +581,8 @@ int isci_task_abort_task(struct sas_task *task) : "") ), test_bit(IREQ_COMPLETE_IN_TARGET, - &old_request->flags)); + &old_request->flags), + test_bit(IDEV_GONE, &idev->flags)); spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | @@ -734,7 +746,7 @@ static int isci_reset_device(struct isci_host *ihost, struct domain_device *dev, struct isci_remote_device *idev) { - int rc = TMF_RESP_FUNC_COMPLETE, reset_stat; + int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; @@ -752,14 +764,15 @@ static int isci_reset_device(struct isci_host *ihost, * primary duty of this function is to cleanup tasks, so that is the * relevant status. */ - - if (scsi_is_sas_phy_local(phy)) { - struct isci_phy *iphy = &ihost->phys[phy->number]; - - reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy); - } else - reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); - + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (scsi_is_sas_phy_local(phy)) { + struct isci_phy *iphy = &ihost->phys[phy->number]; + + reset_stat = isci_port_perform_hard_reset(ihost, iport, + iphy); + } else + reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); + } /* Explicitly resume the RNC here, since there was no task sent. */ isci_remote_device_resume_from_abort(ihost, idev); -- cgit v1.2.3