]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/isci/remote_device.c
isci: Manage device suspensions during TC terminations.
[karo-tx-linux.git] / drivers / scsi / isci / remote_device.c
index 8f501b0a81d6e7842a9d9caaaae1d228b996f2ab..4f76dcd1cec2593898f8e3b1151fa42ce1c293ac 100644 (file)
@@ -133,6 +133,59 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
                wake_up(&ihost->eventq);
 }
 
+static enum sci_status sci_remote_device_suspend(
+       struct isci_remote_device *idev)
+{
+       return sci_remote_node_context_suspend(
+               &idev->rnc,
+               SCI_SOFTWARE_SUSPENSION,
+               SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT,
+               NULL, NULL);
+}
+
+static int isci_remote_device_suspendcheck(struct isci_remote_device *idev)
+{
+       return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+           || !test_bit(IDEV_ALLOCATED, &idev->flags);
+}
+
+enum sci_status isci_remote_device_suspend(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       enum sci_status status;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       if (isci_get_device(idev->domain_dev) == NULL) {
+               spin_unlock_irqrestore(&ihost->scic_lock, flags);
+               status = SCI_FAILURE;
+       } else {
+               status = sci_remote_device_suspend(idev);
+               spin_unlock_irqrestore(&ihost->scic_lock, flags);
+               if (status == SCI_SUCCESS) {
+                       dev_dbg(&ihost->pdev->dev,
+                               "%s: idev=%p, about to wait\n",
+                               __func__, idev);
+                       wait_event(ihost->eventq,
+                                  isci_remote_device_suspendcheck(idev));
+                       status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+                                       ? SCI_SUCCESS : SCI_FAILURE;
+                       dev_dbg(&ihost->pdev->dev,
+                               "%s: idev=%p, wait done, device is %s\n",
+                               __func__, idev,
+                               test_bit(IDEV_TXRX_SUSPENDED, &idev->flags)
+                                       ? "<suspended>" : "<deallocated!>");
+
+               } else
+                       dev_dbg(scirdev_to_dev(idev),
+                                "%s: sci_remote_device_suspend failed, "
+                                "status = %d\n", __func__, status);
+               isci_put_device(idev);
+       }
+       return status;
+}
+
 /* called once the remote node context is ready to be freed.
  * The remote device can now report that its stop operation is complete. none
  */
@@ -144,7 +197,9 @@ static void rnc_destruct_done(void *_dev)
        sci_change_state(&idev->sm, SCI_DEV_STOPPED);
 }
 
-static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev)
+static enum sci_status sci_remote_device_terminate_requests_checkabort(
+       struct isci_remote_device *idev,
+       int check_abort_pending)
 {
        struct isci_host *ihost = idev->owning_port->owning_controller;
        enum sci_status status  = SCI_SUCCESS;
@@ -155,7 +210,9 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
                enum sci_status s;
 
                if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
-                   ireq->target_device != idev)
+                   (ireq->target_device != idev) ||
+                   (check_abort_pending && !test_bit(IREQ_PENDING_ABORT,
+                                                     &ireq->flags)))
                        continue;
 
                s = sci_controller_terminate_request(ihost, idev, ireq);
@@ -166,6 +223,12 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d
        return status;
 }
 
+enum sci_status sci_remote_device_terminate_requests(
+       struct isci_remote_device *idev)
+{
+       return sci_remote_device_terminate_requests_checkabort(idev, 0);
+}
+
 enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
                                        u32 timeout)
 {
@@ -265,22 +328,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,8 +459,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 +472,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 +502,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 +792,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 +981,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 +1040,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 +1105,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,
@@ -1113,33 +1166,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 +1211,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 +1227,35 @@ 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;
+}
+
+enum sci_status isci_remote_device_resume(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev,
+       scics_sds_remote_node_context_callback cb_fn,
+       void *cb_p)
+{
+       unsigned long flags;
+       enum sci_status status;
+
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       status = sci_remote_device_resume(idev, cb_fn, cb_p);
+       spin_unlock_irqrestore(&ihost->scic_lock, 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 +1270,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 +1282,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;
 
@@ -1434,3 +1496,60 @@ int isci_remote_device_found(struct domain_device *dev)
 
        return status == SCI_SUCCESS ? 0 : -ENODEV;
 }
+
+enum sci_status isci_remote_device_reset(
+       struct isci_host *ihost,
+       struct isci_remote_device *idev)
+{
+       unsigned long flags;
+       enum sci_status status;
+
+       /* Put the device into a reset state so the suspension will not
+        * automatically resume.
+        */
+       spin_lock_irqsave(&ihost->scic_lock, flags);
+       status = sci_remote_device_reset(idev);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+       if (status != SCI_SUCCESS) {
+               dev_dbg(&ihost->pdev->dev,
+                       "%s: sci_remote_device_reset(%p) returned %d!\n",
+                       __func__, idev, status);
+               return status;
+       }
+       /* Wait for the device suspend. */
+       status = isci_remote_device_suspend(ihost, idev);
+       if (status != SCI_SUCCESS) {
+               dev_dbg(&ihost->pdev->dev,
+                       "%s: isci_remote_device_suspend(%p) returned %d!\n",
+                       __func__, idev, status);
+       }
+       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_requests_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);
+       sci_remote_device_resume(idev, NULL, NULL);
+       spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+       return status;
+}
+