Commit 726980d5 authored by Jeff Skirvin's avatar Jeff Skirvin Committed by Dan Williams

isci: Terminate outstanding TCs on TX/RX RNC suspensions.

TCs must only be terminated when RNCs are suspended.
Signed-off-by: default avatarJeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent ac78ed0f
...@@ -2696,18 +2696,18 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, ...@@ -2696,18 +2696,18 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost,
__func__, ihost->sm.current_state_id); __func__, ihost->sm.current_state_id);
return SCI_FAILURE_INVALID_STATE; return SCI_FAILURE_INVALID_STATE;
} }
status = sci_io_request_terminate(ireq); status = sci_io_request_terminate(ireq);
if (status != SCI_SUCCESS) if ((status == SCI_SUCCESS) &&
return status; !test_bit(IREQ_PENDING_ABORT, &ireq->flags) &&
!test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) {
/* /* Utilize the original post context command and or in the
* Utilize the original post context command and or in the POST_TC_ABORT * POST_TC_ABORT request sub-type.
* request sub-type. */
*/ sci_controller_post_request(
sci_controller_post_request(ihost, ihost, ireq->post_context |
ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT);
return SCI_SUCCESS; }
return status;
} }
/** /**
......
...@@ -133,6 +133,50 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote ...@@ -133,6 +133,50 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
wake_up(&ihost->eventq); wake_up(&ihost->eventq);
} }
static enum sci_status sci_remote_device_suspend(
struct isci_remote_device *idev)
{
return sci_remote_node_context_suspend(
&idev->rnc,
SCI_SOFTWARE_SUSPENSION,
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
NULL, NULL);
}
enum sci_status isci_remote_device_suspend(
struct isci_host *ihost,
struct isci_remote_device *idev)
{
enum sci_status status;
unsigned long flags;
spin_lock_irqsave(&ihost->scic_lock, flags);
if (isci_lookup_device(idev->domain_dev) == NULL) {
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) {
wait_event(ihost->eventq,
test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
|| !test_bit(IDEV_ALLOCATED, &idev->flags));
status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
? SCI_SUCCESS : SCI_FAILURE;
dev_dbg(&ihost->pdev->dev,
"%s: idev=%p, wait done, device is %s\n",
__func__, idev,
test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
? "<suspended>" : "<deallocated!>");
}
isci_put_device(idev);
}
return status;
}
/* called once the remote node context is ready to be freed. /* called once the remote node context is ready to be freed.
* The remote device can now report that its stop operation is complete. none * The remote device can now report that its stop operation is complete. none
*/ */
...@@ -144,7 +188,9 @@ static void rnc_destruct_done(void *_dev) ...@@ -144,7 +188,9 @@ static void rnc_destruct_done(void *_dev)
sci_change_state(&idev->sm, SCI_DEV_STOPPED); sci_change_state(&idev->sm, SCI_DEV_STOPPED);
} }
static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev) 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; struct isci_host *ihost = idev->owning_port->owning_controller;
enum sci_status status = SCI_SUCCESS; enum sci_status status = SCI_SUCCESS;
...@@ -155,7 +201,9 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d ...@@ -155,7 +201,9 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
enum sci_status s; enum sci_status s;
if (!test_bit(IREQ_ACTIVE, &ireq->flags) || if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
ireq->target_device != idev) (ireq->target_device != idev) ||
(check_abort_pending && !test_bit(IREQ_PENDING_ABORT,
&ireq->flags)))
continue; continue;
s = sci_controller_terminate_request(ihost, idev, ireq); s = sci_controller_terminate_request(ihost, idev, ireq);
...@@ -166,6 +214,12 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d ...@@ -166,6 +214,12 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
return status; return status;
} }
enum sci_status sci_remote_device_terminate_requests(
struct isci_remote_device *idev)
{
return sci_remote_device_terminate_requests_checkabort(idev, 0);
}
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
u32 timeout) u32 timeout)
{ {
...@@ -265,14 +319,6 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev ...@@ -265,14 +319,6 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev
return SCI_SUCCESS; return SCI_SUCCESS;
} }
enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev)
{
return sci_remote_node_context_suspend(&idev->rnc,
SCI_SOFTWARE_SUSPENSION,
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
NULL, NULL);
}
enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev, enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
u32 frame_index) u32 frame_index)
{ {
...@@ -1186,7 +1232,7 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, ...@@ -1186,7 +1232,7 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
* the device when there have been no phys added to it. * the device when there have been no phys added to it.
*/ */
static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,
u32 timeout) u32 timeout)
{ {
struct sci_base_state_machine *sm = &idev->sm; struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id; enum sci_remote_device_states state = sm->current_state_id;
...@@ -1413,3 +1459,41 @@ int isci_remote_device_found(struct domain_device *dev) ...@@ -1413,3 +1459,41 @@ int isci_remote_device_found(struct domain_device *dev)
return status == SCI_SUCCESS ? 0 : -ENODEV; return status == SCI_SUCCESS ? 0 : -ENODEV;
} }
enum sci_status isci_remote_device_reset(
struct isci_remote_device *idev)
{
struct isci_host *ihost = dev_to_ihost(idev->domain_dev);
unsigned long flags;
enum sci_status status;
/* Wait for the device suspend. */
status = isci_remote_device_suspend(ihost, idev);
if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
"%s: isci_remote_device_suspend(%p) returned %d!\n",
__func__, idev, status);
return status;
}
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);
}
return status;
}
int isci_remote_device_is_safe_to_abort(
struct isci_remote_device *idev)
{
return sci_remote_node_context_is_safe_to_abort(&idev->rnc);
}
enum sci_status sci_remote_device_abort_requests_pending_abort(
struct isci_remote_device *idev)
{
return sci_remote_device_terminate_requests_checkabort(idev, 1);
}
...@@ -85,6 +85,7 @@ struct isci_remote_device { ...@@ -85,6 +85,7 @@ struct isci_remote_device {
#define IDEV_GONE 3 #define IDEV_GONE 3
#define IDEV_IO_READY 4 #define IDEV_IO_READY 4
#define IDEV_IO_NCQERROR 5 #define IDEV_IO_NCQERROR 5
#define IDEV_TXRX_SUSPENDED 6
unsigned long flags; unsigned long flags;
struct kref kref; struct kref kref;
struct isci_port *isci_port; struct isci_port *isci_port;
...@@ -335,4 +336,13 @@ void sci_remote_device_post_request( ...@@ -335,4 +336,13 @@ void sci_remote_device_post_request(
struct isci_remote_device *idev, struct isci_remote_device *idev,
u32 request); u32 request);
enum sci_status sci_remote_device_terminate_requests(
struct isci_remote_device *idev);
int isci_remote_device_is_safe_to_abort(
struct isci_remote_device *idev);
enum sci_status
sci_remote_device_abort_requests_pending_abort(
struct isci_remote_device *idev);
#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */
...@@ -268,6 +268,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta ...@@ -268,6 +268,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta
{ {
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
/* Terminate outstanding requests pending abort. */
sci_remote_device_abort_requests_pending_abort(rnc_to_dev(rnc));
sci_remote_node_context_invalidate_context_buffer(rnc); sci_remote_node_context_invalidate_context_buffer(rnc);
} }
...@@ -312,10 +314,28 @@ static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_sta ...@@ -312,10 +314,28 @@ static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_sta
static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm) static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm)
{ {
struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); 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;
set_bit(IDEV_TXRX_SUSPENDED, &idev->flags);
/* Terminate outstanding requests pending abort. */
sci_remote_device_abort_requests_pending_abort(idev);
wake_up(&ihost->eventq);
sci_remote_node_context_continue_state_transitions(rnc); sci_remote_node_context_continue_state_transitions(rnc);
} }
static void sci_remote_node_context_tx_rx_suspended_state_exit(
struct sci_base_state_machine *sm)
{
struct sci_remote_node_context *rnc
= container_of(sm, typeof(*rnc), sm);
struct isci_remote_device *idev = rnc_to_dev(rnc);
clear_bit(IDEV_TXRX_SUSPENDED, &idev->flags);
}
static void sci_remote_node_context_await_suspend_state_exit( static void sci_remote_node_context_await_suspend_state_exit(
struct sci_base_state_machine *sm) struct sci_base_state_machine *sm)
{ {
...@@ -346,6 +366,8 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = { ...@@ -346,6 +366,8 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = {
}, },
[SCI_RNC_TX_RX_SUSPENDED] = { [SCI_RNC_TX_RX_SUSPENDED] = {
.enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
.exit_state
= sci_remote_node_context_tx_rx_suspended_state_exit,
}, },
[SCI_RNC_AWAIT_SUSPENSION] = { [SCI_RNC_AWAIT_SUSPENSION] = {
.exit_state = sci_remote_node_context_await_suspend_state_exit, .exit_state = sci_remote_node_context_await_suspend_state_exit,
...@@ -515,6 +537,13 @@ enum sci_status sci_remote_node_context_suspend( ...@@ -515,6 +537,13 @@ enum sci_status sci_remote_node_context_suspend(
struct isci_remote_device *idev = rnc_to_dev(sci_rnc); struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
enum sci_status status = SCI_FAILURE_INVALID_STATE; enum sci_status status = SCI_FAILURE_INVALID_STATE;
dev_dbg(scirdev_to_dev(idev),
"%s: current state %d, current suspend_type %x dest state %d,"
" arg suspend_reason %d, arg suspend_type %x",
__func__, state, sci_rnc->suspend_type,
sci_rnc->destination_state, suspend_reason,
suspend_type);
/* Disable automatic state continuations if explicitly suspending. */ /* Disable automatic state continuations if explicitly suspending. */
if (suspend_reason == SCI_SOFTWARE_SUSPENSION) if (suspend_reason == SCI_SOFTWARE_SUSPENSION)
sci_rnc->destination_state sci_rnc->destination_state
...@@ -546,7 +575,10 @@ enum sci_status sci_remote_node_context_suspend( ...@@ -546,7 +575,10 @@ enum sci_status sci_remote_node_context_suspend(
sci_rnc->suspend_type = suspend_type; sci_rnc->suspend_type = suspend_type;
if (status == SCI_SUCCESS) { /* Already in the destination state? */ if (status == SCI_SUCCESS) { /* Already in the destination state? */
struct isci_host *ihost = idev->owning_port->owning_controller;
sci_remote_node_context_notify_user(sci_rnc); sci_remote_node_context_notify_user(sci_rnc);
wake_up_all(&ihost->eventq); /* Let observers look. */
return SCI_SUCCESS; return SCI_SUCCESS;
} }
if (suspend_reason == SCI_SOFTWARE_SUSPENSION) { if (suspend_reason == SCI_SOFTWARE_SUSPENSION) {
...@@ -661,3 +693,27 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex ...@@ -661,3 +693,27 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex
return SCI_FAILURE_INVALID_STATE; return SCI_FAILURE_INVALID_STATE;
} }
} }
int sci_remote_node_context_is_safe_to_abort(
struct sci_remote_node_context *sci_rnc)
{
enum scis_sds_remote_node_context_states state;
state = sci_rnc->sm.current_state_id;
switch (state) {
case SCI_RNC_INVALIDATING:
case SCI_RNC_TX_RX_SUSPENDED:
return 1;
case SCI_RNC_POSTING:
case SCI_RNC_RESUMING:
case SCI_RNC_READY:
case SCI_RNC_TX_SUSPENDED:
case SCI_RNC_AWAIT_SUSPENSION:
case SCI_RNC_INITIAL:
return 0;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %d\n", __func__, state);
return 0;
}
}
...@@ -214,5 +214,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex ...@@ -214,5 +214,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex
struct isci_request *ireq); struct isci_request *ireq);
enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc, enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc,
struct isci_request *ireq); struct isci_request *ireq);
int sci_remote_node_context_is_safe_to_abort(
struct sci_remote_node_context *sci_rnc);
#endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */
...@@ -863,6 +863,8 @@ sci_io_request_terminate(struct isci_request *ireq) ...@@ -863,6 +863,8 @@ sci_io_request_terminate(struct isci_request *ireq)
switch (state) { switch (state) {
case SCI_REQ_CONSTRUCTED: case SCI_REQ_CONSTRUCTED:
/* Set to make sure no HW terminate posting is done: */
set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags);
ireq->scu_status = SCU_TASK_DONE_TASK_ABORT; ireq->scu_status = SCU_TASK_DONE_TASK_ABORT;
ireq->sci_status = SCI_FAILURE_IO_TERMINATED; ireq->sci_status = SCI_FAILURE_IO_TERMINATED;
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
...@@ -883,8 +885,7 @@ sci_io_request_terminate(struct isci_request *ireq) ...@@ -883,8 +885,7 @@ sci_io_request_terminate(struct isci_request *ireq)
case SCI_REQ_ATAPI_WAIT_PIO_SETUP: case SCI_REQ_ATAPI_WAIT_PIO_SETUP:
case SCI_REQ_ATAPI_WAIT_D2H: case SCI_REQ_ATAPI_WAIT_D2H:
case SCI_REQ_ATAPI_WAIT_TC_COMP: case SCI_REQ_ATAPI_WAIT_TC_COMP:
sci_change_state(&ireq->sm, SCI_REQ_ABORTING); /* Fall through and change state to ABORTING... */
return SCI_SUCCESS;
case SCI_REQ_TASK_WAIT_TC_RESP: case SCI_REQ_TASK_WAIT_TC_RESP:
/* The task frame was already confirmed to have been /* The task frame was already confirmed to have been
* sent by the SCU HW. Since the state machine is * sent by the SCU HW. Since the state machine is
...@@ -893,20 +894,21 @@ sci_io_request_terminate(struct isci_request *ireq) ...@@ -893,20 +894,21 @@ sci_io_request_terminate(struct isci_request *ireq)
* and don't wait for the task response. * and don't wait for the task response.
*/ */
sci_change_state(&ireq->sm, SCI_REQ_ABORTING); sci_change_state(&ireq->sm, SCI_REQ_ABORTING);
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); /* Fall through and handle like ABORTING... */
return SCI_SUCCESS;
case SCI_REQ_ABORTING: case SCI_REQ_ABORTING:
/* If a request has a termination requested twice, return if (!isci_remote_device_is_safe_to_abort(ireq->target_device))
* a failure indication, since HW confirmation of the first set_bit(IREQ_PENDING_ABORT, &ireq->flags);
* abort is still outstanding. else
clear_bit(IREQ_PENDING_ABORT, &ireq->flags);
/* If the request is only waiting on the remote device
* suspension, return SUCCESS so the caller will wait too.
*/ */
return SCI_SUCCESS;
case SCI_REQ_COMPLETED: case SCI_REQ_COMPLETED:
default: default:
dev_warn(&ireq->owning_controller->pdev->dev, dev_warn(&ireq->owning_controller->pdev->dev,
"%s: SCIC IO Request requested to abort while in wrong " "%s: SCIC IO Request requested to abort while in wrong "
"state %d\n", "state %d\n", __func__, ireq->sm.current_state_id);
__func__,
ireq->sm.current_state_id);
break; break;
} }
......
...@@ -102,6 +102,8 @@ struct isci_request { ...@@ -102,6 +102,8 @@ struct isci_request {
#define IREQ_TERMINATED 1 #define IREQ_TERMINATED 1
#define IREQ_TMF 2 #define IREQ_TMF 2
#define IREQ_ACTIVE 3 #define IREQ_ACTIVE 3
#define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */
#define IREQ_TC_ABORT_POSTED 5
unsigned long flags; unsigned long flags;
/* XXX kill ttype and ttype_ptr, allocate full sas_task */ /* XXX kill ttype and ttype_ptr, allocate full sas_task */
union ttype_ptr_union { union ttype_ptr_union {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment