isci: Manage the link layer hang detect timer for RNC suspensions.
authorJeff Skirvin <jeffrey.d.skirvin@intel.com>
Fri, 9 Mar 2012 06:41:48 +0000 (22:41 -0800)
committerDan Williams <dan.j.williams@intel.com>
Thu, 17 May 2012 21:33:36 +0000 (14:33 -0700)
For STP devices under certain protocol conditions, an RNC will not
suspend until the current transfer state is broken with a SYNC/ESC
sequence from the SCU.  The SYNC/ESC driven by expiration of the
SCU link layer hang detect timer, which has too small a dynamic
range to support slow SATA devices, so normally it is disabled.

This change enables the timer with the minimum period at the point
when the suspension is requested.

Note that there is potential collateral damage to other open
connections to slow SATA devices on the same port, since there
is no alternative but to enable the LLHANG timer on every phy in
the port for the current suspension request - there is no way to
tell on which phy the RNC in question is currently active.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/scsi/isci/port.c
drivers/scsi/isci/port.h
drivers/scsi/isci/remote_device.h
drivers/scsi/isci/remote_node_context.c

index 0a3aec118097993c050df0239cb2f1db44c240a7..ed206c5a00a6191c3148a859d1668b1ed81a1e04 100644 (file)
@@ -1548,6 +1548,29 @@ static void sci_port_failed_state_enter(struct sci_base_state_machine *sm)
        isci_port_hard_reset_complete(iport, SCI_FAILURE_TIMEOUT);
 }
 
+void sci_port_set_hang_detection_timeout(struct isci_port *iport, u32 timeout)
+{
+       int phy_index;
+       u32 phy_mask = iport->active_phy_mask;
+
+       if (timeout)
+               ++iport->hang_detect_users;
+       else if (iport->hang_detect_users > 1)
+               --iport->hang_detect_users;
+       else
+               iport->hang_detect_users = 0;
+
+       if (timeout || (iport->hang_detect_users == 0)) {
+               for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
+                       if ((phy_mask >> phy_index) & 1) {
+                               writel(timeout,
+                                      &iport->phy_table[phy_index]
+                                         ->link_layer_registers
+                                         ->link_layer_hang_detection_timeout);
+                       }
+               }
+       }
+}
 /* --------------------------------------------------------------------------- */
 
 static const struct sci_base_state sci_port_state_table[] = {
@@ -1596,6 +1619,7 @@ void sci_port_construct(struct isci_port *iport, u8 index,
 
        iport->started_request_count = 0;
        iport->assigned_device_count = 0;
+       iport->hang_detect_users = 0;
 
        iport->reserved_rni = SCU_DUMMY_INDEX;
        iport->reserved_tag = SCI_CONTROLLER_INVALID_IO_TAG;
@@ -1733,7 +1757,7 @@ void isci_port_formed(struct asd_sas_phy *phy)
        struct isci_host *ihost = phy->ha->lldd_ha;
        struct isci_phy *iphy = to_iphy(phy);
        struct asd_sas_port *port = phy->port;
-       struct isci_port *iport;
+       struct isci_port *iport = NULL;
        unsigned long flags;
        int i;
 
index f8bd1e8dbfead4f1ed3c833f41f4abb753943e5e..861e8f72811bd463130933aee28caaec7e5f318d 100644 (file)
@@ -111,6 +111,7 @@ struct isci_port {
        u16 reserved_tag;
        u32 started_request_count;
        u32 assigned_device_count;
+       u32 hang_detect_users;
        u32 not_ready_reason;
        struct isci_phy *phy_table[SCI_MAX_PHYS];
        struct isci_host *owning_controller;
@@ -269,6 +270,10 @@ void sci_port_get_attached_sas_address(
        struct isci_port *iport,
        struct sci_sas_address *sas_address);
 
+void sci_port_set_hang_detection_timeout(
+       struct isci_port *isci_port,
+       u32 timeout);
+
 void isci_port_formed(struct asd_sas_phy *);
 void isci_port_deformed(struct asd_sas_phy *);
 
index 4a67ff0eb94ec4da488697950c544b79d03663ff..4850b58edbe69cf06a44aa6b7c65796bbfe8f26a 100644 (file)
@@ -301,6 +301,13 @@ static inline void sci_remote_device_decrement_request_count(struct isci_remote_
                idev->started_request_count--;
 }
 
+static inline void isci_dev_set_hang_detection_timeout(
+       struct isci_remote_device *idev,
+       u32 timeout)
+{
+       sci_port_set_hang_detection_timeout(idev->owning_port, timeout);
+}
+
 enum sci_status sci_remote_device_frame_handler(
        struct isci_remote_device *idev,
        u32 frame_index);
index 8ce5a35891e1536b6d0f45d86e8cfe6870ff379f..3a55ba66b8acdb39eba8ad5aee90fa7ca4aba97a 100644 (file)
@@ -316,6 +316,15 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_
        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);
+
+       isci_dev_set_hang_detection_timeout(rnc_to_dev(rnc), 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,
@@ -338,7 +347,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,
@@ -513,6 +524,8 @@ enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *
        if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
                sci_remote_device_post_request(rnc_to_dev(sci_rnc),
                                                    SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
+               isci_dev_set_hang_detection_timeout(rnc_to_dev(sci_rnc),
+                                                   0x00000001);
        }
 
        sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION);