From 0c3ce38f1bc8b6a6d8df0959e3c0dece31f9350c Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:02 -0800 Subject: 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 Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 2 + drivers/scsi/isci/remote_device.h | 1 + drivers/scsi/isci/remote_node_context.c | 126 ++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 46 deletions(-) diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index acc94a454a1..d1c2a2294a3 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1266,6 +1266,7 @@ enum sci_status isci_remote_device_resume_from_abort( /* Preserve any current resume callbacks, for instance from other * resumptions. */ + clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); status = sci_remote_device_resume(idev, idev->rnc.user_callback, idev->rnc.user_cookie); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1501,6 +1502,7 @@ enum sci_status isci_remote_device_suspend_terminate( /* Put the device into suspension. */ 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); spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index d1d18925fbf..53564c35cf2 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -86,6 +86,7 @@ struct isci_remote_device { #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 #define IDEV_RNC_LLHANG_ENABLED 6 + #define IDEV_ABORT_PATH_ACTIVE 7 unsigned long flags; struct kref kref; struct isci_port *isci_port; diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 77c8b5138b7..faeae9554ee 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -161,6 +161,14 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont 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: @@ -179,10 +187,9 @@ static void sci_remote_node_context_setup_to_resume( { if (sci_rnc->destination_state != RNC_DEST_FINAL) { sci_rnc->destination_state = dest_param; - if (callback != NULL) { - sci_rnc->user_callback = callback; - sci_rnc->user_cookie = callback_parameter; - } + if (callback != NULL) + sci_remote_node_context_save_cbparams( + 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) { 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; - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n", - __func__, rnc_state_name(state), cb_fn, cb_p, - sci_rnc->destination_state); + dev_dbg(scirdev_to_dev(idev), + "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; " + "dev resume path %s\n", + __func__, rnc_state_name(state), cb_fn, cb_p, + sci_rnc->destination_state, + test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags) + ? "" : ""); switch (state) { case SCI_RNC_INITIAL: if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) return SCI_FAILURE_INVALID_STATE; - 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); + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + 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; + case SCI_RNC_POSTING: case SCI_RNC_INVALIDATING: case SCI_RNC_RESUMING: - /* We are still waiting to post when a resume was requested. */ - switch (sci_rnc->destination_state) { - case RNC_DEST_SUSPENDED: - case RNC_DEST_SUSPENDED_RESUME: - /* Previously waiting to suspend after posting. Now - * continue onto resumption. + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else { + /* We are still waiting to post when a resume was + * requested. */ - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); - break; - default: - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_READY); - break; + switch (sci_rnc->destination_state) { + case RNC_DEST_SUSPENDED: + case RNC_DEST_SUSPENDED_RESUME: + /* Previously waiting to suspend after posting. + * Now continue onto resumption. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + 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; + case SCI_RNC_TX_SUSPENDED: - case SCI_RNC_TX_RX_SUSPENDED: { - struct isci_remote_device *idev = rnc_to_dev(sci_rnc); - 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); + case SCI_RNC_TX_RX_SUSPENDED: + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + 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 (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); + 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; - } + case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), -- cgit v1.2.3-70-g09d2