Merge tag 'soc-for-linus-3' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[linux-2.6-block.git] / drivers / scsi / isci / remote_node_context.c
index 3a9463481f3810f4bf0c8daa85451ccb0994b696..1910100638a280a5c70e1f2118e354d6ecfb1a43 100644 (file)
@@ -52,7 +52,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
+#include <scsi/sas_ata.h>
 #include "host.h"
 #include "isci.h"
 #include "remote_device.h"
@@ -90,6 +90,15 @@ bool sci_remote_node_context_is_ready(
        return false;
 }
 
+bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc)
+{
+       u32 current_state = sci_rnc->sm.current_state_id;
+
+       if (current_state == SCI_RNC_TX_RX_SUSPENDED)
+               return true;
+       return false;
+}
+
 static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id)
 {
        if (id < ihost->remote_node_entries &&
@@ -131,7 +140,7 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
 
        rnc->ssp.arbitration_wait_time = 0;
 
-       if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
+       if (dev_is_sata(dev)) {
                rnc->ssp.connection_occupancy_timeout =
                        ihost->user_parameters.stp_max_occupancy_timeout;
                rnc->ssp.connection_inactivity_timeout =
@@ -151,7 +160,6 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
        rnc->ssp.oaf_source_zone_group = 0;
        rnc->ssp.oaf_more_compatibility_features = 0;
 }
-
 /**
  *
  * @sci_rnc:
@@ -165,23 +173,30 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont
 static void sci_remote_node_context_setup_to_resume(
        struct sci_remote_node_context *sci_rnc,
        scics_sds_remote_node_context_callback callback,
-       void *callback_parameter)
+       void *callback_parameter,
+       enum sci_remote_node_context_destination_state dest_param)
 {
-       if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) {
-               sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY;
-               sci_rnc->user_callback     = callback;
-               sci_rnc->user_cookie       = callback_parameter;
+       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;
+               }
        }
 }
 
-static void sci_remote_node_context_setup_to_destory(
+static void sci_remote_node_context_setup_to_destroy(
        struct sci_remote_node_context *sci_rnc,
        scics_sds_remote_node_context_callback callback,
        void *callback_parameter)
 {
-       sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL;
+       struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc));
+
+       sci_rnc->destination_state = RNC_DEST_FINAL;
        sci_rnc->user_callback     = callback;
        sci_rnc->user_cookie       = callback_parameter;
+
+       wake_up(&ihost->eventq);
 }
 
 /**
@@ -203,9 +218,19 @@ static void sci_remote_node_context_notify_user(
 
 static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc)
 {
-       if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
+       switch (rnc->destination_state) {
+       case RNC_DEST_READY:
+       case RNC_DEST_SUSPENDED_RESUME:
+               rnc->destination_state = RNC_DEST_READY;
+               /* Fall through... */
+       case RNC_DEST_FINAL:
                sci_remote_node_context_resume(rnc, rnc->user_callback,
-                                                   rnc->user_cookie);
+                                              rnc->user_cookie);
+               break;
+       default:
+               rnc->destination_state = RNC_DEST_UNSPECIFIED;
+               break;
+       }
 }
 
 static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc)
@@ -219,13 +244,12 @@ static void sci_remote_node_context_validate_context_buffer(struct sci_remote_no
 
        rnc_buffer->ssp.is_valid = true;
 
-       if (!idev->is_direct_attached &&
-           (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) {
+       if (dev_is_sata(dev) && dev->parent) {
                sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96);
        } else {
                sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32);
 
-               if (idev->is_direct_attached)
+               if (!dev->parent)
                        sci_port_setup_transports(idev->owning_port,
                                                  sci_rnc->remote_node_index);
        }
@@ -248,13 +272,18 @@ static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_
 static void sci_remote_node_context_initial_state_enter(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);
+       struct isci_host *ihost = idev->owning_port->owning_controller;
 
        /* Check to see if we have gotten back to the initial state because
         * someone requested to destroy the remote node context object.
         */
        if (sm->previous_state_id == SCI_RNC_INVALIDATING) {
-               rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+               rnc->destination_state = RNC_DEST_UNSPECIFIED;
                sci_remote_node_context_notify_user(rnc);
+
+               smp_wmb();
+               wake_up(&ihost->eventq);
        }
 }
 
@@ -269,6 +298,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);
 
+       /* Terminate all outstanding requests. */
+       sci_remote_device_terminate_requests(rnc_to_dev(rnc));
        sci_remote_node_context_invalidate_context_buffer(rnc);
 }
 
@@ -287,10 +318,8 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m
         * resume because of a target reset we also need to update
         * the STPTLDARNI register with the RNi of the device
         */
-       if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) &&
-           idev->is_direct_attached)
-               sci_port_setup_transports(idev->owning_port,
-                                              rnc->remote_node_index);
+       if (dev_is_sata(dev) && !dev->parent)
+               sci_port_setup_transports(idev->owning_port, rnc->remote_node_index);
 
        sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME);
 }
@@ -298,10 +327,22 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m
 static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm)
 {
        struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm);
+       enum sci_remote_node_context_destination_state dest_select;
+       int tell_user = 1;
+
+       dest_select = rnc->destination_state;
+       rnc->destination_state = RNC_DEST_UNSPECIFIED;
 
-       rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+       if ((dest_select == RNC_DEST_SUSPENDED) ||
+           (dest_select == RNC_DEST_SUSPENDED_RESUME)) {
+               sci_remote_node_context_suspend(
+                       rnc, rnc->suspend_reason,
+                       SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);
 
-       if (rnc->user_callback)
+               if (dest_select == RNC_DEST_SUSPENDED_RESUME)
+                       tell_user = 0;  /* Wait until ready again. */
+       }
+       if (tell_user)
                sci_remote_node_context_notify_user(rnc);
 }
 
@@ -315,10 +356,34 @@ 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)
 {
        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;
+       u32 new_count = rnc->suspend_count + 1;
+
+       if (new_count == 0)
+               rnc->suspend_count = 1;
+       else
+               rnc->suspend_count = new_count;
+       smp_wmb();
 
+       /* 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);
 }
 
+static void sci_remote_node_context_await_suspend_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);
+
+       if (dev_is_sata(idev->domain_dev))
+               isci_dev_set_hang_detection_timeout(idev, 0);
+}
+
 static const struct sci_base_state sci_remote_node_context_state_table[] = {
        [SCI_RNC_INITIAL] = {
                .enter_state = sci_remote_node_context_initial_state_enter,
@@ -341,7 +406,9 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = {
        [SCI_RNC_TX_RX_SUSPENDED] = {
                .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter,
        },
-       [SCI_RNC_AWAIT_SUSPENSION] = { },
+       [SCI_RNC_AWAIT_SUSPENSION] = {
+               .exit_state = sci_remote_node_context_await_suspend_state_exit,
+       },
 };
 
 void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
@@ -350,7 +417,7 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc,
        memset(rnc, 0, sizeof(struct sci_remote_node_context));
 
        rnc->remote_node_index = remote_node_index;
-       rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
+       rnc->destination_state = RNC_DEST_UNSPECIFIED;
 
        sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL);
 }
@@ -359,6 +426,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) {
@@ -373,18 +441,18 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
                break;
        case SCI_RNC_INVALIDATING:
                if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) {
-                       if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL)
-                               state = SCI_RNC_INITIAL;
+                       if (sci_rnc->destination_state == RNC_DEST_FINAL)
+                               next_state = SCI_RNC_INITIAL;
                        else
-                               state = SCI_RNC_POSTING;
-                       sci_change_state(&sci_rnc->sm, state);
+                               next_state = SCI_RNC_POSTING;
+                       sci_change_state(&sci_rnc->sm, next_state);
                } else {
                        switch (scu_get_event_type(event_code)) {
                        case SCU_EVENT_TYPE_RNC_SUSPEND_TX:
                        case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
                                /* We really dont care if the hardware is going to suspend
                                 * the device since it's being invalidated anyway */
-                               dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+                               dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
                                        "%s: SCIC Remote Node Context 0x%p was "
                                        "suspeneded by hardware while being "
                                        "invalidated.\n", __func__, sci_rnc);
@@ -403,7 +471,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con
                        case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX:
                                /* We really dont care if the hardware is going to suspend
                                 * the device since it's being resumed anyway */
-                               dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+                               dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
                                        "%s: SCIC Remote Node Context 0x%p was "
                                        "suspeneded by hardware while being resumed.\n",
                                        __func__, sci_rnc);
@@ -417,11 +485,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;
@@ -430,27 +498,29 @@ 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)),
-                        "%s: invalid state %d\n", __func__, state);
+                        "%s: invalid state: %s\n", __func__,
+                        rnc_state_name(state));
                return SCI_FAILURE_INVALID_STATE;
        }
        return SCI_SUCCESS;
 
  out:
        dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-                "%s: code: %#x state: %d\n", __func__, event_code, state);
+                "%s: code: %#x state: %s\n", __func__, event_code,
+                rnc_state_name(state));
        return SCI_FAILURE;
 
 }
@@ -464,20 +534,23 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
        state = sci_rnc->sm.current_state_id;
        switch (state) {
        case SCI_RNC_INVALIDATING:
-               sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+               sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
                return SCI_SUCCESS;
        case SCI_RNC_POSTING:
        case SCI_RNC_RESUMING:
        case SCI_RNC_READY:
        case SCI_RNC_TX_SUSPENDED:
        case SCI_RNC_TX_RX_SUSPENDED:
-       case SCI_RNC_AWAIT_SUSPENSION:
-               sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p);
+               sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
                sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
                return SCI_SUCCESS;
+       case SCI_RNC_AWAIT_SUSPENSION:
+               sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p);
+               return SCI_SUCCESS;
        case SCI_RNC_INITIAL:
                dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-                        "%s: invalid state %d\n", __func__, state);
+                        "%s: invalid state: %s\n", __func__,
+                        rnc_state_name(state));
                /* We have decided that the destruct request on the remote node context
                 * can not fail since it is either in the initial/destroyed state or is
                 * can be destroyed.
@@ -485,35 +558,101 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context
                return SCI_SUCCESS;
        default:
                dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-                        "%s: invalid state %d\n", __func__, state);
+                        "%s: invalid state %s\n", __func__,
+                        rnc_state_name(state));
                return SCI_FAILURE_INVALID_STATE;
        }
 }
 
-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)
 {
-       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;
+       enum sci_remote_node_context_destination_state dest_param =
+               RNC_DEST_UNSPECIFIED;
+
+       dev_dbg(scirdev_to_dev(idev),
+               "%s: current state %s, current suspend_type %x dest state %d,"
+                       " arg suspend_reason %d, arg suspend_type %x",
+               __func__, rnc_state_name(state), sci_rnc->suspend_type,
+               sci_rnc->destination_state, suspend_reason,
+               suspend_type);
+
+       /* Disable automatic state continuations if explicitly suspending. */
+       if ((suspend_reason == SCI_HW_SUSPEND) ||
+           (sci_rnc->destination_state == RNC_DEST_FINAL))
+               dest_param = sci_rnc->destination_state;
 
-       state = sci_rnc->sm.current_state_id;
-       if (state != SCI_RNC_READY) {
+       switch (state) {
+       case SCI_RNC_READY:
+               break;
+       case SCI_RNC_INVALIDATING:
+               if (sci_rnc->destination_state == RNC_DEST_FINAL) {
+                       dev_warn(scirdev_to_dev(idev),
+                                "%s: already destroying %p\n",
+                                __func__, sci_rnc);
+                       return SCI_FAILURE_INVALID_STATE;
+               }
+               /* Fall through and handle like SCI_RNC_POSTING */
+       case SCI_RNC_RESUMING:
+               /* Fall through and handle like SCI_RNC_POSTING */
+       case SCI_RNC_POSTING:
+               /* Set the destination state to AWAIT - this signals the
+                * entry into the SCI_RNC_READY state that a suspension
+                * needs to be done immediately.
+                */
+               if (sci_rnc->destination_state != RNC_DEST_FINAL)
+                       sci_rnc->destination_state = RNC_DEST_SUSPENDED;
+               sci_rnc->suspend_type = suspend_type;
+               sci_rnc->suspend_reason = suspend_reason;
+               return SCI_SUCCESS;
+
+       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 %d\n", __func__, state);
+                        "%s: invalid state %s\n", __func__,
+                        rnc_state_name(state));
                return SCI_FAILURE_INVALID_STATE;
        }
+       sci_rnc->destination_state = dest_param;
+       sci_rnc->suspend_type = suspend_type;
+       sci_rnc->suspend_reason = suspend_reason;
+
+       if (status == SCI_SUCCESS) { /* Already in the destination state? */
+               struct isci_host *ihost = idev->owning_port->owning_controller;
+
+               wake_up_all(&ihost->eventq); /* Let observers look. */
+               return SCI_SUCCESS;
+       }
+       if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) ||
+           (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) {
 
-       sci_rnc->user_callback   = cb_fn;
-       sci_rnc->user_cookie     = cb_p;
-       sci_rnc->suspension_code = suspend_type;
+               if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)
+                       isci_dev_set_hang_detection_timeout(idev, 0x00000001);
 
-       if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
-               sci_remote_device_post_request(rnc_to_dev(sci_rnc),
-                                                   SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
+               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;
 }
 
@@ -522,56 +661,86 @@ 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(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)
+                       ? "<abort active>" : "<normal>");
+
        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);
-               sci_remote_node_context_construct_buffer(sci_rnc);
-               sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING);
+               sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p,
+                                                       RNC_DEST_READY);
+               if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
+                       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:
-               if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY)
-                       return SCI_FAILURE_INVALID_STATE;
-
-               sci_rnc->user_callback = cb_fn;
-               sci_rnc->user_cookie   = cb_p;
+               /* 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.
+                        */
+                       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: {
-               struct isci_remote_device *idev = rnc_to_dev(sci_rnc);
-               struct domain_device *dev = idev->domain_dev;
-
-               sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
-
-               /* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */
-               if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev))
-                       sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
-               else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
-                       if (idev->is_direct_attached) {
-                               /* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */
-                               sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
-                       } else {
-                               sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING);
+
+       case SCI_RNC_TX_SUSPENDED:
+       case SCI_RNC_TX_RX_SUSPENDED:
+               {
+                       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 (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) {
+                               if ((dev_is_sata(dev) && dev->parent) ||
+                                   (sci_rnc->destination_state == RNC_DEST_FINAL))
+                                       sci_change_state(&sci_rnc->sm,
+                                                        SCI_RNC_INVALIDATING);
+                               else
+                                       sci_change_state(&sci_rnc->sm,
+                                                        SCI_RNC_RESUMING);
                        }
-               } else
-                       return SCI_FAILURE;
+               }
                return SCI_SUCCESS;
-       }
-       case SCI_RNC_TX_RX_SUSPENDED:
-               sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
-               sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING);
-               return SCI_FAILURE_INVALID_STATE;
+
        case SCI_RNC_AWAIT_SUSPENSION:
-               sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p);
+               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)),
-                        "%s: invalid state %d\n", __func__, state);
+                        "%s: invalid state %s\n", __func__,
+                        rnc_state_name(state));
                return SCI_FAILURE_INVALID_STATE;
        }
 }
@@ -590,35 +759,51 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context
        case SCI_RNC_TX_RX_SUSPENDED:
        case SCI_RNC_AWAIT_SUSPENSION:
                dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-                        "%s: invalid state %d\n", __func__, state);
+                        "%s: invalid state %s\n", __func__,
+                        rnc_state_name(state));
                return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED;
        default:
-               break;
+               dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+                       "%s: invalid state %s\n", __func__,
+                       rnc_state_name(state));
+               return SCI_FAILURE_INVALID_STATE;
        }
-       dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)),
-               "%s: requested to start IO while still resuming, %d\n",
-               __func__, state);
-       return SCI_FAILURE_INVALID_STATE;
 }
 
-enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc,
-                                                       struct isci_request *ireq)
+enum sci_status sci_remote_node_context_start_task(
+       struct sci_remote_node_context *sci_rnc,
+       struct isci_request *ireq,
+       scics_sds_remote_node_context_callback cb_fn,
+       void *cb_p)
+{
+       enum sci_status status = sci_remote_node_context_resume(sci_rnc,
+                                                               cb_fn, cb_p);
+       if (status != SCI_SUCCESS)
+               dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)),
+                       "%s: resume failed: %d\n", __func__, status);
+       return status;
+}
+
+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_AWAIT_SUSPENSION:
-               return SCI_SUCCESS;
        case SCI_RNC_TX_SUSPENDED:
-       case SCI_RNC_TX_RX_SUSPENDED:
-               sci_remote_node_context_resume(sci_rnc, NULL, NULL);
-               return SCI_SUCCESS;
+       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 SCI_FAILURE_INVALID_STATE;
+               return 0;
        }
 }