diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2012-03-08 22:42:01 -0800 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2012-05-17 14:33:41 -0700 |
commit | 31a38ef0a5ad12dbe262ca55d0a905657be55a8d (patch) | |
tree | cf05c3c5fb97670711e58edc6708d7f52e02f418 /drivers/scsi | |
parent | 08c031e4e3294a66a64074e12482abda846dd39c (diff) | |
download | linux-stable-31a38ef0a5ad12dbe262ca55d0a905657be55a8d.tar.gz linux-stable-31a38ef0a5ad12dbe262ca55d0a905657be55a8d.tar.bz2 linux-stable-31a38ef0a5ad12dbe262ca55d0a905657be55a8d.zip |
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 <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 44 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.h | 6 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_node_context.c | 21 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_node_context.h | 3 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 14 |
5 files changed, 66 insertions, 22 deletions
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); |