isci: When in the abort path, defeat other resume calls until done.
Completion of I/Os during the one of the abort path interface calls from libsas can drive remote device state changes and the resumption of the device RNC. This is a problem when the abort path is attempting to cleanup outstanding I/O at the same time - the resumption can prevent the termination from occuring correctly. 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:
parent
31a38ef0a5
commit
0c3ce38f1b
3 changed files with 83 additions and 46 deletions
|
@ -1266,6 +1266,7 @@ enum sci_status isci_remote_device_resume_from_abort(
|
||||||
/* Preserve any current resume callbacks, for instance from other
|
/* Preserve any current resume callbacks, for instance from other
|
||||||
* resumptions.
|
* resumptions.
|
||||||
*/
|
*/
|
||||||
|
clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
|
||||||
status = sci_remote_device_resume(idev, idev->rnc.user_callback,
|
status = sci_remote_device_resume(idev, idev->rnc.user_callback,
|
||||||
idev->rnc.user_cookie);
|
idev->rnc.user_cookie);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
@ -1501,6 +1502,7 @@ enum sci_status isci_remote_device_suspend_terminate(
|
||||||
|
|
||||||
/* Put the device into suspension. */
|
/* Put the device into suspension. */
|
||||||
spin_lock_irqsave(&ihost->scic_lock, flags);
|
spin_lock_irqsave(&ihost->scic_lock, flags);
|
||||||
|
set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
|
||||||
sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
|
sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
|
||||||
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ struct isci_remote_device {
|
||||||
#define IDEV_IO_READY 4
|
#define IDEV_IO_READY 4
|
||||||
#define IDEV_IO_NCQERROR 5
|
#define IDEV_IO_NCQERROR 5
|
||||||
#define IDEV_RNC_LLHANG_ENABLED 6
|
#define IDEV_RNC_LLHANG_ENABLED 6
|
||||||
|
#define IDEV_ABORT_PATH_ACTIVE 7
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
struct isci_port *isci_port;
|
struct isci_port *isci_port;
|
||||||
|
|
|
@ -161,6 +161,14 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
|
||||||
rnc->ssp.oaf_more_compatibility_features = 0;
|
rnc->ssp.oaf_more_compatibility_features = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sci_remote_node_context_save_cbparams(
|
||||||
|
struct sci_remote_node_context *sci_rnc,
|
||||||
|
scics_sds_remote_node_context_callback callback,
|
||||||
|
void *callback_parameter)
|
||||||
|
{
|
||||||
|
sci_rnc->user_callback = callback;
|
||||||
|
sci_rnc->user_cookie = callback_parameter;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @sci_rnc:
|
* @sci_rnc:
|
||||||
|
@ -179,10 +187,9 @@ static void sci_remote_node_context_setup_to_resume(
|
||||||
{
|
{
|
||||||
if (sci_rnc->destination_state != RNC_DEST_FINAL) {
|
if (sci_rnc->destination_state != RNC_DEST_FINAL) {
|
||||||
sci_rnc->destination_state = dest_param;
|
sci_rnc->destination_state = dest_param;
|
||||||
if (callback != NULL) {
|
if (callback != NULL)
|
||||||
sci_rnc->user_callback = callback;
|
sci_remote_node_context_save_cbparams(
|
||||||
sci_rnc->user_cookie = callback_parameter;
|
sci_rnc, callback, callback_parameter);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,67 +655,94 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s
|
||||||
void *cb_p)
|
void *cb_p)
|
||||||
{
|
{
|
||||||
enum scis_sds_remote_node_context_states state;
|
enum scis_sds_remote_node_context_states state;
|
||||||
|
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
||||||
|
|
||||||
state = sci_rnc->sm.current_state_id;
|
state = sci_rnc->sm.current_state_id;
|
||||||
dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
dev_dbg(scirdev_to_dev(idev),
|
||||||
"%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n",
|
"%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; "
|
||||||
__func__, rnc_state_name(state), cb_fn, cb_p,
|
"dev resume path %s\n",
|
||||||
sci_rnc->destination_state);
|
__func__, rnc_state_name(state), cb_fn, cb_p,
|
||||||
|
sci_rnc->destination_state,
|
||||||
|
test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)
|
||||||
|
? "<abort active>" : "<normal>");
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case SCI_RNC_INITIAL:
|
case SCI_RNC_INITIAL:
|
||||||
if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
|
if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX)
|
||||||
return SCI_FAILURE_INVALID_STATE;
|
return SCI_FAILURE_INVALID_STATE;
|
||||||
|
|
||||||
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
|
if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags))
|
||||||
RNC_DEST_READY);
|
sci_remote_node_context_save_cbparams(sci_rnc, cb_fn,
|
||||||
sci_remote_node_context_construct_buffer(sci_rnc);
|
cb_p);
|
||||||
sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
|
else {
|
||||||
|
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn,
|
||||||
|
cb_p, RNC_DEST_READY);
|
||||||
|
sci_remote_node_context_construct_buffer(sci_rnc);
|
||||||
|
sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
|
||||||
|
}
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
|
|
||||||
case SCI_RNC_POSTING:
|
case SCI_RNC_POSTING:
|
||||||
case SCI_RNC_INVALIDATING:
|
case SCI_RNC_INVALIDATING:
|
||||||
case SCI_RNC_RESUMING:
|
case SCI_RNC_RESUMING:
|
||||||
/* We are still waiting to post when a resume was requested. */
|
if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags))
|
||||||
switch (sci_rnc->destination_state) {
|
sci_remote_node_context_save_cbparams(sci_rnc, cb_fn,
|
||||||
case RNC_DEST_SUSPENDED:
|
cb_p);
|
||||||
case RNC_DEST_SUSPENDED_RESUME:
|
else {
|
||||||
/* Previously waiting to suspend after posting. Now
|
/* We are still waiting to post when a resume was
|
||||||
* continue onto resumption.
|
* requested.
|
||||||
*/
|
*/
|
||||||
sci_remote_node_context_setup_to_resume(
|
switch (sci_rnc->destination_state) {
|
||||||
sci_rnc, cb_fn, cb_p,
|
case RNC_DEST_SUSPENDED:
|
||||||
RNC_DEST_SUSPENDED_RESUME);
|
case RNC_DEST_SUSPENDED_RESUME:
|
||||||
break;
|
/* Previously waiting to suspend after posting.
|
||||||
default:
|
* Now continue onto resumption.
|
||||||
sci_remote_node_context_setup_to_resume(
|
*/
|
||||||
sci_rnc, cb_fn, cb_p,
|
sci_remote_node_context_setup_to_resume(
|
||||||
RNC_DEST_READY);
|
sci_rnc, cb_fn, cb_p,
|
||||||
break;
|
RNC_DEST_SUSPENDED_RESUME);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sci_remote_node_context_setup_to_resume(
|
||||||
|
sci_rnc, cb_fn, cb_p,
|
||||||
|
RNC_DEST_READY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
|
|
||||||
case SCI_RNC_TX_SUSPENDED:
|
case SCI_RNC_TX_SUSPENDED:
|
||||||
case SCI_RNC_TX_RX_SUSPENDED: {
|
case SCI_RNC_TX_RX_SUSPENDED:
|
||||||
struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
|
if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags))
|
||||||
struct domain_device *dev = idev->domain_dev;
|
sci_remote_node_context_save_cbparams(sci_rnc, cb_fn,
|
||||||
|
cb_p);
|
||||||
|
else {
|
||||||
|
struct domain_device *dev = idev->domain_dev;
|
||||||
|
/* If this is an expander attached SATA device we must
|
||||||
|
* invalidate and repost the RNC since this is the only
|
||||||
|
* way to clear the TCi to NCQ tag mapping table for
|
||||||
|
* the RNi. All other device types we can just resume.
|
||||||
|
*/
|
||||||
|
sci_remote_node_context_setup_to_resume(
|
||||||
|
sci_rnc, cb_fn, cb_p, RNC_DEST_READY);
|
||||||
|
|
||||||
/* If this is an expander attached SATA device we must
|
if (dev_is_sata(dev) && dev->parent)
|
||||||
* invalidate and repost the RNC since this is the only way
|
sci_change_state(&sci_rnc->sm,
|
||||||
* to clear the TCi to NCQ tag mapping table for the RNi.
|
SCI_RNC_INVALIDATING);
|
||||||
* All other device types we can just resume.
|
else
|
||||||
*/
|
sci_change_state(&sci_rnc->sm,
|
||||||
sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
|
SCI_RNC_RESUMING);
|
||||||
RNC_DEST_READY);
|
}
|
||||||
|
|
||||||
if (dev_is_sata(dev) && dev->parent)
|
|
||||||
sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
|
|
||||||
else
|
|
||||||
sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
|
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
}
|
|
||||||
case SCI_RNC_AWAIT_SUSPENSION:
|
case SCI_RNC_AWAIT_SUSPENSION:
|
||||||
sci_remote_node_context_setup_to_resume(
|
if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags))
|
||||||
sci_rnc, cb_fn, cb_p,
|
sci_remote_node_context_save_cbparams(sci_rnc, cb_fn,
|
||||||
RNC_DEST_SUSPENDED_RESUME);
|
cb_p);
|
||||||
|
else
|
||||||
|
sci_remote_node_context_setup_to_resume(
|
||||||
|
sci_rnc, cb_fn, cb_p,
|
||||||
|
RNC_DEST_SUSPENDED_RESUME);
|
||||||
return SCI_SUCCESS;
|
return SCI_SUCCESS;
|
||||||
default:
|
default:
|
||||||
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
|
||||||
|
|
Reference in a new issue