diff options
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 92 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 15 | ||||
-rw-r--r-- | drivers/scsi/isci/init.c | 5 | ||||
-rw-r--r-- | drivers/scsi/isci/isci.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/phy.c | 11 | ||||
-rw-r--r-- | drivers/scsi/isci/port.c | 148 | ||||
-rw-r--r-- | drivers/scsi/isci/port.h | 6 | ||||
-rw-r--r-- | drivers/scsi/isci/port_config.c | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.h | 4 | ||||
-rw-r--r-- | drivers/scsi/isci/registers.h | 122 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 109 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.h | 14 | ||||
-rw-r--r-- | drivers/scsi/isci/request.c | 432 | ||||
-rw-r--r-- | drivers/scsi/isci/request.h | 37 | ||||
-rw-r--r-- | drivers/scsi/isci/sas.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 721 | ||||
-rw-r--r-- | drivers/scsi/isci/task.h | 54 |
17 files changed, 856 insertions, 920 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 6981b773a88..e7fe9c4c85b 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1263,6 +1263,10 @@ void isci_host_deinit(struct isci_host *ihost) { int i; + /* disable output data selects */ + for (i = 0; i < isci_gpio_count(ihost); i++) + writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); + isci_host_change_state(ihost, isci_stopping); for (i = 0; i < SCI_MAX_PORTS; i++) { struct isci_port *iport = &ihost->ports[i]; @@ -1281,6 +1285,12 @@ void isci_host_deinit(struct isci_host *ihost) spin_unlock_irq(&ihost->scic_lock); wait_for_stop(ihost); + + /* disable sgpio: where the above wait should give time for the + * enclosure to sample the gpios going inactive + */ + writel(0, &ihost->scu_registers->peg0.sgpio.interface_control); + sci_controller_reset(ihost); /* Cancel any/all outstanding port timers */ @@ -1340,7 +1350,7 @@ static void isci_user_parameters_get(struct sci_user_parameters *u) u->stp_max_occupancy_timeout = stp_max_occ_to; u->ssp_max_occupancy_timeout = ssp_max_occ_to; u->no_outbound_task_timeout = no_outbound_task_to; - u->max_number_concurrent_device_spin_up = max_concurr_spinup; + u->max_concurr_spinup = max_concurr_spinup; } static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm) @@ -1651,7 +1661,7 @@ static void sci_controller_set_default_config_parameters(struct isci_host *ihost ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; /* Default to APC mode. */ - ihost->oem_parameters.controller.max_concurrent_dev_spin_up = 1; + ihost->oem_parameters.controller.max_concurr_spin_up = 1; /* Default to no SSC operation. */ ihost->oem_parameters.controller.do_enable_ssc = false; @@ -1777,7 +1787,8 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem) } else return -EINVAL; - if (oem->controller.max_concurrent_dev_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT) + if (oem->controller.max_concurr_spin_up > MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT || + oem->controller.max_concurr_spin_up < 1) return -EINVAL; return 0; @@ -1800,6 +1811,16 @@ static enum sci_status sci_oem_parameters_set(struct isci_host *ihost) return SCI_FAILURE_INVALID_STATE; } +static u8 max_spin_up(struct isci_host *ihost) +{ + if (ihost->user_parameters.max_concurr_spinup) + return min_t(u8, ihost->user_parameters.max_concurr_spinup, + MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT); + else + return min_t(u8, ihost->oem_parameters.controller.max_concurr_spin_up, + MAX_CONCURRENT_DEVICE_SPIN_UP_COUNT); +} + static void power_control_timeout(unsigned long data) { struct sci_timer *tmr = (struct sci_timer *)data; @@ -1829,8 +1850,7 @@ static void power_control_timeout(unsigned long data) if (iphy == NULL) continue; - if (ihost->power_control.phys_granted_power >= - ihost->oem_parameters.controller.max_concurrent_dev_spin_up) + if (ihost->power_control.phys_granted_power >= max_spin_up(ihost)) break; ihost->power_control.requesters[i] = NULL; @@ -1855,8 +1875,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost, { BUG_ON(iphy == NULL); - if (ihost->power_control.phys_granted_power < - ihost->oem_parameters.controller.max_concurrent_dev_spin_up) { + if (ihost->power_control.phys_granted_power < max_spin_up(ihost)) { ihost->power_control.phys_granted_power++; sci_phy_consume_power_handler(iphy); @@ -2365,6 +2384,12 @@ int isci_host_init(struct isci_host *ihost) for (i = 0; i < SCI_MAX_PHYS; i++) isci_phy_init(&ihost->phys[i], ihost, i); + /* enable sgpio */ + writel(1, &ihost->scu_registers->peg0.sgpio.interface_control); + for (i = 0; i < isci_gpio_count(ihost); i++) + writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); + writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code); + for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { struct isci_remote_device *idev = &ihost->devices[i]; @@ -2760,3 +2785,56 @@ enum sci_task_status sci_controller_start_task(struct isci_host *ihost, return status; } + +static int sci_write_gpio_tx_gp(struct isci_host *ihost, u8 reg_index, u8 reg_count, u8 *write_data) +{ + int d; + + /* no support for TX_GP_CFG */ + if (reg_index == 0) + return -EINVAL; + + for (d = 0; d < isci_gpio_count(ihost); d++) { + u32 val = 0x444; /* all ODx.n clear */ + int i; + + for (i = 0; i < 3; i++) { + int bit = (i << 2) + 2; + + bit = try_test_sas_gpio_gp_bit(to_sas_gpio_od(d, i), + write_data, reg_index, + reg_count); + if (bit < 0) + break; + + /* if od is set, clear the 'invert' bit */ + val &= ~(bit << ((i << 2) + 2)); + } + + if (i < 3) + break; + writel(val, &ihost->scu_registers->peg0.sgpio.output_data_select[d]); + } + + /* unless reg_index is > 1, we should always be able to write at + * least one register + */ + return d > 0; +} + +int isci_gpio_write(struct sas_ha_struct *sas_ha, u8 reg_type, u8 reg_index, + u8 reg_count, u8 *write_data) +{ + struct isci_host *ihost = sas_ha->lldd_ha; + int written; + + switch (reg_type) { + case SAS_GPIO_REG_TX_GP: + written = sci_write_gpio_tx_gp(ihost, reg_index, reg_count, write_data); + break; + default: + written = -EINVAL; + } + + return written; +} diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 9f33831a2f0..646051afd3c 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -440,6 +440,18 @@ static inline bool is_c0(struct pci_dev *pdev) return false; } +/* set hw control for 'activity', even though active enclosures seem to drive + * the activity led on their own. Skip setting FSENG control on 'status' due + * to unexpected operation and 'error' due to not being a supported automatic + * FSENG output + */ +#define SGPIO_HW_CONTROL 0x00000443 + +static inline int isci_gpio_count(struct isci_host *ihost) +{ + return ARRAY_SIZE(ihost->scu_registers->peg0.sgpio.output_data_select); +} + void sci_controller_post_request(struct isci_host *ihost, u32 request); void sci_controller_release_frame(struct isci_host *ihost, @@ -542,4 +554,7 @@ void sci_port_configuration_agent_construct( enum sci_status sci_port_configuration_agent_initialize( struct isci_host *ihost, struct sci_port_configuration_agent *port_agent); + +int isci_gpio_write(struct sas_ha_struct *, u8 reg_type, u8 reg_index, + u8 reg_count, u8 *write_data); #endif diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 29aa34efb0f..a97edabcb85 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -118,7 +118,7 @@ unsigned char phy_gen = 3; module_param(phy_gen, byte, 0); MODULE_PARM_DESC(phy_gen, "PHY generation (1: 1.5Gbps 2: 3.0Gbps 3: 6.0Gbps)"); -unsigned char max_concurr_spinup = 1; +unsigned char max_concurr_spinup; module_param(max_concurr_spinup, byte, 0); MODULE_PARM_DESC(max_concurr_spinup, "Max concurrent device spinup"); @@ -192,6 +192,9 @@ static struct sas_domain_function_template isci_transport_ops = { /* Phy management */ .lldd_control_phy = isci_phy_control, + + /* GPIO support */ + .lldd_write_gpio = isci_gpio_write, }; diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h index d1de63312e7..8efeb6b0832 100644 --- a/drivers/scsi/isci/isci.h +++ b/drivers/scsi/isci/isci.h @@ -97,7 +97,7 @@ #define SCU_MAX_COMPLETION_QUEUE_SHIFT (ilog2(SCU_MAX_COMPLETION_QUEUE_ENTRIES)) #define SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES (4096) -#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024) +#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024U) #define SCU_INVALID_FRAME_INDEX (0xFFFF) #define SCU_IO_REQUEST_MAX_SGE_SIZE (0x00FFFFFF) diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 09e61134037..35f50c2183e 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -1313,6 +1313,17 @@ int isci_phy_control(struct asd_sas_phy *sas_phy, ret = isci_port_perform_hard_reset(ihost, iport, iphy); break; + case PHY_FUNC_GET_EVENTS: { + struct scu_link_layer_registers __iomem *r; + struct sas_phy *phy = sas_phy->phy; + + r = iphy->link_layer_registers; + phy->running_disparity_error_count = readl(&r->running_disparity_error_count); + phy->loss_of_dword_sync_count = readl(&r->loss_of_sync_error_count); + phy->phy_reset_problem_count = readl(&r->phy_reset_problem_count); + phy->invalid_dword_count = readl(&r->invalid_dword_counter); + break; + } default: dev_dbg(&ihost->pdev->dev, diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 8f6f9b77e41..ac7f27749f9 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -145,48 +145,15 @@ static void sci_port_bcn_enable(struct isci_port *iport) } } -/* called under sci_lock to stabilize phy:port associations */ -void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport) -{ - int i; - - clear_bit(IPORT_BCN_BLOCKED, &iport->flags); - wake_up(&ihost->eventq); - - if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags)) - return; - - for (i = 0; i < ARRAY_SIZE(iport->phy_table); i++) { - struct isci_phy *iphy = iport->phy_table[i]; - - if (!iphy) - continue; - - ihost->sas_ha.notify_port_event(&iphy->sas_phy, - PORTE_BROADCAST_RCVD); - break; - } -} - static void isci_port_bc_change_received(struct isci_host *ihost, struct isci_port *iport, struct isci_phy *iphy) { - if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) { - dev_dbg(&ihost->pdev->dev, - "%s: disabled BCN; isci_phy = %p, sas_phy = %p\n", - __func__, iphy, &iphy->sas_phy); - set_bit(IPORT_BCN_PENDING, &iport->flags); - atomic_inc(&iport->event); - wake_up(&ihost->eventq); - } else { - dev_dbg(&ihost->pdev->dev, - "%s: isci_phy = %p, sas_phy = %p\n", - __func__, iphy, &iphy->sas_phy); + dev_dbg(&ihost->pdev->dev, + "%s: isci_phy = %p, sas_phy = %p\n", + __func__, iphy, &iphy->sas_phy); - ihost->sas_ha.notify_port_event(&iphy->sas_phy, - PORTE_BROADCAST_RCVD); - } + ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD); sci_port_bcn_enable(iport); } @@ -278,9 +245,6 @@ static void isci_port_link_down(struct isci_host *isci_host, /* check to see if this is the last phy on this port. */ if (isci_phy->sas_phy.port && isci_phy->sas_phy.port->num_phys == 1) { - atomic_inc(&isci_port->event); - isci_port_bcn_enable(isci_host, isci_port); - /* change the state for all devices on this port. The * next task sent to this device will be returned as * SAS_TASK_UNDELIVERED, and the scsi mid layer will @@ -294,8 +258,8 @@ static void isci_port_link_down(struct isci_host *isci_host, __func__, isci_device); set_bit(IDEV_GONE, &isci_device->flags); } + isci_port_change_state(isci_port, isci_stopping); } - isci_port_change_state(isci_port, isci_stopping); } /* Notify libsas of the borken link, this will trigger calls to our @@ -350,6 +314,34 @@ static void isci_port_stop_complete(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "Port stop complete\n"); } + +static bool is_port_ready_state(enum sci_port_states state) +{ + switch (state) { + case SCI_PORT_READY: + case SCI_PORT_SUB_WAITING: + case SCI_PORT_SUB_OPERATIONAL: + case SCI_PORT_SUB_CONFIGURING: + return true; + default: + return false; + } +} + +/* flag dummy rnc hanling when exiting a ready state */ +static void port_state_machine_change(struct isci_port *iport, + enum sci_port_states state) +{ + struct sci_base_state_machine *sm = &iport->sm; + enum sci_port_states old_state = sm->current_state_id; + + if (is_port_ready_state(old_state) && !is_port_ready_state(state)) + iport->ready_exit = true; + + sci_change_state(sm, state); + iport->ready_exit = false; +} + /** * isci_port_hard_reset_complete() - This function is called by the sci core * when the hard reset complete notification has been received. @@ -368,6 +360,26 @@ static void isci_port_hard_reset_complete(struct isci_port *isci_port, /* Save the status of the hard reset from the port. */ isci_port->hard_reset_status = completion_status; + if (completion_status != SCI_SUCCESS) { + + /* The reset failed. The port state is now SCI_PORT_FAILED. */ + if (isci_port->active_phy_mask == 0) { + + /* Generate the link down now to the host, since it + * was intercepted by the hard reset state machine when + * it really happened. + */ + isci_port_link_down(isci_port->isci_host, + &isci_port->isci_host->phys[ + isci_port->last_active_phy], + isci_port); + } + /* Advance the port state so that link state changes will be + * noticed. + */ + port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING); + + } complete_all(&isci_port->hard_reset_complete); } @@ -657,6 +669,8 @@ void sci_port_deactivate_phy(struct isci_port *iport, struct isci_phy *iphy, struct isci_host *ihost = iport->owning_controller; iport->active_phy_mask &= ~(1 << iphy->phy_index); + if (!iport->active_phy_mask) + iport->last_active_phy = iphy->phy_index; iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN; @@ -683,33 +697,6 @@ static void sci_port_invalid_link_up(struct isci_port *iport, struct isci_phy *i } } -static bool is_port_ready_state(enum sci_port_states state) -{ - switch (state) { - case SCI_PORT_READY: - case SCI_PORT_SUB_WAITING: - case SCI_PORT_SUB_OPERATIONAL: - case SCI_PORT_SUB_CONFIGURING: - return true; - default: - return false; - } -} - -/* flag dummy rnc hanling when exiting a ready state */ -static void port_state_machine_change(struct isci_port *iport, - enum sci_port_states state) -{ - struct sci_base_state_machine *sm = &iport->sm; - enum sci_port_states old_state = sm->current_state_id; - - if (is_port_ready_state(old_state) && !is_port_ready_state(state)) - iport->ready_exit = true; - - sci_change_state(sm, state); - iport->ready_exit = false; -} - /** * sci_port_general_link_up_handler - phy can be assigned to port? * @sci_port: sci_port object for which has a phy that has gone link up. @@ -1622,7 +1609,8 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->logical_port_index = SCIC_SDS_DUMMY_PORT; iport->physical_port_index = index; iport->active_phy_mask = 0; - iport->ready_exit = false; + iport->last_active_phy = 0; + iport->ready_exit = false; iport->owning_controller = ihost; @@ -1648,7 +1636,6 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index) init_completion(&iport->start_complete); iport->isci_host = ihost; isci_port_change_state(iport, isci_freed); - atomic_set(&iport->event, 0); } /** @@ -1676,7 +1663,7 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor { unsigned long flags; enum sci_status status; - int idx, ret = TMF_RESP_FUNC_COMPLETE; + int ret = TMF_RESP_FUNC_COMPLETE; dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n", __func__, iport); @@ -1697,8 +1684,13 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor "%s: iport = %p; hard reset completion\n", __func__, iport); - if (iport->hard_reset_status != SCI_SUCCESS) + if (iport->hard_reset_status != SCI_SUCCESS) { ret = TMF_RESP_FUNC_FAILED; + + dev_err(&ihost->pdev->dev, + "%s: iport = %p; hard reset failed (0x%x)\n", + __func__, iport, iport->hard_reset_status); + } } else { ret = TMF_RESP_FUNC_FAILED; @@ -1718,18 +1710,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor "%s: iport = %p; hard reset failed " "(0x%x) - driving explicit link fail for all phys\n", __func__, iport, iport->hard_reset_status); - - /* Down all phys in the port. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - for (idx = 0; idx < SCI_MAX_PHYS; ++idx) { - struct isci_phy *iphy = iport->phy_table[idx]; - - if (!iphy) - continue; - sci_phy_stop(iphy); - sci_phy_start(iphy); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); } return ret; } diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index b50ecd4e8f9..cb5ffbc3860 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -77,7 +77,6 @@ enum isci_status { /** * struct isci_port - isci direct attached sas port object - * @event: counts bcns and port stop events (for bcn filtering) * @ready_exit: several states constitute 'ready'. When exiting ready we * need to take extra port-teardown actions that are * skipped when exiting to another 'ready' state. @@ -92,10 +91,6 @@ enum isci_status { */ struct isci_port { enum isci_status status; - #define IPORT_BCN_BLOCKED 0 - #define IPORT_BCN_PENDING 1 - unsigned long flags; - atomic_t event; struct isci_host *isci_host; struct asd_sas_port sas_port; struct list_head remote_dev_list; @@ -109,6 +104,7 @@ struct isci_port { u8 logical_port_index; u8 physical_port_index; u8 active_phy_mask; + u8 last_active_phy; u16 reserved_rni; u16 reserved_tag; u32 started_request_count; diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 486b113c634..38a99d28114 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -678,7 +678,7 @@ static void apc_agent_timeout(unsigned long data) configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; if (!configure_phy_mask) - return; + goto done; for (index = 0; index < SCI_MAX_PHYS; index++) { if ((configure_phy_mask & (1 << index)) == 0) diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index dc007e692f4..2c75248ca32 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -112,7 +112,7 @@ struct sci_user_parameters { * This field specifies the maximum number of direct attached devices * that can have power supplied to them simultaneously. */ - u8 max_number_concurrent_device_spin_up; + u8 max_concurr_spinup; /** * This field specifies the number of seconds to allow a phy to consume @@ -219,7 +219,7 @@ struct sci_bios_oem_param_block_hdr { struct sci_oem_params { struct { uint8_t mode_type; - uint8_t max_concurrent_dev_spin_up; + uint8_t max_concurr_spin_up; uint8_t do_enable_ssc; uint8_t reserved; } controller; diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h index 00afc738bbe..eaa541afc75 100644 --- a/drivers/scsi/isci/registers.h +++ b/drivers/scsi/isci/registers.h @@ -875,122 +875,6 @@ struct scu_iit_entry { #define SCU_PTSxSR_GEN_BIT(name) \ SCU_GEN_BIT(SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ ## name) - -/* - * ***************************************************************************** - * * SGPIO Register shift and mask values - * ***************************************************************************** */ -#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_SHIFT (0) -#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_MASK (0x00000001) -#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_SHIFT (1) -#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_MASK (0x00000002) -#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_SHIFT (2) -#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_MASK (0x00000004) -#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_SHIFT (15) -#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_MASK (0x00008000) -#define SCU_SGPIO_CONTROL_SGPIO_RESERVED_MASK (0xFFFF7FF8) - -#define SCU_SGICRx_GEN_BIT(name) \ - SCU_GEN_BIT(SCU_SGPIO_CONTROL_SGPIO_ ## name) - -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_SHIFT (0) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_MASK (0x0000000F) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_SHIFT (4) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_MASK (0x000000F0) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_SHIFT (8) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_MASK (0x00000F00) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_SHIFT (12) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_MASK (0x0000F000) -#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_RESERVED_MASK (0xFFFF0000) - -#define SCU_SGPBRx_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_ ## name, value) - -#define SCU_SGPIO_START_DRIVE_LOWER_R0_SHIFT (0) -#define SCU_SGPIO_START_DRIVE_LOWER_R0_MASK (0x00000003) -#define SCU_SGPIO_START_DRIVE_LOWER_R1_SHIFT (4) -#define SCU_SGPIO_START_DRIVE_LOWER_R1_MASK (0x00000030) -#define SCU_SGPIO_START_DRIVE_LOWER_R2_SHIFT (8) -#define SCU_SGPIO_START_DRIVE_LOWER_R2_MASK (0x00000300) -#define SCU_SGPIO_START_DRIVE_LOWER_R3_SHIFT (12) -#define SCU_SGPIO_START_DRIVE_LOWER_R3_MASK (0x00003000) -#define SCU_SGPIO_START_DRIVE_LOWER_RESERVED_MASK (0xFFFF8888) - -#define SCU_SGSDLRx_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value) - -#define SCU_SGPIO_START_DRIVE_UPPER_R0_SHIFT (0) -#define SCU_SGPIO_START_DRIVE_UPPER_R0_MASK (0x00000003) -#define SCU_SGPIO_START_DRIVE_UPPER_R1_SHIFT (4) -#define SCU_SGPIO_START_DRIVE_UPPER_R1_MASK (0x00000030) -#define SCU_SGPIO_START_DRIVE_UPPER_R2_SHIFT (8) -#define SCU_SGPIO_START_DRIVE_UPPER_R2_MASK (0x00000300) -#define SCU_SGPIO_START_DRIVE_UPPER_R3_SHIFT (12) -#define SCU_SGPIO_START_DRIVE_UPPER_R3_MASK (0x00003000) -#define SCU_SGPIO_START_DRIVE_UPPER_RESERVED_MASK (0xFFFF8888) - -#define SCU_SGSDURx_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value) - -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_SHIFT (0) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_MASK (0x00000003) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_SHIFT (4) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_MASK (0x00000030) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_SHIFT (8) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_MASK (0x00000300) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_SHIFT (12) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_MASK (0x00003000) -#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_RESERVED_MASK (0xFFFF8888) - -#define SCU_SGSIDLRx_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value) - -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_SHIFT (0) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_MASK (0x00000003) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_SHIFT (4) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_MASK (0x00000030) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_SHIFT (8) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_MASK (0x00000300) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_SHIFT (12) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_MASK (0x00003000) -#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_RESERVED_MASK (0xFFFF8888) - -#define SCU_SGSIDURx_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value) - -#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_SHIFT (0) -#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_MASK (0x0000000F) -#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_RESERVED_MASK (0xFFFFFFF0) - -#define SCU_SGVSCR_GEN_VAL(value) \ - SCU_GEN_VALUE(SCU_SGPIO_VENDOR_SPECIFIC_CODE ## name, value) - -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_SHIFT (0) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_MASK (0x00000003) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_SHIFT (2) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_MASK (0x00000004) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_SHIFT (3) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_MASK (0x00000008) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_SHIFT (4) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_MASK (0x00000030) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_SHIFT (6) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_MASK (0x00000040) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_SHIFT (7) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_MASK (0x00000080) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_SHIFT (8) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_MASK (0x00000300) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_SHIFT (10) -#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_MASK (0x00000400) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_SHIFT (11) -#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_MASK (0x00000800) -#define SCU_SGPIO_OUPUT_DATA_SELECT_RESERVED_MASK (0xFFFFF000) - -#define SCU_SGODSR_GEN_VAL(name, value) \ - SCU_GEN_VALUE(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name, value) - -#define SCU_SGODSR_GEN_BIT(name) \ - SCU_GEN_BIT(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name) - /* * ***************************************************************************** * * SMU Registers @@ -1529,10 +1413,12 @@ struct scu_sgpio_registers { u32 serial_input_upper; /* 0x0018 SGPIO_SGVSCR */ u32 vendor_specific_code; +/* 0x001C Reserved */ + u32 reserved_001c; /* 0x0020 SGPIO_SGODSR */ - u32 ouput_data_select[8]; + u32 output_data_select[8]; /* Remainder of memory space 256 bytes */ - u32 reserved_1444_14ff[0x31]; + u32 reserved_1444_14ff[0x30]; }; diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b6e6368c266..b207cd3b15a 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -386,6 +386,18 @@ static bool is_remote_device_ready(struct isci_remote_device *idev) } } +/* + * called once the remote node context has transisitioned to a ready + * state (after suspending RX and/or TX due to early D2H fis) + */ +static void atapi_remote_device_resume_done(void *_dev) +{ + struct isci_remote_device *idev = _dev; + struct isci_request *ireq = idev->working_request; + + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); +} + enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, u32 event_code) { @@ -432,6 +444,16 @@ 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 @@ -625,6 +647,7 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost, case SCI_STP_DEV_CMD: case SCI_STP_DEV_NCQ: case SCI_STP_DEV_NCQ_ERROR: + case SCI_STP_DEV_ATAPI_ERROR: status = common_complete_io(iport, idev, ireq); if (status != SCI_SUCCESS) break; @@ -1020,6 +1043,7 @@ 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_AWAIT_RESET] = { }, [SCI_SMP_DEV_IDLE] = { .enter_state = sci_smp_remote_device_ready_idle_substate_enter, @@ -1414,88 +1438,3 @@ int isci_remote_device_found(struct domain_device *domain_dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } -/** - * isci_device_is_reset_pending() - This function will check if there is any - * pending reset condition on the device. - * @request: This parameter is the isci_device object. - * - * true if there is a reset pending for the device. - */ -bool isci_device_is_reset_pending( - struct isci_host *isci_host, - struct isci_remote_device *isci_device) -{ - struct isci_request *isci_request; - struct isci_request *tmp_req; - bool reset_is_pending = false; - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p\n", __func__, isci_device); - - spin_lock_irqsave(&isci_host->scic_lock, flags); - - /* Check for reset on all pending requests. */ - list_for_each_entry_safe(isci_request, tmp_req, - &isci_device->reqs_in_process, dev_node) { - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p request = %p\n", - __func__, isci_device, isci_request); - - if (isci_request->ttype == io_task) { - struct sas_task *task = isci_request_access_task( - isci_request); - - spin_lock(&task->task_state_lock); - if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) - reset_is_pending = true; - spin_unlock(&task->task_state_lock); - } - } - - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p reset_is_pending = %d\n", - __func__, isci_device, reset_is_pending); - - return reset_is_pending; -} - -/** - * isci_device_clear_reset_pending() - This function will clear if any pending - * reset condition flags on the device. - * @request: This parameter is the isci_device object. - * - * true if there is a reset pending for the device. - */ -void isci_device_clear_reset_pending(struct isci_host *ihost, struct isci_remote_device *idev) -{ - struct isci_request *isci_request; - struct isci_request *tmp_req; - unsigned long flags = 0; - - dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ihost=%p\n", - __func__, idev, ihost); - - spin_lock_irqsave(&ihost->scic_lock, flags); - - /* Clear reset pending on all pending requests. */ - list_for_each_entry_safe(isci_request, tmp_req, - &idev->reqs_in_process, dev_node) { - dev_dbg(&ihost->pdev->dev, "%s: idev = %p request = %p\n", - __func__, idev, isci_request); - - if (isci_request->ttype == io_task) { - - unsigned long flags2; - struct sas_task *task = isci_request_access_task( - isci_request); - - spin_lock_irqsave(&task->task_state_lock, flags2); - task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, flags2); - } - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); -} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 57ccfc3d6ad..483ee50152f 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -132,10 +132,7 @@ void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev); void isci_remote_device_gone(struct domain_device *domain_dev); int isci_remote_device_found(struct domain_device *domain_dev); -bool isci_device_is_reset_pending(struct isci_host *ihost, - struct isci_remote_device *idev); -void isci_device_clear_reset_pending(struct isci_host *ihost, - struct isci_remote_device *idev); + /** * sci_remote_device_stop() - This method will stop both transmission and * reception of link activity for the supplied remote device. This method @@ -244,6 +241,15 @@ enum sci_remote_device_states { SCI_STP_DEV_NCQ_ERROR, /** + * This is the ATAPI error state for the STP ATAPI remote device. + * This state is entered when ATAPI device sends error status FIS + * without data while the device object is in CMD state. + * A suspension event is expected in this state. + * The device object will resume right away. + */ + SCI_STP_DEV_ATAPI_ERROR, + + /** * This is the READY substate indicates the device is waiting for the RESET task * coming to be recovered from certain hardware specific error. */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index b5d3a8c4d32..192cb48d849 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -191,7 +191,7 @@ static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq) task_iu->task_func = isci_tmf->tmf_code; task_iu->task_tag = - (ireq->ttype == tmf_task) ? + (test_bit(IREQ_TMF, &ireq->flags)) ? isci_tmf->io_tag : SCI_CONTROLLER_INVALID_IO_TAG; } @@ -481,7 +481,29 @@ static void sci_stp_optimized_request_construct(struct isci_request *ireq, } } +static void sci_atapi_construct(struct isci_request *ireq) +{ + struct host_to_dev_fis *h2d_fis = &ireq->stp.cmd; + struct sas_task *task; + + /* To simplify the implementation we take advantage of the + * silicon's partial acceleration of atapi protocol (dma data + * transfers), so we promote all commands to dma protocol. This + * breaks compatibility with ATA_HORKAGE_ATAPI_MOD16_DMA drives. + */ + h2d_fis->features |= ATAPI_PKT_DMA; + + scu_stp_raw_request_construct_task_context(ireq); + task = isci_request_access_task(ireq); + if (task->data_dir == DMA_NONE) + task->total_xfer_len = 0; + + /* clear the response so we can detect arrivial of an + * unsolicited h2d fis + */ + ireq->stp.rsp.fis_type = 0; +} static enum sci_status sci_io_request_construct_sata(struct isci_request *ireq, @@ -491,9 +513,10 @@ sci_io_request_construct_sata(struct isci_request *ireq, { enum sci_status status = SCI_SUCCESS; struct sas_task *task = isci_request_access_task(ireq); + struct domain_device *dev = ireq->target_device->domain_dev; /* check for management protocols */ - if (ireq->ttype == tmf_task) { + if (test_bit(IREQ_TMF, &ireq->flags)) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); if (tmf->tmf_code == isci_tmf_sata_srst_high || @@ -519,6 +542,13 @@ sci_io_request_construct_sata(struct isci_request *ireq, } + /* ATAPI */ + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET && + task->ata_task.fis.command == ATA_CMD_PACKET) { + sci_atapi_construct(ireq); + return SCI_SUCCESS; + } + /* non data */ if (task->data_dir == DMA_NONE) { scu_stp_raw_request_construct_task_context(ireq); @@ -602,7 +632,7 @@ enum sci_status sci_task_request_construct_sata(struct isci_request *ireq) enum sci_status status = SCI_SUCCESS; /* check for management protocols */ - if (ireq->ttype == tmf_task) { + if (test_bit(IREQ_TMF, &ireq->flags)) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); if (tmf->tmf_code == isci_tmf_sata_srst_high || @@ -627,7 +657,7 @@ enum sci_status sci_task_request_construct_sata(struct isci_request *ireq) /** * sci_req_tx_bytes - bytes transferred when reply underruns request - * @sci_req: request that was terminated early + * @ireq: request that was terminated early */ #define SCU_TASK_CONTEXT_SRAM 0x200000 static u32 sci_req_tx_bytes(struct isci_request *ireq) @@ -729,6 +759,10 @@ sci_io_request_terminate(struct isci_request *ireq) case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED: case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG: case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: + case SCI_REQ_ATAPI_WAIT_H2D: + case SCI_REQ_ATAPI_WAIT_PIO_SETUP: + case SCI_REQ_ATAPI_WAIT_D2H: + case SCI_REQ_ATAPI_WAIT_TC_COMP: sci_change_state(&ireq->sm, SCI_REQ_ABORTING); return SCI_SUCCESS; case SCI_REQ_TASK_WAIT_TC_RESP: @@ -1194,8 +1228,8 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re { struct isci_stp_request *stp_req = &ireq->stp.req; struct scu_sgl_element_pair *sgl_pair; + enum sci_status status = SCI_SUCCESS; struct scu_sgl_element *sgl; - enum sci_status status; u32 offset; u32 len = 0; @@ -1249,7 +1283,7 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re */ static enum sci_status sci_stp_request_pio_data_in_copy_data_buffer(struct isci_stp_request *stp_req, - u8 *data_buf, u32 len) + u8 *data_buf, u32 len) { struct isci_request *ireq; u8 *src_addr; @@ -1423,6 +1457,128 @@ static enum sci_status sci_stp_request_udma_general_frame_handler(struct isci_re return status; } +static enum sci_status process_unsolicited_fis(struct isci_request *ireq, + u32 frame_index) +{ + struct isci_host *ihost = ireq->owning_controller; + enum sci_status status; + struct dev_to_host_fis *frame_header; + u32 *frame_buffer; + + status = sci_unsolicited_frame_control_get_header(&ihost->uf_control, + frame_index, + (void **)&frame_header); + + if (status != SCI_SUCCESS) + return status; + + if (frame_header->fis_type != FIS_REGD2H) { + dev_err(&ireq->isci_host->pdev->dev, + "%s ERROR: invalid fis type 0x%X\n", + __func__, frame_header->fis_type); + return SCI_FAILURE; + } + + sci_unsolicited_frame_control_get_buffer(&ihost->uf_control, + frame_index, + (void **)&frame_buffer); + + sci_controller_copy_sata_response(&ireq->stp.rsp, + (u32 *)frame_header, + frame_buffer); + + /* Frame has been decoded return it to the controller */ + sci_controller_release_frame(ihost, frame_index); + + return status; +} + +static enum sci_status atapi_d2h_reg_frame_handler(struct isci_request *ireq, + u32 frame_index) +{ + struct sas_task *task = isci_request_access_task(ireq); + enum sci_status status; + + status = process_unsolicited_fis(ireq, frame_index); + + if (status == SCI_SUCCESS) { + if (ireq->stp.rsp.status & ATA_ERR) + status = SCI_IO_FAILURE_RESPONSE_VALID; + } else { + status = SCI_IO_FAILURE_RESPONSE_VALID; + } + + if (status != SCI_SUCCESS) { + ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; + ireq->sci_status = status; + } else { + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + } + + /* the d2h ufi is the end of non-data commands */ + if (task->data_dir == DMA_NONE) + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + + return status; +} + +static void scu_atapi_reconstruct_raw_frame_task_context(struct isci_request *ireq) +{ + struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev); + void *atapi_cdb = ireq->ttype_ptr.io_task_ptr->ata_task.atapi_packet; + struct scu_task_context *task_context = ireq->tc; + + /* fill in the SCU Task Context for a DATA fis containing CDB in Raw Frame + * type. The TC for previous Packet fis was already there, we only need to + * change the H2D fis content. + */ + memset(&ireq->stp.cmd, 0, sizeof(struct host_to_dev_fis)); + memcpy(((u8 *)&ireq->stp.cmd + sizeof(u32)), atapi_cdb, ATAPI_CDB_LEN); + memset(&(task_context->type.stp), 0, sizeof(struct stp_task_context)); + task_context->type.stp.fis_type = FIS_DATA; + task_context->transfer_length_bytes = dev->cdb_len; +} + +static void scu_atapi_construct_task_context(struct isci_request *ireq) +{ + struct ata_device *dev = sas_to_ata_dev(ireq->target_device->domain_dev); + struct sas_task *task = isci_request_access_task(ireq); + struct scu_task_context *task_context = ireq->tc; + int cdb_len = dev->cdb_len; + + /* reference: SSTL 1.13.4.2 + * task_type, sata_direction + */ + if (task->data_dir == DMA_TO_DEVICE) { + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_OUT; + task_context->sata_direction = 0; + } else { + /* todo: for NO_DATA command, we need to send out raw frame. */ + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_IN; + task_context->sata_direction = 1; + } + + memset(&task_context->type.stp, 0, sizeof(task_context->type.stp)); + task_context->type.stp.fis_type = FIS_DATA; + + memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd)); + memcpy(&ireq->stp.cmd.lbal, task->ata_task.atapi_packet, cdb_len); + task_context->ssp_command_iu_length = cdb_len / sizeof(u32); + + /* task phase is set to TX_CMD */ + task_context->task_phase = 0x1; + + /* retry counter */ + task_context->stp_retry_count = 0; + + /* data transfer size. */ + task_context->transfer_length_bytes = task->total_xfer_len; + + /* setup sgl */ + sci_request_build_sgl(ireq); +} + enum sci_status sci_io_request_frame_handler(struct isci_request *ireq, u32 frame_index) @@ -1490,29 +1646,30 @@ sci_io_request_frame_handler(struct isci_request *ireq, return SCI_SUCCESS; case SCI_REQ_SMP_WAIT_RESP: { - struct smp_resp *rsp_hdr = &ireq->smp.rsp; - void *frame_header; + struct sas_task *task = isci_request_access_task(ireq); + struct scatterlist *sg = &task->smp_task.smp_resp; + void *frame_header, *kaddr; + u8 *rsp; sci_unsolicited_frame_control_get_header(&ihost->uf_control, - frame_index, - &frame_header); - - /* byte swap the header. */ - word_cnt = SMP_RESP_HDR_SZ / sizeof(u32); - sci_swab32_cpy(rsp_hdr, frame_header, word_cnt); + frame_index, + &frame_header); + kaddr = kmap_atomic(sg_page(sg), KM_IRQ0); + rsp = kaddr + sg->offset; + sci_swab32_cpy(rsp, frame_header, 1); - if (rsp_hdr->frame_type == SMP_RESPONSE) { + if (rsp[0] == SMP_RESPONSE) { void *smp_resp; sci_unsolicited_frame_control_get_buffer(&ihost->uf_control, - frame_index, - &smp_resp); + frame_index, + &smp_resp); - word_cnt = (sizeof(struct smp_resp) - SMP_RESP_HDR_SZ) / - sizeof(u32); - - sci_swab32_cpy(((u8 *) rsp_hdr) + SMP_RESP_HDR_SZ, - smp_resp, word_cnt); + word_cnt = (sg->length/4)-1; + if (word_cnt > 0) + word_cnt = min_t(unsigned int, word_cnt, + SCU_UNSOLICITED_FRAME_BUFFER_SIZE/4); + sci_swab32_cpy(rsp + 4, smp_resp, word_cnt); ireq->scu_status = SCU_TASK_DONE_GOOD; ireq->sci_status = SCI_SUCCESS; @@ -1528,12 +1685,13 @@ sci_io_request_frame_handler(struct isci_request *ireq, __func__, ireq, frame_index, - rsp_hdr->frame_type); + rsp[0]); ireq->scu_status = SCU_TASK_DONE_SMP_FRM_TYPE_ERR; ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR; sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); } + kunmap_atomic(kaddr, KM_IRQ0); sci_controller_release_frame(ihost, frame_index); @@ -1833,6 +1991,24 @@ sci_io_request_frame_handler(struct isci_request *ireq, return status; } + case SCI_REQ_ATAPI_WAIT_PIO_SETUP: { + struct sas_task *task = isci_request_access_task(ireq); + + sci_controller_release_frame(ihost, frame_index); + ireq->target_device->working_request = ireq; + if (task->data_dir == DMA_NONE) { + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_TC_COMP); + scu_atapi_reconstruct_raw_frame_task_context(ireq); + } else { + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H); + scu_atapi_construct_task_context(ireq); + } + + sci_controller_continue_io(ireq); + return SCI_SUCCESS; + } + case SCI_REQ_ATAPI_WAIT_D2H: + return atapi_d2h_reg_frame_handler(ireq, frame_index); case SCI_REQ_ABORTING: /* * TODO: Is it even possible to get an unsolicited frame in the @@ -1898,10 +2074,9 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CMD_LL_R_ERR): - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CRC_ERR): sci_remote_device_suspend(ireq->target_device, SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code))); - /* Fall through to the default case */ + /* Fall through to the default case */ default: /* All other completion status cause the IO to be complete. */ ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); @@ -1964,6 +2139,112 @@ stp_request_soft_reset_await_h2d_diagnostic_tc_event(struct isci_request *ireq, return SCI_SUCCESS; } +static enum sci_status atapi_raw_completion(struct isci_request *ireq, u32 completion_code, + enum sci_base_request_states next) +{ + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + sci_change_state(&ireq->sm, next); + break; + default: + /* All other completion status cause the IO to be complete. + * If a NAK was received, then it is up to the user to retry + * the request. + */ + ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); + ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR; + + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + } + + return status; +} + +static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ireq, + u32 completion_code) +{ + struct isci_remote_device *idev = ireq->target_device; + struct dev_to_host_fis *d2h = &ireq->stp.rsp; + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case (SCU_TASK_DONE_GOOD << SCU_COMPLETION_TL_STATUS_SHIFT): + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + + case (SCU_TASK_DONE_UNEXP_FIS << SCU_COMPLETION_TL_STATUS_SHIFT): { + u16 len = sci_req_tx_bytes(ireq); + + /* likely non-error data underrrun, workaround missing + * d2h frame from the controller + */ + if (d2h->fis_type != FIS_REGD2H) { + d2h->fis_type = FIS_REGD2H; + d2h->flags = (1 << 6); + d2h->status = 0x50; + d2h->error = 0; + d2h->lbal = 0; + d2h->byte_count_low = len & 0xff; + d2h->byte_count_high = len >> 8; + d2h->device = 0xa0; + d2h->lbal_exp = 0; + d2h->lbam_exp = 0; + d2h->lbah_exp = 0; + d2h->_r_a = 0; + d2h->sector_count = 0x3; + d2h->sector_count_exp = 0; + d2h->_r_b = 0; + d2h->_r_c = 0; + d2h->_r_d = 0; + } + + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS_IO_DONE_EARLY; + status = ireq->sci_status; + + /* the hw will have suspended the rnc, so complete the + * request upon pending resume + */ + sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR); + break; + } + case (SCU_TASK_DONE_EXCESS_DATA << SCU_COMPLETION_TL_STATUS_SHIFT): + /* In this case, there is no UF coming after. + * compelte the IO now. + */ + ireq->scu_status = SCU_TASK_DONE_GOOD; + ireq->sci_status = SCI_SUCCESS; + sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); + break; + + default: + if (d2h->fis_type == FIS_REGD2H) { + /* UF received change the device state to ATAPI_ERROR */ + status = ireq->sci_status; + sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR); + } else { + /* If receiving any non-sucess TC status, no UF + * received yet, then an UF for the status fis + * is coming after (XXX: suspect this is + * actually a protocol error or a bug like the + * DONE_UNEXP_FIS case) + */ + ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; + ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; + + sci_change_state(&ireq->sm, SCI_REQ_ATAPI_WAIT_D2H); + } + break; + } + + return status; +} + enum sci_status sci_io_request_tc_completion(struct isci_request *ireq, u32 completion_code) @@ -2015,6 +2296,17 @@ sci_io_request_tc_completion(struct isci_request *ireq, return request_aborting_state_tc_event(ireq, completion_code); + case SCI_REQ_ATAPI_WAIT_H2D: + return atapi_raw_completion(ireq, completion_code, + SCI_REQ_ATAPI_WAIT_PIO_SETUP); + + case SCI_REQ_ATAPI_WAIT_TC_COMP: + return atapi_raw_completion(ireq, completion_code, + SCI_REQ_ATAPI_WAIT_D2H); + + case SCI_REQ_ATAPI_WAIT_D2H: + return atapi_data_tc_completion_handler(ireq, completion_code); + default: dev_warn(&ihost->pdev->dev, "%s: SCIC IO Request given task completion " @@ -2338,14 +2630,8 @@ static void isci_task_save_for_upper_layer_completion( switch (task_notification_selection) { case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - dev_dbg(&host->pdev->dev, - "%s: Normal - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); + /* Add to the completed list. */ list_add(&request->completed_node, &host->requests_to_complete); @@ -2358,13 +2644,6 @@ static void isci_task_save_for_upper_layer_completion( /* No notification to libsas because this request is * already in the abort path. */ - dev_dbg(&host->pdev->dev, - "%s: Aborted - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); - /* Wake up whatever process was waiting for this * request to complete. */ @@ -2381,30 +2660,22 @@ static void isci_task_save_for_upper_layer_completion( case isci_perform_error_io_completion: /* Use sas_task_abort */ - dev_dbg(&host->pdev->dev, - "%s: Error - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); /* Add to the aborted list. */ list_add(&request->completed_node, &host->requests_to_errorback); break; default: - dev_dbg(&host->pdev->dev, - "%s: Unknown - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, - task, - task->task_status.resp, response, - task->task_status.stat, status); - /* Add to the error to libsas list. */ list_add(&request->completed_node, &host->requests_to_errorback); break; } + dev_dbg(&host->pdev->dev, + "%s: %d - task = %p, response=%d (%d), status=%d (%d)\n", + __func__, task_notification_selection, task, + (task) ? task->task_status.resp : 0, response, + (task) ? task->task_status.stat : 0, status); } static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis) @@ -2421,6 +2692,8 @@ static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_ */ if (fis->status & ATA_DF) ts->stat = SAS_PROTO_RESPONSE; + else if (fis->status & ATA_ERR) + ts->stat = SAM_STAT_CHECK_CONDITION; else ts->stat = SAM_STAT_GOOD; @@ -2434,9 +2707,9 @@ static void isci_request_io_request_complete(struct isci_host *ihost, struct sas_task *task = isci_request_access_task(request); struct ssp_response_iu *resp_iu; unsigned long task_flags; - struct isci_remote_device *idev = isci_lookup_device(task->dev); - enum service_response response = SAS_TASK_UNDELIVERED; - enum exec_status status = SAS_ABORTED_TASK; + struct isci_remote_device *idev = request->target_device; + enum service_response response = SAS_TASK_UNDELIVERED; + enum exec_status status = SAS_ABORTED_TASK; enum isci_request_status request_status; enum isci_completion_selection complete_to_host = isci_perform_normal_io_completion; @@ -2603,18 +2876,7 @@ static void isci_request_io_request_complete(struct isci_host *ihost, status = SAM_STAT_GOOD; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - if (task->task_proto == SAS_PROTOCOL_SMP) { - void *rsp = &request->smp.rsp; - - dev_dbg(&ihost->pdev->dev, - "%s: SMP protocol completion\n", - __func__); - - sg_copy_from_buffer( - &task->smp_task.smp_resp, 1, - rsp, sizeof(struct smp_resp)); - } else if (completion_status - == SCI_IO_SUCCESS_IO_DONE_EARLY) { + if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { /* This was an SSP / STP / SATA transfer. * There is a possibility that less data than @@ -2778,7 +3040,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); - isci_put_device(idev); /* set terminated handle so it cannot be completed or * terminated again, and to cause any calls into abort @@ -2791,37 +3052,42 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm) { struct isci_request *ireq = container_of(sm, typeof(*ireq), sm); struct domain_device *dev = ireq->target_device->domain_dev; + enum sci_base_request_states state; struct sas_task *task; /* XXX as hch said always creating an internal sas_task for tmf * requests would simplify the driver */ - task = ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL; + task = (test_bit(IREQ_TMF, &ireq->flags)) ? NULL : isci_request_access_task(ireq); /* all unaccelerated request types (non ssp or ncq) handled with * substates */ if (!task && dev->dev_type == SAS_END_DEV) { - sci_change_state(sm, SCI_REQ_TASK_WAIT_TC_COMP); + state = SCI_REQ_TASK_WAIT_TC_COMP; } else if (!task && (isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_high || isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_low)) { - sci_change_state(sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED); + state = SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED; } else if (task && task->task_proto == SAS_PROTOCOL_SMP) { - sci_change_state(sm, SCI_REQ_SMP_WAIT_RESP); + state = SCI_REQ_SMP_WAIT_RESP; } else if (task && sas_protocol_ata(task->task_proto) && !task->ata_task.use_ncq) { - u32 state; - - if (task->data_dir == DMA_NONE) + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET && + task->ata_task.fis.command == ATA_CMD_PACKET) { + state = SCI_REQ_ATAPI_WAIT_H2D; + } else if (task->data_dir == DMA_NONE) { state = SCI_REQ_STP_NON_DATA_WAIT_H2D; - else if (task->ata_task.dma_xfer) + } else if (task->ata_task.dma_xfer) { state = SCI_REQ_STP_UDMA_WAIT_TC_COMP; - else /* PIO */ + } else /* PIO */ { state = SCI_REQ_STP_PIO_WAIT_H2D; - - sci_change_state(sm, state); + } + } else { + /* SSP or NCQ are fully accelerated, no substates */ + return; } + sci_change_state(sm, state); } static void sci_request_completed_state_enter(struct sci_base_state_machine *sm) @@ -2913,6 +3179,10 @@ static const struct sci_base_state sci_request_state_table[] = { [SCI_REQ_TASK_WAIT_TC_RESP] = { }, [SCI_REQ_SMP_WAIT_RESP] = { }, [SCI_REQ_SMP_WAIT_TC_COMP] = { }, + [SCI_REQ_ATAPI_WAIT_H2D] = { }, + [SCI_REQ_ATAPI_WAIT_PIO_SETUP] = { }, + [SCI_REQ_ATAPI_WAIT_D2H] = { }, + [SCI_REQ_ATAPI_WAIT_TC_COMP] = { }, [SCI_REQ_COMPLETED] = { .enter_state = sci_request_completed_state_enter, }, @@ -3272,7 +3542,7 @@ static struct isci_request *isci_io_request_from_tag(struct isci_host *ihost, ireq = isci_request_from_tag(ihost, tag); ireq->ttype_ptr.io_task_ptr = task; - ireq->ttype = io_task; + clear_bit(IREQ_TMF, &ireq->flags); task->lldd_task = ireq; return ireq; @@ -3286,7 +3556,7 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost, ireq = isci_request_from_tag(ihost, tag); ireq->ttype_ptr.tmf_task_ptr = isci_tmf; - ireq->ttype = tmf_task; + set_bit(IREQ_TMF, &ireq->flags); return ireq; } diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 7a1d5a9778e..be38933dd6d 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -77,11 +77,6 @@ enum isci_request_status { dead = 0x07 }; -enum task_type { - io_task = 0, - tmf_task = 1 -}; - enum sci_request_protocol { SCIC_NO_PROTOCOL, SCIC_SMP_PROTOCOL, @@ -96,7 +91,6 @@ enum sci_request_protocol { * to wait for another fis or if the transfer is complete. Upon * receipt of a d2h fis this will be the status field of that fis. * @sgl - track pio transfer progress as we iterate through the sgl - * @device_cdb_len - atapi device advertises it's transfer constraints at setup */ struct isci_stp_request { u32 pio_len; @@ -107,7 +101,6 @@ struct isci_stp_request { u8 set; u32 offset; } sgl; - u32 device_cdb_len; }; struct isci_request { @@ -118,7 +111,6 @@ struct isci_request { #define IREQ_ACTIVE 3 unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ - enum task_type ttype; union ttype_ptr_union { struct sas_task *io_task_ptr; /* When ttype==io_task */ struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */ @@ -174,9 +166,6 @@ struct isci_request { }; } ssp; struct { - struct smp_resp rsp; - } smp; - struct { struct isci_stp_request req; struct host_to_dev_fis cmd; struct dev_to_host_fis rsp; @@ -252,6 +241,32 @@ enum sci_base_request_states { SCI_REQ_STP_PIO_DATA_OUT, /* + * While in this state the IO request object is waiting for the TC + * completion notification for the H2D Register FIS + */ + SCI_REQ_ATAPI_WAIT_H2D, + + /* + * While in this state the IO request object is waiting for either a + * PIO Setup. + */ + SCI_REQ_ATAPI_WAIT_PIO_SETUP, + + /* + * The non-data IO transit to this state in this state after receiving + * TC completion. While in this state IO request object is waiting for + * D2H status frame as UF. + */ + SCI_REQ_ATAPI_WAIT_D2H, + + /* + * When transmitting raw frames hardware reports task context completion + * after every frame submission, so in the non-accelerated case we need + * to expect the completion for the "cdb" frame. + */ + SCI_REQ_ATAPI_WAIT_TC_COMP, + + /* * The AWAIT_TC_COMPLETION sub-state indicates that the started raw * task management request is waiting for the transmission of the * initial frame (i.e. command, task, etc.). diff --git a/drivers/scsi/isci/sas.h b/drivers/scsi/isci/sas.h index 462b15174d3..dc26b4aea99 100644 --- a/drivers/scsi/isci/sas.h +++ b/drivers/scsi/isci/sas.h @@ -204,8 +204,6 @@ struct smp_req { u8 req_data[0]; } __packed; -#define SMP_RESP_HDR_SZ 4 - /* * struct sci_sas_address - This structure depicts how a SAS address is * represented by SCI. diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index d6bcdd013dc..66ad3dc8949 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -212,16 +212,27 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); - /* Indicate QUEUE_FULL so that the scsi - * midlayer retries. if the request - * failed for remote device reasons, - * it gets returned as - * SAS_TASK_UNDELIVERED next time - * through. - */ - isci_task_refuse(ihost, task, - SAS_TASK_COMPLETE, - SAS_QUEUE_FULL); + if (test_bit(IDEV_GONE, &idev->flags)) { + + /* Indicate that the device + * is gone. + */ + isci_task_refuse(ihost, task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN); + } else { + /* Indicate QUEUE_FULL so that + * the scsi midlayer retries. + * If the request failed for + * remote device reasons, it + * gets returned as + * SAS_TASK_UNDELIVERED next + * time through. + */ + isci_task_refuse(ihost, task, + SAS_TASK_COMPLETE, + SAS_QUEUE_FULL); + } } } } @@ -243,7 +254,7 @@ static enum sci_status isci_sata_management_task_request_build(struct isci_reque struct isci_tmf *isci_tmf; enum sci_status status; - if (tmf_task != ireq->ttype) + if (!test_bit(IREQ_TMF, &ireq->flags)) return SCI_FAILURE; isci_tmf = isci_request_access_tmf(ireq); @@ -327,6 +338,60 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost, return ireq; } +/** +* isci_request_mark_zombie() - This function must be called with scic_lock held. +*/ +static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_request *ireq) +{ + struct completion *tmf_completion = NULL; + struct completion *req_completion; + + /* Set the request state to "dead". */ + ireq->status = dead; + + req_completion = ireq->io_request_completion; + ireq->io_request_completion = NULL; + + if (test_bit(IREQ_TMF, &ireq->flags)) { + /* Break links with the TMF request. */ + struct isci_tmf *tmf = isci_request_access_tmf(ireq); + + /* In the case where a task request is dying, + * the thread waiting on the complete will sit and + * timeout unless we wake it now. Since the TMF + * has a default error status, complete it here + * to wake the waiting thread. + */ + if (tmf) { + tmf_completion = tmf->complete; + tmf->complete = NULL; + } + ireq->ttype_ptr.tmf_task_ptr = NULL; + dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n", + __func__, tmf->tmf_code, tmf->io_tag); + } else { + /* Break links with the sas_task - the callback is done + * elsewhere. + */ + struct sas_task *task = isci_request_access_task(ireq); + + if (task) + task->lldd_task = NULL; + + ireq->ttype_ptr.io_task_ptr = NULL; + } + + dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n", + ireq->io_tag); + + /* Don't force waiting threads to timeout. */ + if (req_completion) + complete(req_completion); + + if (tmf_completion != NULL) + complete(tmf_completion); +} + static int isci_task_execute_tmf(struct isci_host *ihost, struct isci_remote_device *idev, struct isci_tmf *tmf, unsigned long timeout_ms) @@ -364,6 +429,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost, /* Assign the pointer to the TMF's completion kernel wait structure. */ tmf->complete = &completion; + tmf->status = SCI_FAILURE_TIMEOUT; ireq = isci_task_request_build(ihost, idev, tag, tmf); if (!ireq) @@ -399,18 +465,35 @@ static int isci_task_execute_tmf(struct isci_host *ihost, msecs_to_jiffies(timeout_ms)); if (timeleft == 0) { + /* The TMF did not complete - this could be because + * of an unplug. Terminate the TMF request now. + */ spin_lock_irqsave(&ihost->scic_lock, flags); if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_timed_out, tmf, tmf->cb_data); + tmf->cb_state_func(isci_tmf_timed_out, tmf, + tmf->cb_data); - sci_controller_terminate_request(ihost, - idev, - ireq); + sci_controller_terminate_request(ihost, idev, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_for_completion(tmf->complete); + timeleft = wait_for_completion_timeout( + &completion, + msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); + + if (!timeleft) { + /* Strange condition - the termination of the TMF + * request timed-out. + */ + spin_lock_irqsave(&ihost->scic_lock, flags); + + /* If the TMF status has not changed, kill it. */ + if (tmf->status == SCI_FAILURE_TIMEOUT) + isci_request_mark_zombie(ihost, ireq); + + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } } isci_print_tmf(tmf); @@ -501,48 +584,17 @@ static enum isci_request_status isci_task_validate_request_to_abort( return old_state; } -/** -* isci_request_cleanup_completed_loiterer() - This function will take care of -* the final cleanup on any request which has been explicitly terminated. -* @isci_host: This parameter specifies the ISCI host object -* @isci_device: This is the device to which the request is pending. -* @isci_request: This parameter specifies the terminated request object. -* @task: This parameter is the libsas I/O request. -*/ -static void isci_request_cleanup_completed_loiterer( - struct isci_host *isci_host, - struct isci_remote_device *isci_device, - struct isci_request *isci_request, - struct sas_task *task) +static int isci_request_is_dealloc_managed(enum isci_request_status stat) { - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device=%p, request=%p, task=%p\n", - __func__, isci_device, isci_request, task); - - if (task != NULL) { - - spin_lock_irqsave(&task->task_state_lock, flags); - task->lldd_task = NULL; - - task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; - - isci_set_task_doneflags(task); - - /* If this task is not in the abort path, call task_done. */ - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - - spin_unlock_irqrestore(&task->task_state_lock, flags); - task->task_done(task); - } else - spin_unlock_irqrestore(&task->task_state_lock, flags); - } - - if (isci_request != NULL) { - spin_lock_irqsave(&isci_host->scic_lock, flags); - list_del_init(&isci_request->dev_node); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + switch (stat) { + case aborted: + case aborting: + case terminating: + case completed: + case dead: + return true; + default: + return false; } } @@ -563,11 +615,9 @@ static void isci_terminate_request_core(struct isci_host *ihost, enum sci_status status = SCI_SUCCESS; bool was_terminated = false; bool needs_cleanup_handling = false; - enum isci_request_status request_status; unsigned long flags; unsigned long termination_completed = 1; struct completion *io_request_completion; - struct sas_task *task; dev_dbg(&ihost->pdev->dev, "%s: device = %p; request = %p\n", @@ -577,10 +627,6 @@ static void isci_terminate_request_core(struct isci_host *ihost, io_request_completion = isci_request->io_request_completion; - task = (isci_request->ttype == io_task) - ? isci_request_access_task(isci_request) - : NULL; - /* Note that we are not going to control * the target to abort the request. */ @@ -619,42 +665,27 @@ static void isci_terminate_request_core(struct isci_host *ihost, __func__, isci_request, io_request_completion); /* Wait here for the request to complete. */ - #define TERMINATION_TIMEOUT_MSEC 500 termination_completed = wait_for_completion_timeout( io_request_completion, - msecs_to_jiffies(TERMINATION_TIMEOUT_MSEC)); + msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); if (!termination_completed) { /* The request to terminate has timed out. */ - spin_lock_irqsave(&ihost->scic_lock, - flags); + spin_lock_irqsave(&ihost->scic_lock, flags); /* Check for state changes. */ - if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) { + if (!test_bit(IREQ_TERMINATED, + &isci_request->flags)) { /* The best we can do is to have the * request die a silent death if it * ever really completes. - * - * Set the request state to "dead", - * and clear the task pointer so that - * an actual completion event callback - * doesn't do anything. */ - isci_request->status = dead; - isci_request->io_request_completion - = NULL; - - if (isci_request->ttype == io_task) { - - /* Break links with the - * sas_task. - */ - isci_request->ttype_ptr.io_task_ptr - = NULL; - } + isci_request_mark_zombie(ihost, + isci_request); + needs_cleanup_handling = true; } else termination_completed = 1; @@ -691,29 +722,28 @@ static void isci_terminate_request_core(struct isci_host *ihost, * needs to be detached and freed here. */ spin_lock_irqsave(&isci_request->state_lock, flags); - request_status = isci_request->status; - - if ((isci_request->ttype == io_task) /* TMFs are in their own thread */ - && ((request_status == aborted) - || (request_status == aborting) - || (request_status == terminating) - || (request_status == completed) - || (request_status == dead) - ) - ) { - - /* The completion routine won't free a request in - * the aborted/aborting/etc. states, so we do - * it here. - */ - needs_cleanup_handling = true; - } + + needs_cleanup_handling + = isci_request_is_dealloc_managed( + isci_request->status); + spin_unlock_irqrestore(&isci_request->state_lock, flags); } - if (needs_cleanup_handling) - isci_request_cleanup_completed_loiterer( - ihost, idev, isci_request, task); + if (needs_cleanup_handling) { + + dev_dbg(&ihost->pdev->dev, + "%s: cleanup isci_device=%p, request=%p\n", + __func__, idev, isci_request); + + if (isci_request != NULL) { + spin_lock_irqsave(&ihost->scic_lock, flags); + isci_free_tag(ihost, isci_request->io_tag); + isci_request_change_state(isci_request, unallocated); + list_del_init(&isci_request->dev_node); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } + } } } @@ -772,7 +802,9 @@ void isci_terminate_pending_requests(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "%s: idev=%p request=%p; task=%p old_state=%d\n", __func__, idev, ireq, - ireq->ttype == io_task ? isci_request_access_task(ireq) : NULL, + (!test_bit(IREQ_TMF, &ireq->flags) + ? isci_request_access_task(ireq) + : NULL), old_state); /* If the old_state is started: @@ -889,22 +921,14 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", __func__, domain_device, isci_host, isci_device); - if (isci_device) - set_bit(IDEV_EH, &isci_device->flags); + if (!isci_device) { + /* If the device is gone, stop the escalations. */ + dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__); - /* If there is a device reset pending on any request in the - * device's list, fail this LUN reset request in order to - * escalate to the device reset. - */ - if (!isci_device || - isci_device_is_reset_pending(isci_host, isci_device)) { - dev_dbg(&isci_host->pdev->dev, - "%s: No dev (%p), or " - "RESET PENDING: domain_device=%p\n", - __func__, isci_device, domain_device); - ret = TMF_RESP_FUNC_FAILED; + ret = TMF_RESP_FUNC_COMPLETE; goto out; } + set_bit(IDEV_EH, &isci_device->flags); /* Send the task management part of the reset. */ if (sas_protocol_ata(domain_device->tproto)) { @@ -1013,7 +1037,7 @@ int isci_task_abort_task(struct sas_task *task) struct isci_tmf tmf; int ret = TMF_RESP_FUNC_FAILED; unsigned long flags; - bool any_dev_reset = false; + int perform_termination = 0; /* Get the isci_request reference from the task. Note that * this check does not depend on the pending request list @@ -1035,89 +1059,34 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); dev_dbg(&isci_host->pdev->dev, - "%s: task = %p\n", __func__, task); - - if (!isci_device || !old_request) - goto out; - - set_bit(IDEV_EH, &isci_device->flags); - - /* This version of the driver will fail abort requests for - * SATA/STP. Failing the abort request this way will cause the - * SCSI error handler thread to escalate to LUN reset - */ - if (sas_protocol_ata(task->task_proto)) { - dev_dbg(&isci_host->pdev->dev, - " task %p is for a STP/SATA device;" - " returning TMF_RESP_FUNC_FAILED\n" - " to cause a LUN reset...\n", task); - goto out; - } - - dev_dbg(&isci_host->pdev->dev, - "%s: old_request == %p\n", __func__, old_request); - - any_dev_reset = isci_device_is_reset_pending(isci_host, isci_device); + "%s: dev = %p, task = %p, old_request == %p\n", + __func__, isci_device, task, old_request); - spin_lock_irqsave(&task->task_state_lock, flags); - - any_dev_reset = any_dev_reset || (task->task_state_flags & SAS_TASK_NEED_DEV_RESET); + if (isci_device) + set_bit(IDEV_EH, &isci_device->flags); - /* If the extraction of the request reference from the task - * failed, then the request has been completed (or if there is a - * pending reset then this abort request function must be failed - * in order to escalate to the target reset). + /* Device reset conditions signalled in task_state_flags are the + * responsbility of libsas to observe at the start of the error + * handler thread. */ - if ((old_request == NULL) || any_dev_reset) { - - /* If the device reset task flag is set, fail the task - * management request. Otherwise, the original request - * has completed. - */ - if (any_dev_reset) { - - /* Turn off the task's DONE to make sure this - * task is escalated to a target reset. - */ - task->task_state_flags &= ~SAS_TASK_STATE_DONE; - - /* Make the reset happen as soon as possible. */ - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - - spin_unlock_irqrestore(&task->task_state_lock, flags); - - /* Fail the task management request in order to - * escalate to the target reset. - */ - ret = TMF_RESP_FUNC_FAILED; - - dev_dbg(&isci_host->pdev->dev, - "%s: Failing task abort in order to " - "escalate to target reset because\n" - "SAS_TASK_NEED_DEV_RESET is set for " - "task %p on dev %p\n", - __func__, task, isci_device); - - - } else { - /* The request has already completed and there - * is nothing to do here other than to set the task - * done bit, and indicate that the task abort function - * was sucessful. - */ - isci_set_task_doneflags(task); - - spin_unlock_irqrestore(&task->task_state_lock, flags); + if (!isci_device || !old_request) { + /* The request has already completed and there + * is nothing to do here other than to set the task + * done bit, and indicate that the task abort function + * was sucessful. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); - ret = TMF_RESP_FUNC_COMPLETE; + ret = TMF_RESP_FUNC_COMPLETE; - dev_dbg(&isci_host->pdev->dev, - "%s: abort task not needed for %p\n", - __func__, task); - } + dev_dbg(&isci_host->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); goto out; - } else { - spin_unlock_irqrestore(&task->task_state_lock, flags); } spin_lock_irqsave(&isci_host->scic_lock, flags); @@ -1146,24 +1115,44 @@ int isci_task_abort_task(struct sas_task *task) goto out; } if (task->task_proto == SAS_PROTOCOL_SMP || + sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { spin_unlock_irqrestore(&isci_host->scic_lock, flags); dev_dbg(&isci_host->pdev->dev, - "%s: SMP request (%d)" + "%s: %s request" " or complete_in_target (%d), thus no TMF\n", - __func__, (task->task_proto == SAS_PROTOCOL_SMP), + __func__, + ((task->task_proto == SAS_PROTOCOL_SMP) + ? "SMP" + : (sas_protocol_ata(task->task_proto) + ? "SATA/STP" + : "<other>") + ), test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)); - /* Set the state on the task. */ - isci_task_all_done(task); - - ret = TMF_RESP_FUNC_COMPLETE; + if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); + ret = TMF_RESP_FUNC_COMPLETE; + } else { + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + spin_unlock_irqrestore(&task->task_state_lock, flags); + } - /* Stopping and SMP devices are not sent a TMF, and are not - * reset, but the outstanding I/O request is terminated below. + /* STP and SMP devices are not sent a TMF, but the + * outstanding I/O request is terminated below. This is + * because SATA/STP and SMP discovery path timeouts directly + * call the abort task interface for cleanup. */ + perform_termination = 1; + } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, @@ -1172,22 +1161,24 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); - #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */ + #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); - if (ret != TMF_RESP_FUNC_COMPLETE) + if (ret == TMF_RESP_FUNC_COMPLETE) + perform_termination = 1; + else dev_dbg(&isci_host->pdev->dev, - "%s: isci_task_send_tmf failed\n", - __func__); + "%s: isci_task_send_tmf failed\n", __func__); } - if (ret == TMF_RESP_FUNC_COMPLETE) { + if (perform_termination) { set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags); /* Clean up the request on our side, and wait for the aborted * I/O to complete. */ - isci_terminate_request_core(isci_host, isci_device, old_request); + isci_terminate_request_core(isci_host, isci_device, + old_request); } /* Make sure we do not leave a reference to aborted_io_completion */ @@ -1288,7 +1279,8 @@ isci_task_request_complete(struct isci_host *ihost, enum sci_task_status completion_status) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); - struct completion *tmf_complete; + struct completion *tmf_complete = NULL; + struct completion *request_complete = ireq->io_request_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, status=%d\n", @@ -1296,278 +1288,53 @@ isci_task_request_complete(struct isci_host *ihost, isci_request_change_state(ireq, completed); - tmf->status = completion_status; set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); - if (tmf->proto == SAS_PROTOCOL_SSP) { - memcpy(&tmf->resp.resp_iu, - &ireq->ssp.rsp, - SSP_RESP_IU_MAX_SIZE); - } else if (tmf->proto == SAS_PROTOCOL_SATA) { - memcpy(&tmf->resp.d2h_fis, - &ireq->stp.rsp, - sizeof(struct dev_to_host_fis)); + if (tmf) { + tmf->status = completion_status; + + if (tmf->proto == SAS_PROTOCOL_SSP) { + memcpy(&tmf->resp.resp_iu, + &ireq->ssp.rsp, + SSP_RESP_IU_MAX_SIZE); + } else if (tmf->proto == SAS_PROTOCOL_SATA) { + memcpy(&tmf->resp.d2h_fis, + &ireq->stp.rsp, + sizeof(struct dev_to_host_fis)); + } + /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ + tmf_complete = tmf->complete; } - - /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ - tmf_complete = tmf->complete; - sci_controller_complete_io(ihost, ireq->target_device, ireq); /* set the 'terminated' flag handle to make sure it cannot be terminated * or completed again. */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, unallocated); - list_del_init(&ireq->dev_node); - - /* The task management part completes last. */ - complete(tmf_complete); -} - -static void isci_smp_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->completion); -} - -static void isci_smp_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -static struct sas_task *isci_alloc_task(void) -{ - struct sas_task *task = kzalloc(sizeof(*task), GFP_KERNEL); - - if (task) { - INIT_LIST_HEAD(&task->list); - spin_lock_init(&task->task_state_lock); - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_timer(&task->timer); - init_completion(&task->completion); - } - - return task; -} - -static void isci_free_task(struct isci_host *ihost, struct sas_task *task) -{ - if (task) { - BUG_ON(!list_empty(&task->list)); - kfree(task); - } -} - -static int isci_smp_execute_task(struct isci_host *ihost, - struct domain_device *dev, void *req, - int req_size, void *resp, int resp_size) -{ - int res, retry; - struct sas_task *task = NULL; - - for (retry = 0; retry < 3; retry++) { - task = isci_alloc_task(); - if (!task) - return -ENOMEM; - - task->dev = dev; - task->task_proto = dev->tproto; - sg_init_one(&task->smp_task.smp_req, req, req_size); - sg_init_one(&task->smp_task.smp_resp, resp, resp_size); - - task->task_done = isci_smp_task_done; - - task->timer.data = (unsigned long) task; - task->timer.function = isci_smp_task_timedout; - task->timer.expires = jiffies + 10*HZ; - add_timer(&task->timer); - - res = isci_task_execute_task(task, 1, GFP_KERNEL); - - if (res) { - del_timer(&task->timer); - dev_dbg(&ihost->pdev->dev, - "%s: executing SMP task failed:%d\n", - __func__, res); - goto ex_err; - } - - wait_for_completion(&task->completion); - res = -ECOMM; - if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - dev_dbg(&ihost->pdev->dev, - "%s: smp task timed out or aborted\n", - __func__); - isci_task_abort_task(task); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - dev_dbg(&ihost->pdev->dev, - "%s: SMP task aborted and not done\n", - __func__); - goto ex_err; - } - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAM_STAT_GOOD) { - res = 0; - break; - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_UNDERRUN) { - /* no error, but return the number of bytes of - * underrun */ - res = task->task_status.residual; - break; - } - if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_OVERRUN) { - res = -EMSGSIZE; - break; - } else { - dev_dbg(&ihost->pdev->dev, - "%s: task to dev %016llx response: 0x%x " - "status 0x%x\n", __func__, - SAS_ADDR(dev->sas_addr), - task->task_status.resp, - task->task_status.stat); - isci_free_task(ihost, task); - task = NULL; - } - } -ex_err: - BUG_ON(retry == 3 && task != NULL); - isci_free_task(ihost, task); - return res; -} - -#define DISCOVER_REQ_SIZE 16 -#define DISCOVER_RESP_SIZE 56 - -int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost, - struct domain_device *dev, - int phy_id, int *adt) -{ - struct smp_resp *disc_resp; - u8 *disc_req; - int res; - - disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL); - if (!disc_resp) - return -ENOMEM; - - disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL); - if (disc_req) { - disc_req[0] = SMP_REQUEST; - disc_req[1] = SMP_DISCOVER; - disc_req[9] = phy_id; - } else { - kfree(disc_resp); - return -ENOMEM; - } - res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (!res) { - if (disc_resp->result != SMP_RESP_FUNC_ACC) - res = disc_resp->result; - else - *adt = disc_resp->disc.attached_dev_type; + /* As soon as something is in the terminate path, deallocation is + * managed there. Note that the final non-managed state of a task + * request is "completed". + */ + if ((ireq->status == completed) || + !isci_request_is_dealloc_managed(ireq->status)) { + isci_request_change_state(ireq, unallocated); + isci_free_tag(ihost, ireq->io_tag); + list_del_init(&ireq->dev_node); } - kfree(disc_req); - kfree(disc_resp); - - return res; -} -static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num) -{ - struct domain_device *dev = idev->domain_dev; - struct isci_port *iport = idev->isci_port; - struct isci_host *ihost = iport->isci_host; - int res, iteration = 0, attached_device_type; - #define STP_WAIT_MSECS 25000 - unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS); - unsigned long deadline = jiffies + tmo; - enum { - SMP_PHYWAIT_PHYDOWN, - SMP_PHYWAIT_PHYUP, - SMP_PHYWAIT_DONE - } phy_state = SMP_PHYWAIT_PHYDOWN; - - /* While there is time, wait for the phy to go away and come back */ - while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) { - int event = atomic_read(&iport->event); - - ++iteration; - - tmo = wait_event_timeout(ihost->eventq, - event != atomic_read(&iport->event) || - !test_bit(IPORT_BCN_BLOCKED, &iport->flags), - tmo); - /* link down, stop polling */ - if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags)) - break; + /* "request_complete" is set if the task was being terminated. */ + if (request_complete) + complete(request_complete); - dev_dbg(&ihost->pdev->dev, - "%s: iport %p, iteration %d," - " phase %d: time_remaining %lu, bcns = %d\n", - __func__, iport, iteration, phy_state, - tmo, test_bit(IPORT_BCN_PENDING, &iport->flags)); - - res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num, - &attached_device_type); - tmo = deadline - jiffies; - - if (res) { - dev_dbg(&ihost->pdev->dev, - "%s: iteration %d, phase %d:" - " SMP error=%d, time_remaining=%lu\n", - __func__, iteration, phy_state, res, tmo); - break; - } - dev_dbg(&ihost->pdev->dev, - "%s: iport %p, iteration %d," - " phase %d: time_remaining %lu, bcns = %d, " - "attdevtype = %x\n", - __func__, iport, iteration, phy_state, - tmo, test_bit(IPORT_BCN_PENDING, &iport->flags), - attached_device_type); - - switch (phy_state) { - case SMP_PHYWAIT_PHYDOWN: - /* Has the device gone away? */ - if (!attached_device_type) - phy_state = SMP_PHYWAIT_PHYUP; - - break; - - case SMP_PHYWAIT_PHYUP: - /* Has the device come back? */ - if (attached_device_type) - phy_state = SMP_PHYWAIT_DONE; - break; - - case SMP_PHYWAIT_DONE: - break; - } - - } - dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__); + /* The task management part completes last. */ + if (tmf_complete) + complete(tmf_complete); } static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { struct sas_phy *phy = sas_find_local_phy(idev->domain_dev); - struct isci_port *iport = idev->isci_port; enum sci_status status; unsigned long flags; int rc; @@ -1587,13 +1354,6 @@ static int isci_reset_device(struct isci_host *ihost, } spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* Make sure all pending requests are able to be fully terminated. */ - isci_device_clear_reset_pending(ihost, idev); - - /* If this is a device on an expander, disable BCN processing. */ - if (!scsi_is_sas_phy_local(phy)) - set_bit(IPORT_BCN_BLOCKED, &iport->flags); - rc = sas_phy_reset(phy, true); /* Terminate in-progress I/O now. */ @@ -1604,21 +1364,6 @@ static int isci_reset_device(struct isci_host *ihost, status = sci_remote_device_reset_complete(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* If this is a device on an expander, bring the phy back up. */ - if (!scsi_is_sas_phy_local(phy)) { - /* A phy reset will cause the device to go away then reappear. - * Since libsas will take action on incoming BCNs (eg. remove - * a device going through an SMP phy-control driven reset), - * we need to wait until the phy comes back up before letting - * discovery proceed in libsas. - */ - isci_wait_for_smp_phy_reset(idev, phy->number); - - spin_lock_irqsave(&ihost->scic_lock, flags); - isci_port_bcn_enable(ihost, idev->isci_port); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - } - if (status != SCI_SUCCESS) { dev_dbg(&ihost->pdev->dev, "%s: sci_remote_device_reset_complete(%p) " diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 4a7fa90287e..bc78c0a41d5 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -58,6 +58,8 @@ #include <scsi/sas_ata.h> #include "host.h" +#define ISCI_TERMINATION_TIMEOUT_MSEC 500 + struct isci_request; /** @@ -224,35 +226,6 @@ enum isci_completion_selection { isci_perform_error_io_completion /* Use sas_task_abort */ }; -static inline void isci_set_task_doneflags( - struct sas_task *task) -{ - /* Since no futher action will be taken on this task, - * make sure to mark it complete from the lldd perspective. - */ - task->task_state_flags |= SAS_TASK_STATE_DONE; - task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; - task->task_state_flags &= ~SAS_TASK_STATE_PENDING; -} -/** - * isci_task_all_done() - This function clears the task bits to indicate the - * LLDD is done with the task. - * - * - */ -static inline void isci_task_all_done( - struct sas_task *task) -{ - unsigned long flags; - - /* Since no futher action will be taken on this task, - * make sure to mark it complete from the lldd perspective. - */ - spin_lock_irqsave(&task->task_state_lock, flags); - isci_set_task_doneflags(task); - spin_unlock_irqrestore(&task->task_state_lock, flags); -} - /** * isci_task_set_completion_status() - This function sets the completion status * for the request. @@ -286,6 +259,25 @@ isci_task_set_completion_status( task->task_status.resp = response; task->task_status.stat = status; + switch (task->task_proto) { + + case SAS_PROTOCOL_SATA: + case SAS_PROTOCOL_STP: + case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: + + if (task_notification_selection + == isci_perform_error_io_completion) { + /* SATA/STP I/O has it's own means of scheduling device + * error handling on the normal path. + */ + task_notification_selection + = isci_perform_normal_io_completion; + } + break; + default: + break; + } + switch (task_notification_selection) { case isci_perform_error_io_completion: @@ -315,7 +307,9 @@ isci_task_set_completion_status( /* Fall through to the normal case... */ case isci_perform_normal_io_completion: /* Normal notification (task_done) */ - isci_set_task_doneflags(task); + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); break; default: WARN_ONCE(1, "unknown task_notification_selection: %d\n", |