Commit 726980d56908f2e230624394f03743689db3110c
Committed by
Dan Williams
1 parent
ac78ed0f78
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
isci: Terminate outstanding TCs on TX/RX RNC suspensions.
TCs must only be terminated when RNCs are suspended. Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Showing 7 changed files with 188 additions and 32 deletions Side-by-side Diff
drivers/scsi/isci/host.c
... | ... | @@ -2696,18 +2696,18 @@ |
2696 | 2696 | __func__, ihost->sm.current_state_id); |
2697 | 2697 | return SCI_FAILURE_INVALID_STATE; |
2698 | 2698 | } |
2699 | - | |
2700 | 2699 | status = sci_io_request_terminate(ireq); |
2701 | - if (status != SCI_SUCCESS) | |
2702 | - return status; | |
2703 | - | |
2704 | - /* | |
2705 | - * Utilize the original post context command and or in the POST_TC_ABORT | |
2706 | - * request sub-type. | |
2707 | - */ | |
2708 | - sci_controller_post_request(ihost, | |
2709 | - ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); | |
2710 | - return SCI_SUCCESS; | |
2700 | + if ((status == SCI_SUCCESS) && | |
2701 | + !test_bit(IREQ_PENDING_ABORT, &ireq->flags) && | |
2702 | + !test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) { | |
2703 | + /* Utilize the original post context command and or in the | |
2704 | + * POST_TC_ABORT request sub-type. | |
2705 | + */ | |
2706 | + sci_controller_post_request( | |
2707 | + ihost, ireq->post_context | | |
2708 | + SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); | |
2709 | + } | |
2710 | + return status; | |
2711 | 2711 | } |
2712 | 2712 | |
2713 | 2713 | /** |
drivers/scsi/isci/remote_device.c
... | ... | @@ -133,6 +133,50 @@ |
133 | 133 | wake_up(&ihost->eventq); |
134 | 134 | } |
135 | 135 | |
136 | +static enum sci_status sci_remote_device_suspend( | |
137 | + struct isci_remote_device *idev) | |
138 | +{ | |
139 | + return sci_remote_node_context_suspend( | |
140 | + &idev->rnc, | |
141 | + SCI_SOFTWARE_SUSPENSION, | |
142 | + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, | |
143 | + NULL, NULL); | |
144 | +} | |
145 | + | |
146 | +enum sci_status isci_remote_device_suspend( | |
147 | + struct isci_host *ihost, | |
148 | + struct isci_remote_device *idev) | |
149 | +{ | |
150 | + enum sci_status status; | |
151 | + unsigned long flags; | |
152 | + | |
153 | + spin_lock_irqsave(&ihost->scic_lock, flags); | |
154 | + | |
155 | + if (isci_lookup_device(idev->domain_dev) == NULL) { | |
156 | + spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
157 | + status = SCI_FAILURE; | |
158 | + } else { | |
159 | + status = sci_remote_device_suspend(idev); | |
160 | + spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
161 | + if (status == SCI_SUCCESS) { | |
162 | + wait_event(ihost->eventq, | |
163 | + test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) | |
164 | + || !test_bit(IDEV_ALLOCATED, &idev->flags)); | |
165 | + | |
166 | + status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) | |
167 | + ? SCI_SUCCESS : SCI_FAILURE; | |
168 | + dev_dbg(&ihost->pdev->dev, | |
169 | + "%s: idev=%p, wait done, device is %s\n", | |
170 | + __func__, idev, | |
171 | + test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) | |
172 | + ? "<suspended>" : "<deallocated!>"); | |
173 | + | |
174 | + } | |
175 | + isci_put_device(idev); | |
176 | + } | |
177 | + return status; | |
178 | +} | |
179 | + | |
136 | 180 | /* called once the remote node context is ready to be freed. |
137 | 181 | * The remote device can now report that its stop operation is complete. none |
138 | 182 | */ |
... | ... | @@ -144,7 +188,9 @@ |
144 | 188 | sci_change_state(&idev->sm, SCI_DEV_STOPPED); |
145 | 189 | } |
146 | 190 | |
147 | -static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev) | |
191 | +static enum sci_status sci_remote_device_terminate_requests_checkabort( | |
192 | + struct isci_remote_device *idev, | |
193 | + int check_abort_pending) | |
148 | 194 | { |
149 | 195 | struct isci_host *ihost = idev->owning_port->owning_controller; |
150 | 196 | enum sci_status status = SCI_SUCCESS; |
... | ... | @@ -155,7 +201,9 @@ |
155 | 201 | enum sci_status s; |
156 | 202 | |
157 | 203 | if (!test_bit(IREQ_ACTIVE, &ireq->flags) || |
158 | - ireq->target_device != idev) | |
204 | + (ireq->target_device != idev) || | |
205 | + (check_abort_pending && !test_bit(IREQ_PENDING_ABORT, | |
206 | + &ireq->flags))) | |
159 | 207 | continue; |
160 | 208 | |
161 | 209 | s = sci_controller_terminate_request(ihost, idev, ireq); |
... | ... | @@ -166,6 +214,12 @@ |
166 | 214 | return status; |
167 | 215 | } |
168 | 216 | |
217 | +enum sci_status sci_remote_device_terminate_requests( | |
218 | + struct isci_remote_device *idev) | |
219 | +{ | |
220 | + return sci_remote_device_terminate_requests_checkabort(idev, 0); | |
221 | +} | |
222 | + | |
169 | 223 | enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, |
170 | 224 | u32 timeout) |
171 | 225 | { |
... | ... | @@ -265,14 +319,6 @@ |
265 | 319 | return SCI_SUCCESS; |
266 | 320 | } |
267 | 321 | |
268 | -enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev) | |
269 | -{ | |
270 | - return sci_remote_node_context_suspend(&idev->rnc, | |
271 | - SCI_SOFTWARE_SUSPENSION, | |
272 | - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, | |
273 | - NULL, NULL); | |
274 | -} | |
275 | - | |
276 | 322 | enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev, |
277 | 323 | u32 frame_index) |
278 | 324 | { |
... | ... | @@ -1186,7 +1232,7 @@ |
1186 | 1232 | * the device when there have been no phys added to it. |
1187 | 1233 | */ |
1188 | 1234 | static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, |
1189 | - u32 timeout) | |
1235 | + u32 timeout) | |
1190 | 1236 | { |
1191 | 1237 | struct sci_base_state_machine *sm = &idev->sm; |
1192 | 1238 | enum sci_remote_device_states state = sm->current_state_id; |
... | ... | @@ -1412,5 +1458,43 @@ |
1412 | 1458 | wait_for_device_start(isci_host, isci_device); |
1413 | 1459 | |
1414 | 1460 | return status == SCI_SUCCESS ? 0 : -ENODEV; |
1461 | +} | |
1462 | + | |
1463 | +enum sci_status isci_remote_device_reset( | |
1464 | + struct isci_remote_device *idev) | |
1465 | +{ | |
1466 | + struct isci_host *ihost = dev_to_ihost(idev->domain_dev); | |
1467 | + unsigned long flags; | |
1468 | + enum sci_status status; | |
1469 | + | |
1470 | + /* Wait for the device suspend. */ | |
1471 | + status = isci_remote_device_suspend(ihost, idev); | |
1472 | + if (status != SCI_SUCCESS) { | |
1473 | + dev_dbg(&ihost->pdev->dev, | |
1474 | + "%s: isci_remote_device_suspend(%p) returned %d!\n", | |
1475 | + __func__, idev, status); | |
1476 | + return status; | |
1477 | + } | |
1478 | + spin_lock_irqsave(&ihost->scic_lock, flags); | |
1479 | + status = sci_remote_device_reset(idev); | |
1480 | + spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
1481 | + if (status != SCI_SUCCESS) { | |
1482 | + dev_dbg(&ihost->pdev->dev, | |
1483 | + "%s: sci_remote_device_reset(%p) returned %d!\n", | |
1484 | + __func__, idev, status); | |
1485 | + } | |
1486 | + return status; | |
1487 | +} | |
1488 | + | |
1489 | +int isci_remote_device_is_safe_to_abort( | |
1490 | + struct isci_remote_device *idev) | |
1491 | +{ | |
1492 | + return sci_remote_node_context_is_safe_to_abort(&idev->rnc); | |
1493 | +} | |
1494 | + | |
1495 | +enum sci_status sci_remote_device_abort_requests_pending_abort( | |
1496 | + struct isci_remote_device *idev) | |
1497 | +{ | |
1498 | + return sci_remote_device_terminate_requests_checkabort(idev, 1); | |
1415 | 1499 | } |
drivers/scsi/isci/remote_device.h
... | ... | @@ -85,6 +85,7 @@ |
85 | 85 | #define IDEV_GONE 3 |
86 | 86 | #define IDEV_IO_READY 4 |
87 | 87 | #define IDEV_IO_NCQERROR 5 |
88 | + #define IDEV_TXRX_SUSPENDED 6 | |
88 | 89 | unsigned long flags; |
89 | 90 | struct kref kref; |
90 | 91 | struct isci_port *isci_port; |
... | ... | @@ -335,5 +336,14 @@ |
335 | 336 | struct isci_remote_device *idev, |
336 | 337 | u32 request); |
337 | 338 | |
339 | +enum sci_status sci_remote_device_terminate_requests( | |
340 | + struct isci_remote_device *idev); | |
341 | + | |
342 | +int isci_remote_device_is_safe_to_abort( | |
343 | + struct isci_remote_device *idev); | |
344 | + | |
345 | +enum sci_status | |
346 | +sci_remote_device_abort_requests_pending_abort( | |
347 | + struct isci_remote_device *idev); | |
338 | 348 | #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ |
drivers/scsi/isci/remote_node_context.c
... | ... | @@ -268,6 +268,8 @@ |
268 | 268 | { |
269 | 269 | struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); |
270 | 270 | |
271 | + /* Terminate outstanding requests pending abort. */ | |
272 | + sci_remote_device_abort_requests_pending_abort(rnc_to_dev(rnc)); | |
271 | 273 | sci_remote_node_context_invalidate_context_buffer(rnc); |
272 | 274 | } |
273 | 275 | |
274 | 276 | |
275 | 277 | |
... | ... | @@ -312,10 +314,28 @@ |
312 | 314 | static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm) |
313 | 315 | { |
314 | 316 | struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); |
317 | + struct isci_remote_device *idev = rnc_to_dev(rnc); | |
318 | + struct isci_host *ihost = idev->owning_port->owning_controller; | |
315 | 319 | |
320 | + set_bit(IDEV_TXRX_SUSPENDED, &idev->flags); | |
321 | + | |
322 | + /* Terminate outstanding requests pending abort. */ | |
323 | + sci_remote_device_abort_requests_pending_abort(idev); | |
324 | + | |
325 | + wake_up(&ihost->eventq); | |
316 | 326 | sci_remote_node_context_continue_state_transitions(rnc); |
317 | 327 | } |
318 | 328 | |
329 | +static void sci_remote_node_context_tx_rx_suspended_state_exit( | |
330 | + struct sci_base_state_machine *sm) | |
331 | +{ | |
332 | + struct sci_remote_node_context *rnc | |
333 | + = container_of(sm, typeof(*rnc), sm); | |
334 | + struct isci_remote_device *idev = rnc_to_dev(rnc); | |
335 | + | |
336 | + clear_bit(IDEV_TXRX_SUSPENDED, &idev->flags); | |
337 | +} | |
338 | + | |
319 | 339 | static void sci_remote_node_context_await_suspend_state_exit( |
320 | 340 | struct sci_base_state_machine *sm) |
321 | 341 | { |
... | ... | @@ -346,6 +366,8 @@ |
346 | 366 | }, |
347 | 367 | [SCI_RNC_TX_RX_SUSPENDED] = { |
348 | 368 | .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, |
369 | + .exit_state | |
370 | + = sci_remote_node_context_tx_rx_suspended_state_exit, | |
349 | 371 | }, |
350 | 372 | [SCI_RNC_AWAIT_SUSPENSION] = { |
351 | 373 | .exit_state = sci_remote_node_context_await_suspend_state_exit, |
... | ... | @@ -515,6 +537,13 @@ |
515 | 537 | struct isci_remote_device *idev = rnc_to_dev(sci_rnc); |
516 | 538 | enum sci_status status = SCI_FAILURE_INVALID_STATE; |
517 | 539 | |
540 | + dev_dbg(scirdev_to_dev(idev), | |
541 | + "%s: current state %d, current suspend_type %x dest state %d," | |
542 | + " arg suspend_reason %d, arg suspend_type %x", | |
543 | + __func__, state, sci_rnc->suspend_type, | |
544 | + sci_rnc->destination_state, suspend_reason, | |
545 | + suspend_type); | |
546 | + | |
518 | 547 | /* Disable automatic state continuations if explicitly suspending. */ |
519 | 548 | if (suspend_reason == SCI_SOFTWARE_SUSPENSION) |
520 | 549 | sci_rnc->destination_state |
521 | 550 | |
... | ... | @@ -546,7 +575,10 @@ |
546 | 575 | sci_rnc->suspend_type = suspend_type; |
547 | 576 | |
548 | 577 | if (status == SCI_SUCCESS) { /* Already in the destination state? */ |
578 | + struct isci_host *ihost = idev->owning_port->owning_controller; | |
579 | + | |
549 | 580 | sci_remote_node_context_notify_user(sci_rnc); |
581 | + wake_up_all(&ihost->eventq); /* Let observers look. */ | |
550 | 582 | return SCI_SUCCESS; |
551 | 583 | } |
552 | 584 | if (suspend_reason == SCI_SOFTWARE_SUSPENSION) { |
... | ... | @@ -659,6 +691,30 @@ |
659 | 691 | "%s: invalid state %s\n", __func__, |
660 | 692 | rnc_state_name(state)); |
661 | 693 | return SCI_FAILURE_INVALID_STATE; |
694 | + } | |
695 | +} | |
696 | + | |
697 | +int sci_remote_node_context_is_safe_to_abort( | |
698 | + struct sci_remote_node_context *sci_rnc) | |
699 | +{ | |
700 | + enum scis_sds_remote_node_context_states state; | |
701 | + | |
702 | + state = sci_rnc->sm.current_state_id; | |
703 | + switch (state) { | |
704 | + case SCI_RNC_INVALIDATING: | |
705 | + case SCI_RNC_TX_RX_SUSPENDED: | |
706 | + return 1; | |
707 | + case SCI_RNC_POSTING: | |
708 | + case SCI_RNC_RESUMING: | |
709 | + case SCI_RNC_READY: | |
710 | + case SCI_RNC_TX_SUSPENDED: | |
711 | + case SCI_RNC_AWAIT_SUSPENSION: | |
712 | + case SCI_RNC_INITIAL: | |
713 | + return 0; | |
714 | + default: | |
715 | + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), | |
716 | + "%s: invalid state %d\n", __func__, state); | |
717 | + return 0; | |
662 | 718 | } |
663 | 719 | } |
drivers/scsi/isci/remote_node_context.h
... | ... | @@ -214,6 +214,8 @@ |
214 | 214 | struct isci_request *ireq); |
215 | 215 | enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc, |
216 | 216 | struct isci_request *ireq); |
217 | +int sci_remote_node_context_is_safe_to_abort( | |
218 | + struct sci_remote_node_context *sci_rnc); | |
217 | 219 | |
218 | 220 | #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ |
drivers/scsi/isci/request.c
... | ... | @@ -863,6 +863,8 @@ |
863 | 863 | |
864 | 864 | switch (state) { |
865 | 865 | case SCI_REQ_CONSTRUCTED: |
866 | + /* Set to make sure no HW terminate posting is done: */ | |
867 | + set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags); | |
866 | 868 | ireq->scu_status = SCU_TASK_DONE_TASK_ABORT; |
867 | 869 | ireq->sci_status = SCI_FAILURE_IO_TERMINATED; |
868 | 870 | sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); |
... | ... | @@ -883,8 +885,7 @@ |
883 | 885 | case SCI_REQ_ATAPI_WAIT_PIO_SETUP: |
884 | 886 | case SCI_REQ_ATAPI_WAIT_D2H: |
885 | 887 | case SCI_REQ_ATAPI_WAIT_TC_COMP: |
886 | - sci_change_state(&ireq->sm, SCI_REQ_ABORTING); | |
887 | - return SCI_SUCCESS; | |
888 | + /* Fall through and change state to ABORTING... */ | |
888 | 889 | case SCI_REQ_TASK_WAIT_TC_RESP: |
889 | 890 | /* The task frame was already confirmed to have been |
890 | 891 | * sent by the SCU HW. Since the state machine is |
891 | 892 | |
892 | 893 | |
893 | 894 | |
... | ... | @@ -893,20 +894,21 @@ |
893 | 894 | * and don't wait for the task response. |
894 | 895 | */ |
895 | 896 | sci_change_state(&ireq->sm, SCI_REQ_ABORTING); |
896 | - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); | |
897 | - return SCI_SUCCESS; | |
897 | + /* Fall through and handle like ABORTING... */ | |
898 | 898 | case SCI_REQ_ABORTING: |
899 | - /* If a request has a termination requested twice, return | |
900 | - * a failure indication, since HW confirmation of the first | |
901 | - * abort is still outstanding. | |
899 | + if (!isci_remote_device_is_safe_to_abort(ireq->target_device)) | |
900 | + set_bit(IREQ_PENDING_ABORT, &ireq->flags); | |
901 | + else | |
902 | + clear_bit(IREQ_PENDING_ABORT, &ireq->flags); | |
903 | + /* If the request is only waiting on the remote device | |
904 | + * suspension, return SUCCESS so the caller will wait too. | |
902 | 905 | */ |
906 | + return SCI_SUCCESS; | |
903 | 907 | case SCI_REQ_COMPLETED: |
904 | 908 | default: |
905 | 909 | dev_warn(&ireq->owning_controller->pdev->dev, |
906 | 910 | "%s: SCIC IO Request requested to abort while in wrong " |
907 | - "state %d\n", | |
908 | - __func__, | |
909 | - ireq->sm.current_state_id); | |
911 | + "state %d\n", __func__, ireq->sm.current_state_id); | |
910 | 912 | break; |
911 | 913 | } |
912 | 914 |
drivers/scsi/isci/request.h
... | ... | @@ -102,6 +102,8 @@ |
102 | 102 | #define IREQ_TERMINATED 1 |
103 | 103 | #define IREQ_TMF 2 |
104 | 104 | #define IREQ_ACTIVE 3 |
105 | + #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ | |
106 | + #define IREQ_TC_ABORT_POSTED 5 | |
105 | 107 | unsigned long flags; |
106 | 108 | /* XXX kill ttype and ttype_ptr, allocate full sas_task */ |
107 | 109 | union ttype_ptr_union { |