]> Pileus Git - ~andy/linux/blobdiff - drivers/scsi/isci/remote_device.c
Merge remote-tracking branch 'regulator/fix/doc' into tmp
[~andy/linux] / drivers / scsi / isci / remote_device.c
index 8f501b0a81d6e7842a9d9caaaae1d228b996f2ab..c3aa6c5457b9c386e7237db6201dfb1f8603b83c 100644 (file)
@@ -72,46 +72,11 @@ const char *dev_state_name(enum sci_remote_device_states state)
 }
 #undef C
 
-/**
- * isci_remote_device_not_ready() - This function is called by the ihost when
- *    the remote device is not ready. We mark the isci device as ready (not
- *    "ready_for_io") and signal the waiting proccess.
- * @isci_host: This parameter specifies the isci host object.
- * @isci_device: This parameter specifies the remote device
- *
- * sci_lock is held on entrance to this function.
- */
-static void isci_remote_device_not_ready(struct isci_host *ihost,
-                                 struct isci_remote_device *idev, u32 reason)
+enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
+                                         enum sci_remote_node_suspension_reasons reason)
 {
-       struct isci_request *ireq;
-
-       dev_dbg(&ihost->pdev->dev,
-               "%s: isci_device = %p\n", __func__, idev);
-
-       switch (reason) {
-       case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED:
-               set_bit(IDEV_GONE, &idev->flags);
-               break;
-       case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
-               set_bit(IDEV_IO_NCQERROR, &idev->flags);
-
-               /* Kill all outstanding requests for the device. */
-               list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) {
-
-                       dev_dbg(&ihost->pdev->dev,
-                               "%s: isci_device = %p request = %p\n",
-                               __func__, idev, ireq);
-
-                       sci_controller_terminate_request(ihost,
-                                                         idev,
-                                                         ireq);
-               }
-               /* Fall through into the default case... */
-       default:
-               clear_bit(IDEV_IO_READY, &idev->flags);
-               break;
-       }
+       return sci_remote_node_context_suspend(&idev->rnc, reason,
+                                              SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);
 }
 
 /**
@@ -133,18 +98,29 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
                wake_up(&ihost->eventq);
 }
 
-/* called once the remote node context is ready to be freed.
- * The remote device can now report that its stop operation is complete. none
- */
-static void rnc_destruct_done(void *_dev)
+static enum sci_status sci_remote_device_terminate_req(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       int check_abort,
+       struct isci_request *ireq)
 {
-       struct isci_remote_device *idev = _dev;
+       if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
+           (ireq->target_device != idev) ||
+           (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags)))
+               return SCI_SUCCESS;
 
-       BUG_ON(idev->started_request_count != 0);
-       sci_change_state(&idev->sm, SCI_DEV_STOPPED);
+       dev_dbg(&ihost->pdev->dev,
+               "%s: idev=%p; flags=%lx; req=%p; req target=%p\n",
+               __func__, idev, idev->flags, ireq, ireq->target_device);
+
+       set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags);
+
+       return sci_controller_terminate_request(ihost, idev, ireq);
 }
 
-static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev)
+static enum sci_status sci_remote_device_terminate_reqs_checkabort(
+       struct isci_remote_device *idev,
+       int chk)
 {
        struct isci_host *ihost = idev->owning_port->owning_controller;
        enum sci_status status  = SCI_SUCCESS;
@@ -154,18 +130,210 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
                struct isci_request *ireq = ihost->reqs[i];
                enum sci_status s;
 
-               if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
-                   ireq->target_device != idev)
-                       continue;
-
-               s = sci_controller_terminate_request(ihost, idev, ireq);
+               s = sci_remote_device_terminate_req(ihost, idev, chk, ireq);
                if (s != SCI_SUCCESS)
                        status = s;
        }
+       return status;
+}
+
+static bool isci_compare_suspendcount(
+       struct isci_remote_device *idev,
+       u32 localcount)
+{
+       smp_rmb();
+
+       /* Check for a change in the suspend count, or the RNC
+        * being destroyed.
+        */
+       return (localcount != idev->rnc.suspend_count)
+           || sci_remote_node_context_is_being_destroyed(&idev->rnc);
+}
+
+static bool isci_check_reqterm(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       struct isci_request *ireq,
+       u32 localcount)
+{
+       unsigned long flags;
+       bool res;
 
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       res = isci_compare_suspendcount(idev, localcount)
+               && !test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+       return res;
+}
+
+static bool isci_check_devempty(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       u32 localcount)
+{
+       unsigned long flags;
+       bool res;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       res = isci_compare_suspendcount(idev, localcount)
+               && idev->started_request_count == 0;
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+       return res;
+}
+
+enum sci_status isci_remote_device_terminate_requests(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       struct isci_request *ireq)
+{
+       enum sci_status status = SCI_SUCCESS;
+       unsigned long flags;
+       u32 rnc_suspend_count;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+
+       if (isci_get_device(idev) == NULL) {
+               dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n",
+                       __func__, idev);
+               spin_unlock_irqrestore(&ihost->scic_lock, flags);
+               status = SCI_FAILURE;
+       } else {
+               /* If already suspended, don't wait for another suspension. */
+               smp_rmb();
+               rnc_suspend_count
+                       = sci_remote_node_context_is_suspended(&idev->rnc)
+                               ? 0 : idev->rnc.suspend_count;
+
+               dev_dbg(&ihost->pdev->dev,
+                       "%s: idev=%p, ireq=%p; started_request_count=%d, "
+                               "rnc_suspend_count=%d, rnc.suspend_count=%d"
+                               "about to wait\n",
+                       __func__, idev, ireq, idev->started_request_count,
+                       rnc_suspend_count, idev->rnc.suspend_count);
+
+               #define MAX_SUSPEND_MSECS 10000
+               if (ireq) {
+                       /* Terminate a specific TC. */
+                       set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags);
+                       sci_remote_device_terminate_req(ihost, idev, 0, ireq);
+                       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+                       if (!wait_event_timeout(ihost->eventq,
+                                               isci_check_reqterm(ihost, idev, ireq,
+                                                                  rnc_suspend_count),
+                                               msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
+
+                               dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n",
+                                        __func__, ihost->id);
+                               dev_dbg(&ihost->pdev->dev,
+                                        "%s: ******* Timeout waiting for "
+                                        "suspend; idev=%p, current state %s; "
+                                        "started_request_count=%d, flags=%lx\n\t"
+                                        "rnc_suspend_count=%d, rnc.suspend_count=%d "
+                                        "RNC: current state %s, current "
+                                        "suspend_type %x dest state %d;\n"
+                                        "ireq=%p, ireq->flags = %lx\n",
+                                        __func__, idev,
+                                        dev_state_name(idev->sm.current_state_id),
+                                        idev->started_request_count, idev->flags,
+                                        rnc_suspend_count, idev->rnc.suspend_count,
+                                        rnc_state_name(idev->rnc.sm.current_state_id),
+                                        idev->rnc.suspend_type,
+                                        idev->rnc.destination_state,
+                                        ireq, ireq->flags);
+                       }
+                       spin_lock_irqsave(&ihost->scic_lock, flags);
+                       clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags);
+                       if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags))
+                               isci_free_tag(ihost, ireq->io_tag);
+                       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+               } else {
+                       /* Terminate all TCs. */
+                       sci_remote_device_terminate_requests(idev);
+                       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+                       if (!wait_event_timeout(ihost->eventq,
+                                               isci_check_devempty(ihost, idev,
+                                                                   rnc_suspend_count),
+                                               msecs_to_jiffies(MAX_SUSPEND_MSECS))) {
+
+                               dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n",
+                                        __func__, ihost->id);
+                               dev_dbg(&ihost->pdev->dev,
+                                       "%s: ******* Timeout waiting for "
+                                       "suspend; idev=%p, current state %s; "
+                                       "started_request_count=%d, flags=%lx\n\t"
+                                       "rnc_suspend_count=%d, "
+                                       "RNC: current state %s, "
+                                       "rnc.suspend_count=%d, current "
+                                       "suspend_type %x dest state %d\n",
+                                       __func__, idev,
+                                       dev_state_name(idev->sm.current_state_id),
+                                       idev->started_request_count, idev->flags,
+                                       rnc_suspend_count,
+                                       rnc_state_name(idev->rnc.sm.current_state_id),
+                                       idev->rnc.suspend_count,
+                                       idev->rnc.suspend_type,
+                                       idev->rnc.destination_state);
+                       }
+               }
+               dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n",
+                       __func__, idev);
+               isci_put_device(idev);
+       }
        return status;
 }
 
+/**
+* isci_remote_device_not_ready() - This function is called by the ihost when
+*    the remote device is not ready. We mark the isci device as ready (not
+*    "ready_for_io") and signal the waiting proccess.
+* @isci_host: This parameter specifies the isci host object.
+* @isci_device: This parameter specifies the remote device
+*
+* sci_lock is held on entrance to this function.
+*/
+static void isci_remote_device_not_ready(struct isci_host *ihost,
+                                        struct isci_remote_device *idev,
+                                        u32 reason)
+{
+       dev_dbg(&ihost->pdev->dev,
+               "%s: isci_device = %p; reason = %d\n", __func__, idev, reason);
+
+       switch (reason) {
+       case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED:
+               set_bit(IDEV_IO_NCQERROR, &idev->flags);
+
+               /* Suspend the remote device so the I/O can be terminated. */
+               sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
+
+               /* Kill all outstanding requests for the device. */
+               sci_remote_device_terminate_requests(idev);
+
+               /* Fall through into the default case... */
+       default:
+               clear_bit(IDEV_IO_READY, &idev->flags);
+               break;
+       }
+}
+
+/* called once the remote node context is ready to be freed.
+ * The remote device can now report that its stop operation is complete. none
+ */
+static void rnc_destruct_done(void *_dev)
+{
+       struct isci_remote_device *idev = _dev;
+
+       BUG_ON(idev->started_request_count != 0);
+       sci_change_state(&idev->sm, SCI_DEV_STOPPED);
+}
+
+enum sci_status sci_remote_device_terminate_requests(
+       struct isci_remote_device *idev)
+{
+       return sci_remote_device_terminate_reqs_checkabort(idev, 0);
+}
+
 enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
                                        u32 timeout)
 {
@@ -201,13 +369,16 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
        case SCI_SMP_DEV_IDLE:
        case SCI_SMP_DEV_CMD:
                sci_change_state(sm, SCI_DEV_STOPPING);
-               if (idev->started_request_count == 0) {
+               if (idev->started_request_count == 0)
                        sci_remote_node_context_destruct(&idev->rnc,
-                                                             rnc_destruct_done, idev);
-                       return SCI_SUCCESS;
-               } else
-                       return sci_remote_device_terminate_requests(idev);
-               break;
+                                                        rnc_destruct_done,
+                                                        idev);
+               else {
+                       sci_remote_device_suspend(
+                               idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
+                       sci_remote_device_terminate_requests(idev);
+               }
+               return SCI_SUCCESS;
        case SCI_DEV_STOPPING:
                /* All requests should have been terminated, but if there is an
                 * attempt to stop a device already in the stopping state, then
@@ -265,22 +436,6 @@ 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)
-{
-       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);
-}
-
 enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
                                                     u32 frame_index)
 {
@@ -412,9 +567,9 @@ 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)
 {
+       enum sci_status status;
        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)) {
        case SCU_EVENT_TYPE_RNC_OPS_MISC:
@@ -427,9 +582,7 @@ 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_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);
 
                        dev_dbg(scirdev_to_dev(idev),
                                "%s: device: %p event code: %x: %s\n",
@@ -455,6 +608,10 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,
        if (status != SCI_SUCCESS)
                return status;
 
+       /* Decode device-specific states that may require an RNC resume during
+        * normal operation.  When the abort path is active, these resumes are
+        * managed when the abort path exits.
+        */
        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 ||
@@ -743,10 +900,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
                if (status != SCI_SUCCESS)
                        return status;
 
-               status = sci_remote_node_context_start_task(&idev->rnc, ireq);
-               if (status != SCI_SUCCESS)
-                       goto out;
-
                status = sci_request_start(ireq);
                if (status != SCI_SUCCESS)
                        goto out;
@@ -765,11 +918,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_device_suspend(idev,
+                                         SCI_SW_SUSPEND_LINKHANG_DETECT);
+
+               status = sci_remote_node_context_start_task(&idev->rnc, ireq,
+                               sci_remote_device_continue_request, idev);
 
        out:
                sci_remote_device_start_request(idev, ireq, status);
@@ -783,7 +936,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
                if (status != SCI_SUCCESS)
                        return status;
 
-               status = sci_remote_node_context_start_task(&idev->rnc, ireq);
+               /* Resume the RNC as needed: */
+               status = sci_remote_node_context_start_task(&idev->rnc, ireq,
+                                                           NULL, NULL);
                if (status != SCI_SUCCESS)
                        break;
 
@@ -892,7 +1047,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_
         * here should go through isci_remote_device_nuke_requests.
         * If we hit this condition, we will need a way to complete
         * io requests in process */
-       BUG_ON(!list_empty(&idev->reqs_in_process));
+       BUG_ON(idev->started_request_count > 0);
 
        sci_remote_device_destruct(idev);
        list_del_init(&idev->node);
@@ -954,14 +1109,21 @@ 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;
 
-       sci_remote_node_context_suspend(
-               &idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL);
+       dev_dbg(&ihost->pdev->dev,
+               "%s: isci_device = %p\n", __func__, idev);
+
+       sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);
 }
 
 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);
 }
@@ -1113,33 +1275,20 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport,
 {
        enum sci_status status;
        struct sci_port_properties properties;
-       struct domain_device *dev = idev->domain_dev;
 
        sci_remote_device_construct(iport, idev);
 
-       /*
-        * This information is request to determine how many remote node context
-        * entries will be needed to store the remote node.
-        */
-       idev->is_direct_attached = true;
-
        sci_port_get_properties(iport, &properties);
        /* Get accurate port width from port's phy mask for a DA device. */
        idev->device_port_width = hweight32(properties.phy_mask);
 
        status = sci_controller_allocate_remote_node_context(iport->owning_controller,
-                                                                 idev,
-                                                                 &idev->rnc.remote_node_index);
+                                                            idev,
+                                                            &idev->rnc.remote_node_index);
 
        if (status != SCI_SUCCESS)
                return status;
 
-       if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
-           (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
-               /* pass */;
-       else
-               return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
        idev->connection_rate = sci_port_get_max_allowed_speed(iport);
 
        return SCI_SUCCESS;
@@ -1171,19 +1320,13 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
        if (status != SCI_SUCCESS)
                return status;
 
-       if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV ||
-           (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev))
-               /* pass */;
-       else
-               return SCI_FAILURE_UNSUPPORTED_PROTOCOL;
-
-       /*
-        * For SAS-2 the physical link rate is actually a logical link
+       /* For SAS-2 the physical link rate is actually a logical link
         * rate that incorporates multiplexing.  The SCU doesn't
         * incorporate multiplexing and for the purposes of the
         * connection the logical link rate is that same as the
         * physical.  Furthermore, the SAS-2 and SAS-1.1 fields overlay
-        * one another, so this code works for both situations. */
+        * one another, so this code works for both situations.
+        */
        idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport),
                                         dev->linkrate);
 
@@ -1193,6 +1336,105 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,
        return SCI_SUCCESS;
 }
 
+enum sci_status sci_remote_device_resume(
+       struct isci_remote_device *idev,
+       scics_sds_remote_node_context_callback cb_fn,
+       void *cb_p)
+{
+       enum sci_status status;
+
+       status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p);
+       if (status != SCI_SUCCESS)
+               dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n",
+                       __func__, status);
+       return status;
+}
+
+static void isci_remote_device_resume_from_abort_complete(void *cbparam)
+{
+       struct isci_remote_device *idev = cbparam;
+       struct isci_host *ihost = idev->owning_port->owning_controller;
+       scics_sds_remote_node_context_callback abort_resume_cb =
+               idev->abort_resume_cb;
+
+       dev_dbg(scirdev_to_dev(idev), "%s: passing-along resume: %p\n",
+               __func__, abort_resume_cb);
+
+       if (abort_resume_cb != NULL) {
+               idev->abort_resume_cb = NULL;
+               abort_resume_cb(idev->abort_resume_cbparam);
+       }
+       clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+       wake_up(&ihost->eventq);
+}
+
+static bool isci_remote_device_test_resume_done(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       unsigned long flags;
+       bool done;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       done = !test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags)
+               || test_bit(IDEV_STOP_PENDING, &idev->flags)
+               || sci_remote_node_context_is_being_destroyed(&idev->rnc);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+       return done;
+}
+
+void isci_remote_device_wait_for_resume_from_abort(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       dev_dbg(&ihost->pdev->dev, "%s: starting resume wait: %p\n",
+                __func__, idev);
+
+       #define MAX_RESUME_MSECS 10000
+       if (!wait_event_timeout(ihost->eventq,
+                               isci_remote_device_test_resume_done(ihost, idev),
+                               msecs_to_jiffies(MAX_RESUME_MSECS))) {
+
+               dev_warn(&ihost->pdev->dev, "%s: #### Timeout waiting for "
+                        "resume: %p\n", __func__, idev);
+       }
+       clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+
+       dev_dbg(&ihost->pdev->dev, "%s: resume wait done: %p\n",
+                __func__, idev);
+}
+
+enum sci_status isci_remote_device_resume_from_abort(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       unsigned long flags;
+       enum sci_status status = SCI_SUCCESS;
+       int destroyed;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       /* Preserve any current resume callbacks, for instance from other
+        * resumptions.
+        */
+       idev->abort_resume_cb = idev->rnc.user_callback;
+       idev->abort_resume_cbparam = idev->rnc.user_cookie;
+       set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+       clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags);
+       destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc);
+       if (!destroyed)
+               status = sci_remote_device_resume(
+                       idev, isci_remote_device_resume_from_abort_complete,
+                       idev);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+       if (!destroyed && (status == SCI_SUCCESS))
+               isci_remote_device_wait_for_resume_from_abort(ihost, idev);
+       else
+               clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags);
+
+       return status;
+}
+
 /**
  * sci_remote_device_start() - This method will start the supplied remote
  *    device.  This method enables normal IO requests to flow through to the
@@ -1207,7 +1449,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.
  */
 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;
        enum sci_remote_device_states state = sm->current_state_id;
@@ -1219,9 +1461,8 @@ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,
                return SCI_FAILURE_INVALID_STATE;
        }
 
-       status = sci_remote_node_context_resume(&idev->rnc,
-                                                    remote_device_resume_done,
-                                                    idev);
+       status = sci_remote_device_resume(idev, remote_device_resume_done,
+                                         idev);
        if (status != SCI_SUCCESS)
                return status;
 
@@ -1259,20 +1500,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport,
        return status;
 }
 
-void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev)
-{
-       DECLARE_COMPLETION_ONSTACK(aborted_task_completion);
-
-       dev_dbg(&ihost->pdev->dev,
-               "%s: idev = %p\n", __func__, idev);
-
-       /* Cleanup all requests pending for this device. */
-       isci_terminate_pending_requests(ihost, idev);
-
-       dev_dbg(&ihost->pdev->dev,
-               "%s: idev = %p, done\n", __func__, idev);
-}
-
 /**
  * This function builds the isci_remote_device when a libsas dev_found message
  *    is received.
@@ -1297,10 +1524,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)
                dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__);
                return NULL;
        }
-
-       if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n"))
-               return NULL;
-
        if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n"))
                return NULL;
 
@@ -1342,14 +1565,8 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
        spin_lock_irqsave(&ihost->scic_lock, flags);
        idev->domain_dev->lldd_dev = NULL; /* disable new lookups */
        set_bit(IDEV_GONE, &idev->flags);
-       spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
-       /* Kill all outstanding requests. */
-       isci_remote_device_nuke_requests(ihost, idev);
 
        set_bit(IDEV_STOP_PENDING, &idev->flags);
-
-       spin_lock_irqsave(&ihost->scic_lock, flags);
        status = sci_remote_device_stop(idev, 50);
        spin_unlock_irqrestore(&ihost->scic_lock, flags);
 
@@ -1359,6 +1576,9 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
        else
                wait_for_device_stop(ihost, idev);
 
+       dev_dbg(&ihost->pdev->dev,
+               "%s: isci_device = %p, waiting done.\n", __func__, idev);
+
        return status;
 }
 
@@ -1434,3 +1654,73 @@ int isci_remote_device_found(struct domain_device *dev)
 
        return status == SCI_SUCCESS ? 0 : -ENODEV;
 }
+
+enum sci_status isci_remote_device_suspend_terminate(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       struct isci_request *ireq)
+{
+       unsigned long flags;
+       enum sci_status status;
+
+       /* 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);
+
+       /* Terminate and wait for the completions. */
+       status = isci_remote_device_terminate_requests(ihost, idev, ireq);
+       if (status != SCI_SUCCESS)
+               dev_dbg(&ihost->pdev->dev,
+                       "%s: isci_remote_device_terminate_requests(%p) "
+                               "returned %d!\n",
+                       __func__, idev, status);
+
+       /* NOTE: RNC resumption is left to the caller! */
+       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_reqs_checkabort(idev, 1);
+}
+
+enum sci_status isci_remote_device_reset_complete(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       unsigned long flags;
+       enum sci_status status;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       status = sci_remote_device_reset_complete(idev);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+       return status;
+}
+
+void isci_dev_set_hang_detection_timeout(
+       struct isci_remote_device *idev,
+       u32 timeout)
+{
+       if (dev_is_sata(idev->domain_dev)) {
+               if (timeout) {
+                       if (test_and_set_bit(IDEV_RNC_LLHANG_ENABLED,
+                                            &idev->flags))
+                               return;  /* Already enabled. */
+               } else if (!test_and_clear_bit(IDEV_RNC_LLHANG_ENABLED,
+                                              &idev->flags))
+                       return;  /* Not enabled. */
+
+               sci_port_set_hang_detection_timeout(idev->owning_port,
+                                                   timeout);
+       }
+}