diff options
author | Jeff Skirvin <jeffrey.d.skirvin@intel.com> | 2012-03-09 01:42:01 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2012-05-17 17:33:41 -0400 |
commit | 31a38ef0a5ad12dbe262ca55d0a905657be55a8d (patch) | |
tree | cf05c3c5fb97670711e58edc6708d7f52e02f418 /drivers | |
parent | 08c031e4e3294a66a64074e12482abda846dd39c (diff) |
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')
-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( | |||
137 | return status; | 137 | return status; |
138 | } | 138 | } |
139 | 139 | ||
140 | static bool isci_compare_suspendcount( | ||
141 | struct isci_remote_device *idev, | ||
142 | u32 localcount) | ||
143 | { | ||
144 | smp_rmb(); | ||
145 | return localcount != idev->rnc.suspend_count; | ||
146 | } | ||
147 | |||
140 | enum sci_status isci_remote_device_terminate_requests( | 148 | enum sci_status isci_remote_device_terminate_requests( |
141 | struct isci_host *ihost, | 149 | struct isci_host *ihost, |
142 | struct isci_remote_device *idev, | 150 | struct isci_remote_device *idev, |
@@ -144,31 +152,44 @@ enum sci_status isci_remote_device_terminate_requests( | |||
144 | { | 152 | { |
145 | enum sci_status status = SCI_SUCCESS; | 153 | enum sci_status status = SCI_SUCCESS; |
146 | unsigned long flags; | 154 | unsigned long flags; |
155 | u32 rnc_suspend_count; | ||
147 | 156 | ||
148 | spin_lock_irqsave(&ihost->scic_lock, flags); | 157 | spin_lock_irqsave(&ihost->scic_lock, flags); |
158 | |||
149 | if (isci_get_device(idev) == NULL) { | 159 | if (isci_get_device(idev) == NULL) { |
150 | dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", | 160 | dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", |
151 | __func__, idev); | 161 | __func__, idev); |
152 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 162 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
153 | status = SCI_FAILURE; | 163 | status = SCI_FAILURE; |
154 | } else { | 164 | } else { |
165 | /* If already suspended, don't wait for another suspension. */ | ||
166 | smp_rmb(); | ||
167 | rnc_suspend_count | ||
168 | = sci_remote_node_context_is_suspended(&idev->rnc) | ||
169 | ? 0 : idev->rnc.suspend_count; | ||
170 | |||
155 | dev_dbg(&ihost->pdev->dev, | 171 | dev_dbg(&ihost->pdev->dev, |
156 | "%s: idev=%p, ireq=%p; started_request_count=%d, " | 172 | "%s: idev=%p, ireq=%p; started_request_count=%d, " |
173 | "rnc_suspend_count=%d, rnc.suspend_count=%d" | ||
157 | "about to wait\n", | 174 | "about to wait\n", |
158 | __func__, idev, ireq, idev->started_request_count); | 175 | __func__, idev, ireq, idev->started_request_count, |
176 | rnc_suspend_count, idev->rnc.suspend_count); | ||
159 | if (ireq) { | 177 | if (ireq) { |
160 | /* Terminate a specific TC. */ | 178 | /* Terminate a specific TC. */ |
161 | sci_remote_device_terminate_req(ihost, idev, 0, ireq); | 179 | sci_remote_device_terminate_req(ihost, idev, 0, ireq); |
162 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 180 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
163 | wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE, | 181 | wait_event(ihost->eventq, |
164 | &ireq->flags)); | 182 | (isci_compare_suspendcount(idev, |
165 | 183 | rnc_suspend_count) | |
184 | && !test_bit(IREQ_ACTIVE, &ireq->flags))); | ||
166 | } else { | 185 | } else { |
167 | /* Terminate all TCs. */ | 186 | /* Terminate all TCs. */ |
168 | sci_remote_device_terminate_requests(idev); | 187 | sci_remote_device_terminate_requests(idev); |
169 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 188 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
170 | wait_event(ihost->eventq, | 189 | wait_event(ihost->eventq, |
171 | idev->started_request_count == 0); | 190 | (isci_compare_suspendcount(idev, |
191 | rnc_suspend_count) | ||
192 | && idev->started_request_count == 0)); | ||
172 | } | 193 | } |
173 | dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", | 194 | dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", |
174 | __func__, idev); | 195 | __func__, idev); |
@@ -1234,19 +1255,20 @@ enum sci_status sci_remote_device_resume( | |||
1234 | return status; | 1255 | return status; |
1235 | } | 1256 | } |
1236 | 1257 | ||
1237 | enum sci_status isci_remote_device_resume( | 1258 | enum sci_status isci_remote_device_resume_from_abort( |
1238 | struct isci_host *ihost, | 1259 | struct isci_host *ihost, |
1239 | struct isci_remote_device *idev, | 1260 | struct isci_remote_device *idev) |
1240 | scics_sds_remote_node_context_callback cb_fn, | ||
1241 | void *cb_p) | ||
1242 | { | 1261 | { |
1243 | unsigned long flags; | 1262 | unsigned long flags; |
1244 | enum sci_status status; | 1263 | enum sci_status status; |
1245 | 1264 | ||
1246 | spin_lock_irqsave(&ihost->scic_lock, flags); | 1265 | spin_lock_irqsave(&ihost->scic_lock, flags); |
1247 | status = sci_remote_device_resume(idev, cb_fn, cb_p); | 1266 | /* Preserve any current resume callbacks, for instance from other |
1267 | * resumptions. | ||
1268 | */ | ||
1269 | status = sci_remote_device_resume(idev, idev->rnc.user_callback, | ||
1270 | idev->rnc.user_cookie); | ||
1248 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 1271 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
1249 | |||
1250 | return status; | 1272 | return status; |
1251 | } | 1273 | } |
1252 | /** | 1274 | /** |
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( | |||
357 | scics_sds_remote_node_context_callback cb_fn, | 357 | scics_sds_remote_node_context_callback cb_fn, |
358 | void *cb_p); | 358 | void *cb_p); |
359 | 359 | ||
360 | enum sci_status isci_remote_device_resume( | 360 | enum sci_status isci_remote_device_resume_from_abort( |
361 | struct isci_host *ihost, | 361 | struct isci_host *ihost, |
362 | struct isci_remote_device *idev, | 362 | struct isci_remote_device *idev); |
363 | scics_sds_remote_node_context_callback cb_fn, | ||
364 | void *cb_p); | ||
365 | 363 | ||
366 | enum sci_status isci_remote_device_reset( | 364 | enum sci_status isci_remote_device_reset( |
367 | struct isci_host *ihost, | 365 | 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( | |||
90 | return false; | 90 | return false; |
91 | } | 91 | } |
92 | 92 | ||
93 | bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc) | ||
94 | { | ||
95 | u32 current_state = sci_rnc->sm.current_state_id; | ||
96 | |||
97 | if (current_state == SCI_RNC_TX_RX_SUSPENDED) | ||
98 | return true; | ||
99 | return false; | ||
100 | } | ||
101 | |||
93 | static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id) | 102 | static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id) |
94 | { | 103 | { |
95 | if (id < ihost->remote_node_entries && | 104 | if (id < ihost->remote_node_entries && |
@@ -339,6 +348,13 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ | |||
339 | struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); | 348 | struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); |
340 | struct isci_remote_device *idev = rnc_to_dev(rnc); | 349 | struct isci_remote_device *idev = rnc_to_dev(rnc); |
341 | struct isci_host *ihost = idev->owning_port->owning_controller; | 350 | struct isci_host *ihost = idev->owning_port->owning_controller; |
351 | u32 new_count = rnc->suspend_count + 1; | ||
352 | |||
353 | if (new_count == 0) | ||
354 | rnc->suspend_count = 1; | ||
355 | else | ||
356 | rnc->suspend_count = new_count; | ||
357 | smp_wmb(); | ||
342 | 358 | ||
343 | /* Terminate outstanding requests pending abort. */ | 359 | /* Terminate outstanding requests pending abort. */ |
344 | sci_remote_device_abort_requests_pending_abort(idev); | 360 | 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 | |||
634 | enum scis_sds_remote_node_context_states state; | 650 | enum scis_sds_remote_node_context_states state; |
635 | 651 | ||
636 | state = sci_rnc->sm.current_state_id; | 652 | state = sci_rnc->sm.current_state_id; |
653 | dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), | ||
654 | "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n", | ||
655 | __func__, rnc_state_name(state), cb_fn, cb_p, | ||
656 | sci_rnc->destination_state); | ||
657 | |||
637 | switch (state) { | 658 | switch (state) { |
638 | case SCI_RNC_INITIAL: | 659 | case SCI_RNC_INITIAL: |
639 | if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) | 660 | 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 { | |||
170 | */ | 170 | */ |
171 | u32 suspend_type; | 171 | u32 suspend_type; |
172 | enum sci_remote_node_suspension_reasons suspend_reason; | 172 | enum sci_remote_node_suspension_reasons suspend_reason; |
173 | u32 suspend_count; | ||
173 | 174 | ||
174 | /** | 175 | /** |
175 | * This field is true if the remote node context is resuming from its current | 176 | * 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, | |||
203 | bool sci_remote_node_context_is_ready( | 204 | bool sci_remote_node_context_is_ready( |
204 | struct sci_remote_node_context *sci_rnc); | 205 | struct sci_remote_node_context *sci_rnc); |
205 | 206 | ||
207 | bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc); | ||
208 | |||
206 | enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc, | 209 | enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc, |
207 | u32 event_code); | 210 | u32 event_code); |
208 | enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc, | 211 | 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, | |||
317 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 317 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
318 | goto err_tci; | 318 | goto err_tci; |
319 | } | 319 | } |
320 | /* The RNC must be unsuspended before the TMF can get a response. */ | ||
321 | sci_remote_device_resume(idev, NULL, NULL); | ||
322 | |||
323 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 320 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
324 | 321 | ||
322 | /* The RNC must be unsuspended before the TMF can get a response. */ | ||
323 | isci_remote_device_resume_from_abort(ihost, idev); | ||
324 | |||
325 | /* Wait for the TMF to complete, or a timeout. */ | 325 | /* Wait for the TMF to complete, or a timeout. */ |
326 | timeleft = wait_for_completion_timeout(&completion, | 326 | timeleft = wait_for_completion_timeout(&completion, |
327 | msecs_to_jiffies(timeout_ms)); | 327 | msecs_to_jiffies(timeout_ms)); |
@@ -554,11 +554,11 @@ int isci_task_abort_task(struct sas_task *task) | |||
554 | sas_protocol_ata(task->task_proto) || | 554 | sas_protocol_ata(task->task_proto) || |
555 | test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { | 555 | test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { |
556 | 556 | ||
557 | /* No task to send, so explicitly resume the device here */ | ||
558 | sci_remote_device_resume(idev, NULL, NULL); | ||
559 | |||
560 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | 557 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
561 | 558 | ||
559 | /* No task to send, so explicitly resume the device here */ | ||
560 | isci_remote_device_resume_from_abort(ihost, idev); | ||
561 | |||
562 | dev_warn(&ihost->pdev->dev, | 562 | dev_warn(&ihost->pdev->dev, |
563 | "%s: %s request" | 563 | "%s: %s request" |
564 | " or complete_in_target (%d), thus no TMF\n", | 564 | " or complete_in_target (%d), thus no TMF\n", |
@@ -757,7 +757,7 @@ static int isci_reset_device(struct isci_host *ihost, | |||
757 | reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); | 757 | reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); |
758 | 758 | ||
759 | /* Explicitly resume the RNC here, since there was no task sent. */ | 759 | /* Explicitly resume the RNC here, since there was no task sent. */ |
760 | isci_remote_device_resume(ihost, idev, NULL, NULL); | 760 | isci_remote_device_resume_from_abort(ihost, idev); |
761 | 761 | ||
762 | dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", | 762 | dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", |
763 | __func__, idev, reset_stat); | 763 | __func__, idev, reset_stat); |