isci: Handle all suspending TC completions

Add comprehensive decode for all TC completions that generate RNC
suspensions.

Note that this commit also removes unconditional resumptions of ATAPI
devices when in the SCI_STP_DEV_ATAPI_ERROR state, and STP devices
when in the SCI_STP_DEV_IDLE state. This is because the SCI_STP_DEV_IDLE
and SCI_STP_DEV_ATAPI state entry functions manage the RNC resumption.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
Jeff Skirvin 2012-03-08 22:41:50 -08:00 committed by Dan Williams
parent 56d7c013e7
commit ac78ed0f78
7 changed files with 242 additions and 87 deletions

View file

@ -265,20 +265,12 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev
return SCI_SUCCESS;
}
enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
u32 suspend_type)
enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev)
{
struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id;
if (state != SCI_STP_DEV_CMD) {
dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
__func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
return sci_remote_node_context_suspend(&idev->rnc,
suspend_type, NULL, NULL);
SCI_SOFTWARE_SUSPENSION,
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
NULL, NULL);
}
enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
@ -412,8 +404,6 @@ static void atapi_remote_device_resume_done(void *_dev)
enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
u32 event_code)
{
struct sci_base_state_machine *sm = &idev->sm;
enum sci_remote_device_states state = sm->current_state_id;
enum sci_status status;
switch (scu_get_event_type(event_code)) {
@ -427,9 +417,11 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
status = SCI_SUCCESS;
/* Suspend the associated RNC */
sci_remote_node_context_suspend(&idev->rnc,
SCI_SOFTWARE_SUSPENSION,
NULL, NULL);
sci_remote_node_context_suspend(
&idev->rnc,
SCI_SOFTWARE_SUSPENSION,
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
NULL, NULL);
dev_dbg(scirdev_to_dev(idev),
"%s: device: %p event code: %x: %s\n",
@ -455,26 +447,6 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
if (status != SCI_SUCCESS)
return status;
if (state == SCI_STP_DEV_ATAPI_ERROR) {
/* For ATAPI error state resume the RNC right away. */
if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) {
return sci_remote_node_context_resume(&idev->rnc,
atapi_remote_device_resume_done,
idev);
}
}
if (state == SCI_STP_DEV_IDLE) {
/* We pick up suspension events to handle specifically to this
* state. We resume the RNC right away.
*/
if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX ||
scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX)
status = sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
}
return status;
}
@ -765,11 +737,11 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
* the correct action when the remote node context is suspended
* and later resumed.
*/
sci_remote_node_context_suspend(&idev->rnc,
SCI_SOFTWARE_SUSPENSION, NULL, NULL);
sci_remote_node_context_resume(&idev->rnc,
sci_remote_device_continue_request,
idev);
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);
out:
sci_remote_device_start_request(idev, ireq, status);
@ -954,14 +926,23 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm
static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
struct isci_host *ihost = idev->owning_port->owning_controller;
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p\n", __func__, idev);
sci_remote_node_context_suspend(
&idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL);
&idev->rnc, SCI_SOFTWARE_SUSPENSION,
SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL);
}
static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
struct isci_host *ihost = idev->owning_port->owning_controller;
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p\n", __func__, idev);
sci_remote_node_context_resume(&idev->rnc, NULL, NULL);
}
@ -1004,6 +985,21 @@ static void sci_stp_remote_device_ready_ncq_error_substate_enter(struct sci_base
idev->not_ready_reason);
}
static void sci_stp_remote_device_atapi_error_substate_enter(
struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
/* This state is entered when an I/O is decoded with an error
* condition. By this point the RNC expected suspension state is set.
* The error conditions suspend the device, so unsuspend here if
* possible.
*/
sci_remote_node_context_resume(&idev->rnc,
atapi_remote_device_resume_done,
idev);
}
static void sci_smp_remote_device_ready_idle_substate_enter(struct sci_base_state_machine *sm)
{
struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm);
@ -1054,7 +1050,9 @@ static const struct sci_base_state sci_remote_device_state_table[] = {
[SCI_STP_DEV_NCQ_ERROR] = {
.enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter,
},
[SCI_STP_DEV_ATAPI_ERROR] = { },
[SCI_STP_DEV_ATAPI_ERROR] = {
.enter_state = sci_stp_remote_device_atapi_error_substate_enter,
},
[SCI_STP_DEV_AWAIT_RESET] = { },
[SCI_SMP_DEV_IDLE] = {
.enter_state = sci_smp_remote_device_ready_idle_substate_enter,

View file

@ -331,10 +331,6 @@ enum sci_status sci_remote_device_complete_io(
struct isci_remote_device *idev,
struct isci_request *ireq);
enum sci_status sci_remote_device_suspend(
struct isci_remote_device *idev,
u32 suspend_type);
void sci_remote_device_post_request(
struct isci_remote_device *idev,
u32 request);

View file

@ -367,6 +367,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
u32 event_code)
{
enum scis_sds_remote_node_context_states state;
u32 next_state;
state = sci_rnc->sm.current_state_id;
switch (state) {
@ -425,11 +426,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TL_RNC_SUSPEND_TX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
sci_rnc->suspend_type = scu_get_event_type(event_code);
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
sci_rnc->suspend_type = scu_get_event_type(event_code);
break;
default:
goto out;
@ -438,16 +439,16 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
case SCI_RNC_AWAIT_SUSPENSION:
switch (scu_get_event_type(event_code)) {
case SCU_EVENT_TL_RNC_SUSPEND_TX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
next_state = SCI_RNC_TX_SUSPENDED;
break;
case SCU_EVENT_TL_RNC_SUSPEND_TX_RX:
sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED);
sci_rnc->suspension_code = scu_get_event_specifier(event_code);
next_state = SCI_RNC_TX_RX_SUSPENDED;
break;
default:
goto out;
}
if (sci_rnc->suspend_type == scu_get_event_type(event_code))
sci_change_state(&sci_rnc->sm, next_state);
break;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
@ -502,33 +503,60 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
}
}
enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
u32 suspend_type,
scics_sds_remote_node_context_callback cb_fn,
void *cb_p)
enum sci_status sci_remote_node_context_suspend(
struct sci_remote_node_context *sci_rnc,
enum sci_remote_node_suspension_reasons suspend_reason,
u32 suspend_type,
scics_sds_remote_node_context_callback cb_fn,
void *cb_p)
{
enum scis_sds_remote_node_context_states state;
enum scis_sds_remote_node_context_states state
= sci_rnc->sm.current_state_id;
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
enum sci_status status = SCI_FAILURE_INVALID_STATE;
state = sci_rnc->sm.current_state_id;
if (state != SCI_RNC_READY) {
/* Disable automatic state continuations if explicitly suspending. */
if (suspend_reason == SCI_SOFTWARE_SUSPENSION)
sci_rnc->destination_state
= SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
switch (state) {
case SCI_RNC_READY:
break;
case SCI_RNC_TX_SUSPENDED:
if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX)
status = SCI_SUCCESS;
break;
case SCI_RNC_TX_RX_SUSPENDED:
if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
status = SCI_SUCCESS;
break;
case SCI_RNC_AWAIT_SUSPENSION:
if ((sci_rnc->suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX)
|| (suspend_type == sci_rnc->suspend_type))
return SCI_SUCCESS;
break;
default:
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
"%s: invalid state %s\n", __func__,
rnc_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
sci_rnc->user_callback = cb_fn;
sci_rnc->user_cookie = cb_p;
sci_rnc->suspend_type = suspend_type;
sci_rnc->user_callback = cb_fn;
sci_rnc->user_cookie = cb_p;
sci_rnc->suspension_code = suspend_type;
if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
sci_remote_device_post_request(rnc_to_dev(sci_rnc),
SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
isci_dev_set_hang_detection_timeout(rnc_to_dev(sci_rnc),
0x00000001);
if (status == SCI_SUCCESS) { /* Already in the destination state? */
sci_remote_node_context_notify_user(sci_rnc);
return SCI_SUCCESS;
}
if (suspend_reason == SCI_SOFTWARE_SUSPENSION) {
isci_dev_set_hang_detection_timeout(idev, 0x00000001);
sci_remote_device_post_request(
idev, SCI_SOFTWARE_SUSPEND_CMD);
}
if (state != SCI_RNC_AWAIT_SUSPENSION)
sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);
sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);
return SCI_SUCCESS;
}

View file

@ -75,8 +75,12 @@
*/
#define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF
#define SCU_HARDWARE_SUSPENSION (0)
#define SCI_SOFTWARE_SUSPENSION (1)
enum sci_remote_node_suspension_reasons {
SCU_HARDWARE_SUSPENSION,
SCI_SOFTWARE_SUSPENSION
};
#define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX
#define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX
struct isci_request;
struct isci_remote_device;
@ -156,10 +160,10 @@ struct sci_remote_node_context {
u16 remote_node_index;
/**
* This field is the recored suspension code or the reason for the remote node
* This field is the recored suspension type of the remote node
* context suspension.
*/
u32 suspension_code;
u32 suspend_type;
/**
* This field is true if the remote node context is resuming from its current
@ -200,6 +204,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
void *callback_parameter);
enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc,
u32 suspend_type,
u32 suspension_code,
scics_sds_remote_node_context_callback cb_fn,
void *cb_p);
enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc,

View file

@ -2116,9 +2116,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
* completion.
*/
if (ireq->stp.rsp.fis_type == FIS_REGD2H) {
sci_remote_device_suspend(ireq->target_device,
SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)));
ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
@ -2138,13 +2135,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
/* TODO We can retry the command for SCU_TASK_DONE_CMD_LL_R_ERR
* - this comes only for B0
*/
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_INV_FIS_LEN):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR):
case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CMD_LL_R_ERR):
sci_remote_device_suspend(ireq->target_device,
SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)));
/* Fall through to the default case */
default:
/* All other completion status cause the IO to be complete. */
ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
@ -2262,15 +2252,152 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire
return status;
}
static int sci_request_smp_completion_status_is_tx_suspend(
unsigned int completion_status)
{
switch (completion_status) {
case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
return 1;
}
return 0;
}
static int sci_request_smp_completion_status_is_tx_rx_suspend(
unsigned int completion_status)
{
return 0; /* There are no Tx/Rx SMP suspend conditions. */
}
static int sci_request_ssp_completion_status_is_tx_suspend(
unsigned int completion_status)
{
switch (completion_status) {
case SCU_TASK_DONE_TX_RAW_CMD_ERR:
case SCU_TASK_DONE_LF_ERR:
case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
return 1;
}
return 0;
}
static int sci_request_ssp_completion_status_is_tx_rx_suspend(
unsigned int completion_status)
{
return 0; /* There are no Tx/Rx SSP suspend conditions. */
}
static int sci_request_stpsata_completion_status_is_tx_suspend(
unsigned int completion_status)
{
switch (completion_status) {
case SCU_TASK_DONE_TX_RAW_CMD_ERR:
case SCU_TASK_DONE_LL_R_ERR:
case SCU_TASK_DONE_LL_PERR:
case SCU_TASK_DONE_REG_ERR:
case SCU_TASK_DONE_SDB_ERR:
case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2:
case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3:
case SCU_TASK_OPEN_REJECT_BAD_DESTINATION:
case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION:
case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY:
case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED:
case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED:
return 1;
}
return 0;
}
static int sci_request_stpsata_completion_status_is_tx_rx_suspend(
unsigned int completion_status)
{
switch (completion_status) {
case SCU_TASK_DONE_LF_ERR:
case SCU_TASK_DONE_LL_SY_TERM:
case SCU_TASK_DONE_LL_LF_TERM:
case SCU_TASK_DONE_BREAK_RCVD:
case SCU_TASK_DONE_INV_FIS_LEN:
case SCU_TASK_DONE_UNEXP_FIS:
case SCU_TASK_DONE_UNEXP_SDBFIS:
case SCU_TASK_DONE_MAX_PLD_ERR:
return 1;
}
return 0;
}
static void sci_request_handle_suspending_completions(
struct isci_request *ireq,
u32 completion_code)
{
int is_tx = 0;
int is_tx_rx = 0;
switch (ireq->protocol) {
case SAS_PROTOCOL_SMP:
is_tx = sci_request_smp_completion_status_is_tx_suspend(
completion_code);
is_tx_rx = sci_request_smp_completion_status_is_tx_rx_suspend(
completion_code);
break;
case SAS_PROTOCOL_SSP:
is_tx = sci_request_ssp_completion_status_is_tx_suspend(
completion_code);
is_tx_rx = sci_request_ssp_completion_status_is_tx_rx_suspend(
completion_code);
break;
case SAS_PROTOCOL_STP:
is_tx = sci_request_stpsata_completion_status_is_tx_suspend(
completion_code);
is_tx_rx =
sci_request_stpsata_completion_status_is_tx_rx_suspend(
completion_code);
break;
default:
dev_warn(&ireq->isci_host->pdev->dev,
"%s: request %p has no valid protocol\n",
__func__, ireq);
break;
}
if (is_tx || is_tx_rx) {
BUG_ON(is_tx && is_tx_rx);
sci_remote_node_context_suspend(
&ireq->target_device->rnc,
SCU_HARDWARE_SUSPENSION,
(is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX
: SCU_EVENT_TL_RNC_SUSPEND_TX,
NULL, NULL);
}
}
enum sci_status
sci_io_request_tc_completion(struct isci_request *ireq,
u32 completion_code)
u32 completion_code)
{
enum sci_base_request_states state;
struct isci_host *ihost = ireq->owning_controller;
state = ireq->sm.current_state_id;
/* Decode those completions that signal upcoming suspension events. */
sci_request_handle_suspending_completions(
ireq, SCU_GET_COMPLETION_TL_STATUS(completion_code));
switch (state) {
case SCI_REQ_STARTED:
return request_started_state_tc_event(ireq, completion_code);

View file

@ -414,5 +414,4 @@ static inline int isci_task_is_ncq_recovery(struct sas_task *task)
task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ);
}
#endif /* !defined(_ISCI_REQUEST_H_) */

View file

@ -224,6 +224,7 @@
* 32-bit value like we want, each immediate value must be cast to a u32.
*/
#define SCU_TASK_DONE_GOOD ((u32)0x00)
#define SCU_TASK_DONE_TX_RAW_CMD_ERR ((u32)0x08)
#define SCU_TASK_DONE_CRC_ERR ((u32)0x14)
#define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14)
#define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15)
@ -237,6 +238,7 @@
#define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A)
#define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A)
#define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B)
#define SCU_TASK_DONE_BREAK_RCVD ((u32)0x1B)
#define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B)
#define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C)
#define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C)