diff options
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 703 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 124 | ||||
-rw-r--r-- | drivers/scsi/isci/init.c | 212 | ||||
-rw-r--r-- | drivers/scsi/isci/phy.c | 76 | ||||
-rw-r--r-- | drivers/scsi/isci/phy.h | 9 | ||||
-rw-r--r-- | drivers/scsi/isci/port.c | 68 | ||||
-rw-r--r-- | drivers/scsi/isci/port.h | 11 | ||||
-rw-r--r-- | drivers/scsi/isci/port_config.c | 18 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.c | 12 | ||||
-rw-r--r-- | drivers/scsi/isci/probe_roms.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/registers.h | 8 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 576 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.h | 63 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_node_context.c | 393 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_node_context.h | 43 | ||||
-rw-r--r-- | drivers/scsi/isci/request.c | 715 | ||||
-rw-r--r-- | drivers/scsi/isci/request.h | 125 | ||||
-rw-r--r-- | drivers/scsi/isci/scu_completion_codes.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 800 | ||||
-rw-r--r-- | drivers/scsi/isci/task.h | 132 | ||||
-rw-r--r-- | drivers/scsi/isci/unsolicited_frame_control.c | 30 | ||||
-rw-r--r-- | drivers/scsi/isci/unsolicited_frame_control.h | 6 |
22 files changed, 1873 insertions, 2255 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d4bf9c12ecd..45385f53164 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -192,22 +192,27 @@ static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost) static bool sci_controller_isr(struct isci_host *ihost) { - if (sci_controller_completion_queue_has_entries(ihost)) { + if (sci_controller_completion_queue_has_entries(ihost)) return true; - } else { - /* - * we have a spurious interrupt it could be that we have already - * emptied the completion queue from a previous interrupt */ - writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); - /* - * There is a race in the hardware that could cause us not to be notified - * of an interrupt completion if we do not take this step. We will mask - * then unmask the interrupts so if there is another interrupt pending - * the clearing of the interrupt source we get the next interrupt message. */ + /* we have a spurious interrupt it could be that we have already + * emptied the completion queue from a previous interrupt + * FIXME: really!? + */ + writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); + + /* There is a race in the hardware that could cause us not to be + * notified of an interrupt completion if we do not take this + * step. We will mask then unmask the interrupts so if there is + * another interrupt pending the clearing of the interrupt + * source we get the next interrupt message. + */ + spin_lock(&ihost->scic_lock); + if (test_bit(IHOST_IRQ_ENABLED, &ihost->flags)) { writel(0xFF000000, &ihost->smu_registers->interrupt_mask); writel(0, &ihost->smu_registers->interrupt_mask); } + spin_unlock(&ihost->scic_lock); return false; } @@ -642,7 +647,6 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co if (completion_status != SCI_SUCCESS) dev_info(&ihost->pdev->dev, "controller start timed out, continuing...\n"); - isci_host_change_state(ihost, isci_ready); clear_bit(IHOST_START_PENDING, &ihost->flags); wake_up(&ihost->eventq); } @@ -657,12 +661,7 @@ int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) sas_drain_work(ha); - dev_dbg(&ihost->pdev->dev, - "%s: ihost->status = %d, time = %ld\n", - __func__, isci_host_get_state(ihost), time); - return 1; - } /** @@ -704,14 +703,15 @@ static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost) static void sci_controller_enable_interrupts(struct isci_host *ihost) { - BUG_ON(ihost->smu_registers == NULL); + set_bit(IHOST_IRQ_ENABLED, &ihost->flags); writel(0, &ihost->smu_registers->interrupt_mask); } void sci_controller_disable_interrupts(struct isci_host *ihost) { - BUG_ON(ihost->smu_registers == NULL); + clear_bit(IHOST_IRQ_ENABLED, &ihost->flags); writel(0xffffffff, &ihost->smu_registers->interrupt_mask); + readl(&ihost->smu_registers->interrupt_mask); /* flush */ } static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost) @@ -822,7 +822,7 @@ static void sci_controller_initialize_unsolicited_frame_queue(struct isci_host * &ihost->scu_registers->sdma.unsolicited_frame_put_pointer); } -static void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status) +void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status) { if (ihost->sm.current_state_id == SCIC_STARTING) { /* @@ -849,6 +849,7 @@ static bool is_phy_starting(struct isci_phy *iphy) case SCI_PHY_SUB_AWAIT_SATA_POWER: case SCI_PHY_SUB_AWAIT_SATA_PHY_EN: case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: + case SCI_PHY_SUB_AWAIT_OSSP_EN: case SCI_PHY_SUB_AWAIT_SIG_FIS_UF: case SCI_PHY_SUB_FINAL: return true; @@ -857,6 +858,39 @@ static bool is_phy_starting(struct isci_phy *iphy) } } +bool is_controller_start_complete(struct isci_host *ihost) +{ + int i; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + struct isci_phy *iphy = &ihost->phys[i]; + u32 state = iphy->sm.current_state_id; + + /* in apc mode we need to check every phy, in + * mpc mode we only need to check phys that have + * been configured into a port + */ + if (is_port_config_apc(ihost)) + /* pass */; + else if (!phy_get_non_dummy_port(iphy)) + continue; + + /* The controller start operation is complete iff: + * - all links have been given an opportunity to start + * - have no indication of a connected device + * - have an indication of a connected device and it has + * finished the link training process. + */ + if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) || + (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) || + (iphy->is_in_link_training == true && is_phy_starting(iphy)) || + (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask)) + return false; + } + + return true; +} + /** * sci_controller_start_next_phy - start phy * @scic: controller @@ -877,36 +911,7 @@ static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost) return status; if (ihost->next_phy_to_start >= SCI_MAX_PHYS) { - bool is_controller_start_complete = true; - u32 state; - u8 index; - - for (index = 0; index < SCI_MAX_PHYS; index++) { - iphy = &ihost->phys[index]; - state = iphy->sm.current_state_id; - - if (!phy_get_non_dummy_port(iphy)) - continue; - - /* The controller start operation is complete iff: - * - all links have been given an opportunity to start - * - have no indication of a connected device - * - have an indication of a connected device and it has - * finished the link training process. - */ - if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) || - (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) || - (iphy->is_in_link_training == true && is_phy_starting(iphy)) || - (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask)) { - is_controller_start_complete = false; - break; - } - } - - /* - * The controller has successfully finished the start process. - * Inform the SCI Core user and transition to the READY state. */ - if (is_controller_start_complete == true) { + if (is_controller_start_complete(ihost)) { sci_controller_transition_to_ready(ihost, SCI_SUCCESS); sci_del_timer(&ihost->phy_timer); ihost->phy_startup_timer_pending = false; @@ -987,9 +992,8 @@ static enum sci_status sci_controller_start(struct isci_host *ihost, u16 index; if (ihost->sm.current_state_id != SCIC_INITIALIZED) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller start operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -1053,9 +1057,8 @@ void isci_host_scan_start(struct Scsi_Host *shost) spin_unlock_irq(&ihost->scic_lock); } -static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status) +static void isci_host_stop_complete(struct isci_host *ihost) { - isci_host_change_state(ihost, isci_stopped); sci_controller_disable_interrupts(ihost); clear_bit(IHOST_STOP_PENDING, &ihost->flags); wake_up(&ihost->eventq); @@ -1074,6 +1077,32 @@ static void sci_controller_completion_handler(struct isci_host *ihost) writel(0, &ihost->smu_registers->interrupt_mask); } +void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task) +{ + task->lldd_task = NULL; + if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags) && + !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (test_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags)) { + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, + "%s: Normal - ireq/task = %p/%p\n", + __func__, ireq, task); + + task->task_done(task); + } else { + dev_dbg(&ihost->pdev->dev, + "%s: Error - ireq/task = %p/%p\n", + __func__, ireq, task); + + sas_task_abort(task); + } + } + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); + + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); +} /** * isci_host_completion_routine() - This function is the delayed service * routine that calls the sci core library's completion handler. It's @@ -1082,107 +1111,15 @@ static void sci_controller_completion_handler(struct isci_host *ihost) * @data: This parameter specifies the ISCI host object * */ -static void isci_host_completion_routine(unsigned long data) +void isci_host_completion_routine(unsigned long data) { struct isci_host *ihost = (struct isci_host *)data; - struct list_head completed_request_list; - struct list_head errored_request_list; - struct list_head *current_position; - struct list_head *next_position; - struct isci_request *request; - struct isci_request *next_request; - struct sas_task *task; u16 active; - INIT_LIST_HEAD(&completed_request_list); - INIT_LIST_HEAD(&errored_request_list); - spin_lock_irq(&ihost->scic_lock); - sci_controller_completion_handler(ihost); - - /* Take the lists of completed I/Os from the host. */ - - list_splice_init(&ihost->requests_to_complete, - &completed_request_list); - - /* Take the list of errored I/Os from the host. */ - list_splice_init(&ihost->requests_to_errorback, - &errored_request_list); - spin_unlock_irq(&ihost->scic_lock); - /* Process any completions in the lists. */ - list_for_each_safe(current_position, next_position, - &completed_request_list) { - - request = list_entry(current_position, struct isci_request, - completed_node); - task = isci_request_access_task(request); - - /* Normal notification (task_done) */ - dev_dbg(&ihost->pdev->dev, - "%s: Normal - request/task = %p/%p\n", - __func__, - request, - task); - - /* Return the task to libsas */ - if (task != NULL) { - - task->lldd_task = NULL; - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - - /* If the task is already in the abort path, - * the task_done callback cannot be called. - */ - task->task_done(task); - } - } - - spin_lock_irq(&ihost->scic_lock); - isci_free_tag(ihost, request->io_tag); - spin_unlock_irq(&ihost->scic_lock); - } - list_for_each_entry_safe(request, next_request, &errored_request_list, - completed_node) { - - task = isci_request_access_task(request); - - /* Use sas_task_abort */ - dev_warn(&ihost->pdev->dev, - "%s: Error - request/task = %p/%p\n", - __func__, - request, - task); - - if (task != NULL) { - - /* Put the task into the abort path if it's not there - * already. - */ - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) - sas_task_abort(task); - - } else { - /* This is a case where the request has completed with a - * status such that it needed further target servicing, - * but the sas_task reference has already been removed - * from the request. Since it was errored, it was not - * being aborted, so there is nothing to do except free - * it. - */ - - spin_lock_irq(&ihost->scic_lock); - /* Remove the request from the remote device's list - * of pending requests. - */ - list_del_init(&request->dev_node); - isci_free_tag(ihost, request->io_tag); - spin_unlock_irq(&ihost->scic_lock); - } - } - /* the coalesence timeout doubles at each encoding step, so * update it based on the ilog2 value of the outstanding requests */ @@ -1213,9 +1150,8 @@ static void isci_host_completion_routine(unsigned long data) static enum sci_status sci_controller_stop(struct isci_host *ihost, u32 timeout) { if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller stop operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -1241,7 +1177,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) switch (ihost->sm.current_state_id) { case SCIC_RESET: case SCIC_READY: - case SCIC_STOPPED: + case SCIC_STOPPING: case SCIC_FAILED: /* * The reset operation is not a graceful cleanup, just @@ -1250,13 +1186,50 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) sci_change_state(&ihost->sm, SCIC_RESETTING); return SCI_SUCCESS; default: - dev_warn(&ihost->pdev->dev, - "SCIC Controller reset operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } } +static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) +{ + u32 index; + enum sci_status status; + enum sci_status phy_status; + + status = SCI_SUCCESS; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + phy_status = sci_phy_stop(&ihost->phys[index]); + + if (phy_status != SCI_SUCCESS && + phy_status != SCI_FAILURE_INVALID_STATE) { + status = SCI_FAILURE; + + dev_warn(&ihost->pdev->dev, + "%s: Controller stop operation failed to stop " + "phy %d because of status %d.\n", + __func__, + ihost->phys[index].phy_index, phy_status); + } + } + + return status; +} + + +/** + * isci_host_deinit - shutdown frame reception and dma + * @ihost: host to take down + * + * This is called in either the driver shutdown or the suspend path. In + * the shutdown case libsas went through port teardown and normal device + * removal (i.e. physical links stayed up to service scsi_device removal + * commands). In the suspend case we disable the hardware without + * notifying libsas of the link down events since we want libsas to + * remember the domain across the suspend/resume cycle + */ void isci_host_deinit(struct isci_host *ihost) { int i; @@ -1265,17 +1238,6 @@ void isci_host_deinit(struct isci_host *ihost) 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]; - struct isci_remote_device *idev, *d; - - list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) { - if (test_bit(IDEV_ALLOCATED, &idev->flags)) - isci_remote_device_stop(ihost, idev); - } - } - set_bit(IHOST_STOP_PENDING, &ihost->flags); spin_lock_irq(&ihost->scic_lock); @@ -1284,12 +1246,21 @@ void isci_host_deinit(struct isci_host *ihost) wait_for_stop(ihost); + /* phy stop is after controller stop to allow port and device to + * go idle before shutting down the phys, but the expectation is + * that i/o has been shut off well before we reach this + * function. + */ + sci_controller_stop_phys(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); + spin_lock_irq(&ihost->scic_lock); sci_controller_reset(ihost); + spin_unlock_irq(&ihost->scic_lock); /* Cancel any/all outstanding port timers */ for (i = 0; i < ihost->logical_port_entries; i++) { @@ -1328,29 +1299,6 @@ static void __iomem *smu_base(struct isci_host *isci_host) return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id; } -static void isci_user_parameters_get(struct sci_user_parameters *u) -{ - int i; - - for (i = 0; i < SCI_MAX_PHYS; i++) { - struct sci_phy_user_params *u_phy = &u->phys[i]; - - u_phy->max_speed_generation = phy_gen; - - /* we are not exporting these for now */ - u_phy->align_insertion_frequency = 0x7f; - u_phy->in_connection_align_insertion_frequency = 0xff; - u_phy->notify_enable_spin_up_insertion_frequency = 0x33; - } - - u->stp_inactivity_timeout = stp_inactive_to; - u->ssp_inactivity_timeout = ssp_inactive_to; - 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_concurr_spinup = max_concurr_spinup; -} - static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm) { struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); @@ -1510,32 +1458,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm) sci_controller_set_interrupt_coalescence(ihost, 0, 0); } -static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) -{ - u32 index; - enum sci_status status; - enum sci_status phy_status; - - status = SCI_SUCCESS; - - for (index = 0; index < SCI_MAX_PHYS; index++) { - phy_status = sci_phy_stop(&ihost->phys[index]); - - if (phy_status != SCI_SUCCESS && - phy_status != SCI_FAILURE_INVALID_STATE) { - status = SCI_FAILURE; - - dev_warn(&ihost->pdev->dev, - "%s: Controller stop operation failed to stop " - "phy %d because of status %d.\n", - __func__, - ihost->phys[index].phy_index, phy_status); - } - } - - return status; -} - static enum sci_status sci_controller_stop_ports(struct isci_host *ihost) { u32 index; @@ -1595,10 +1517,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s { struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); - /* Stop all of the components for this controller */ - sci_controller_stop_phys(ihost); - sci_controller_stop_ports(ihost); sci_controller_stop_devices(ihost); + sci_controller_stop_ports(ihost); + + if (!sci_controller_has_remote_devices_stopping(ihost)) + isci_host_stop_complete(ihost); } static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm) @@ -1624,6 +1547,9 @@ static void sci_controller_reset_hardware(struct isci_host *ihost) /* The write to the UFQGP clears the UFQPR */ writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer); + + /* clear all interrupts */ + writel(~SMU_INTERRUPT_STATUS_RESERVED_MASK, &ihost->smu_registers->interrupt_status); } static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm) @@ -1655,59 +1581,9 @@ static const struct sci_base_state sci_controller_state_table[] = { .enter_state = sci_controller_stopping_state_enter, .exit_state = sci_controller_stopping_state_exit, }, - [SCIC_STOPPED] = {}, [SCIC_FAILED] = {} }; -static void sci_controller_set_default_config_parameters(struct isci_host *ihost) -{ - /* these defaults are overridden by the platform / firmware */ - u16 index; - - /* Default to APC mode. */ - ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; - - /* Default to APC mode. */ - ihost->oem_parameters.controller.max_concurr_spin_up = 1; - - /* Default to no SSC operation. */ - ihost->oem_parameters.controller.do_enable_ssc = false; - - /* Default to short cables on all phys. */ - ihost->oem_parameters.controller.cable_selection_mask = 0; - - /* Initialize all of the port parameter information to narrow ports. */ - for (index = 0; index < SCI_MAX_PORTS; index++) { - ihost->oem_parameters.ports[index].phy_mask = 0; - } - - /* Initialize all of the phy parameter information. */ - for (index = 0; index < SCI_MAX_PHYS; index++) { - /* Default to 3G (i.e. Gen 2). */ - ihost->user_parameters.phys[index].max_speed_generation = - SCIC_SDS_PARM_GEN2_SPEED; - - /* the frequencies cannot be 0 */ - ihost->user_parameters.phys[index].align_insertion_frequency = 0x7f; - ihost->user_parameters.phys[index].in_connection_align_insertion_frequency = 0xff; - ihost->user_parameters.phys[index].notify_enable_spin_up_insertion_frequency = 0x33; - - /* - * Previous Vitesse based expanders had a arbitration issue that - * is worked around by having the upper 32-bits of SAS address - * with a value greater then the Vitesse company identifier. - * Hence, usage of 0x5FCFFFFF. */ - ihost->oem_parameters.phys[index].sas_address.low = 0x1 + ihost->id; - ihost->oem_parameters.phys[index].sas_address.high = 0x5FCFFFFF; - } - - ihost->user_parameters.stp_inactivity_timeout = 5; - ihost->user_parameters.ssp_inactivity_timeout = 5; - ihost->user_parameters.stp_max_occupancy_timeout = 5; - ihost->user_parameters.ssp_max_occupancy_timeout = 20; - ihost->user_parameters.no_outbound_task_timeout = 2; -} - static void controller_timeout(unsigned long data) { struct sci_timer *tmr = (struct sci_timer *)data; @@ -1724,7 +1600,7 @@ static void controller_timeout(unsigned long data) sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT); else if (sm->current_state_id == SCIC_STOPPING) { sci_change_state(sm, SCIC_FAILED); - isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT); + isci_host_stop_complete(ihost); } else /* / @todo Now what do we want to do in this case? */ dev_err(&ihost->pdev->dev, "%s: Controller timer fired when controller was not " @@ -1764,9 +1640,6 @@ static enum sci_status sci_controller_construct(struct isci_host *ihost, sci_init_timer(&ihost->timer, controller_timeout); - /* Initialize the User and OEM parameters to default values. */ - sci_controller_set_default_config_parameters(ihost); - return sci_controller_reset(ihost); } @@ -1846,27 +1719,6 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version) return 0; } -static enum sci_status sci_oem_parameters_set(struct isci_host *ihost) -{ - u32 state = ihost->sm.current_state_id; - struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); - - if (state == SCIC_RESET || - state == SCIC_INITIALIZING || - state == SCIC_INITIALIZED) { - u8 oem_version = pci_info->orom ? pci_info->orom->hdr.version : - ISCI_ROM_VER_1_0; - - if (sci_oem_parameters_validate(&ihost->oem_parameters, - oem_version)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - return SCI_SUCCESS; - } - - return SCI_FAILURE_INVALID_STATE; -} - static u8 max_spin_up(struct isci_host *ihost) { if (ihost->user_parameters.max_concurr_spinup) @@ -1914,7 +1766,7 @@ static void power_control_timeout(unsigned long data) ihost->power_control.phys_granted_power++; sci_phy_consume_power_handler(iphy); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + if (iphy->protocol == SAS_PROTOCOL_SSP) { u8 j; for (j = 0; j < SCI_MAX_PHYS; j++) { @@ -1988,7 +1840,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost, sizeof(current_phy->frame_rcvd.iaf.sas_addr)); if (current_phy->sm.current_state_id == SCI_PHY_READY && - current_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS && + current_phy->protocol == SAS_PROTOCOL_SSP && other == 0) { sci_phy_consume_power_handler(iphy); break; @@ -2279,9 +2131,8 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost) unsigned long i, state, val; if (ihost->sm.current_state_id != SCIC_RESET) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller initialize operation requested " - "in invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2384,96 +2235,76 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost) return result; } -static enum sci_status sci_user_parameters_set(struct isci_host *ihost, - struct sci_user_parameters *sci_parms) -{ - u32 state = ihost->sm.current_state_id; - - if (state == SCIC_RESET || - state == SCIC_INITIALIZING || - state == SCIC_INITIALIZED) { - u16 index; - - /* - * Validate the user parameters. If they are not legal, then - * return a failure. - */ - for (index = 0; index < SCI_MAX_PHYS; index++) { - struct sci_phy_user_params *user_phy; - - user_phy = &sci_parms->phys[index]; - - if (!((user_phy->max_speed_generation <= - SCIC_SDS_PARM_MAX_SPEED) && - (user_phy->max_speed_generation > - SCIC_SDS_PARM_NO_SPEED))) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - if (user_phy->in_connection_align_insertion_frequency < - 3) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - if ((user_phy->in_connection_align_insertion_frequency < - 3) || - (user_phy->align_insertion_frequency == 0) || - (user_phy-> - notify_enable_spin_up_insertion_frequency == - 0)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - } - - if ((sci_parms->stp_inactivity_timeout == 0) || - (sci_parms->ssp_inactivity_timeout == 0) || - (sci_parms->stp_max_occupancy_timeout == 0) || - (sci_parms->ssp_max_occupancy_timeout == 0) || - (sci_parms->no_outbound_task_timeout == 0)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms)); - - return SCI_SUCCESS; - } - - return SCI_FAILURE_INVALID_STATE; -} - -static int sci_controller_mem_init(struct isci_host *ihost) +static int sci_controller_dma_alloc(struct isci_host *ihost) { struct device *dev = &ihost->pdev->dev; - dma_addr_t dma; size_t size; - int err; + int i; + + /* detect re-initialization */ + if (ihost->completion_queue) + return 0; size = SCU_MAX_COMPLETION_QUEUE_ENTRIES * sizeof(u32); - ihost->completion_queue = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL); + ihost->completion_queue = dmam_alloc_coherent(dev, size, &ihost->cq_dma, + GFP_KERNEL); if (!ihost->completion_queue) return -ENOMEM; - writel(lower_32_bits(dma), &ihost->smu_registers->completion_queue_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->completion_queue_upper); - size = ihost->remote_node_entries * sizeof(union scu_remote_node_context); - ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &dma, + ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &ihost->rnc_dma, GFP_KERNEL); + if (!ihost->remote_node_context_table) return -ENOMEM; - writel(lower_32_bits(dma), &ihost->smu_registers->remote_node_context_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->remote_node_context_upper); - size = ihost->task_context_entries * sizeof(struct scu_task_context), - ihost->task_context_table = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL); + ihost->task_context_table = dmam_alloc_coherent(dev, size, &ihost->tc_dma, + GFP_KERNEL); if (!ihost->task_context_table) return -ENOMEM; - ihost->task_context_dma = dma; - writel(lower_32_bits(dma), &ihost->smu_registers->host_task_table_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->host_task_table_upper); + size = SCI_UFI_TOTAL_SIZE; + ihost->ufi_buf = dmam_alloc_coherent(dev, size, &ihost->ufi_dma, GFP_KERNEL); + if (!ihost->ufi_buf) + return -ENOMEM; + + for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { + struct isci_request *ireq; + dma_addr_t dma; + + ireq = dmam_alloc_coherent(dev, sizeof(*ireq), &dma, GFP_KERNEL); + if (!ireq) + return -ENOMEM; + + ireq->tc = &ihost->task_context_table[i]; + ireq->owning_controller = ihost; + ireq->request_daddr = dma; + ireq->isci_host = ihost; + ihost->reqs[i] = ireq; + } + + return 0; +} + +static int sci_controller_mem_init(struct isci_host *ihost) +{ + int err = sci_controller_dma_alloc(ihost); - err = sci_unsolicited_frame_control_construct(ihost); if (err) return err; + writel(lower_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_lower); + writel(upper_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_upper); + + writel(lower_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_lower); + writel(upper_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_upper); + + writel(lower_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_lower); + writel(upper_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_upper); + + sci_unsolicited_frame_control_construct(ihost); + /* * Inform the silicon as to the location of the UF headers and * address table. @@ -2491,22 +2322,22 @@ static int sci_controller_mem_init(struct isci_host *ihost) return 0; } +/** + * isci_host_init - (re-)initialize hardware and internal (private) state + * @ihost: host to init + * + * Any public facing objects (like asd_sas_port, and asd_sas_phys), or + * one-time initialization objects like locks and waitqueues, are + * not touched (they are initialized in isci_host_alloc) + */ int isci_host_init(struct isci_host *ihost) { - int err = 0, i; + int i, err; enum sci_status status; - struct sci_user_parameters sci_user_params; - struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); - - spin_lock_init(&ihost->state_lock); - spin_lock_init(&ihost->scic_lock); - init_waitqueue_head(&ihost->eventq); - - isci_host_change_state(ihost, isci_starting); - - status = sci_controller_construct(ihost, scu_base(ihost), - smu_base(ihost)); + spin_lock_irq(&ihost->scic_lock); + status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); + spin_unlock_irq(&ihost->scic_lock); if (status != SCI_SUCCESS) { dev_err(&ihost->pdev->dev, "%s: sci_controller_construct failed - status = %x\n", @@ -2515,48 +2346,6 @@ int isci_host_init(struct isci_host *ihost) return -ENODEV; } - ihost->sas_ha.dev = &ihost->pdev->dev; - ihost->sas_ha.lldd_ha = ihost; - - /* - * grab initial values stored in the controller object for OEM and USER - * parameters - */ - isci_user_parameters_get(&sci_user_params); - status = sci_user_parameters_set(ihost, &sci_user_params); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "%s: sci_user_parameters_set failed\n", - __func__); - return -ENODEV; - } - - /* grab any OEM parameters specified in orom */ - if (pci_info->orom) { - status = isci_parse_oem_parameters(&ihost->oem_parameters, - pci_info->orom, - ihost->id); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "parsing firmware oem parameters failed\n"); - return -EINVAL; - } - } - - status = sci_oem_parameters_set(ihost); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "%s: sci_oem_parameters_set failed\n", - __func__); - return -ENODEV; - } - - tasklet_init(&ihost->completion_tasklet, - isci_host_completion_routine, (unsigned long)ihost); - - INIT_LIST_HEAD(&ihost->requests_to_complete); - INIT_LIST_HEAD(&ihost->requests_to_errorback); - spin_lock_irq(&ihost->scic_lock); status = sci_controller_initialize(ihost); spin_unlock_irq(&ihost->scic_lock); @@ -2572,43 +2361,12 @@ int isci_host_init(struct isci_host *ihost) if (err) return err; - for (i = 0; i < SCI_MAX_PORTS; i++) - isci_port_init(&ihost->ports[i], ihost, i); - - 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]; - - INIT_LIST_HEAD(&idev->reqs_in_process); - INIT_LIST_HEAD(&idev->node); - } - - for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { - struct isci_request *ireq; - dma_addr_t dma; - - ireq = dmam_alloc_coherent(&ihost->pdev->dev, - sizeof(struct isci_request), &dma, - GFP_KERNEL); - if (!ireq) - return -ENOMEM; - - ireq->tc = &ihost->task_context_table[i]; - ireq->owning_controller = ihost; - spin_lock_init(&ireq->state_lock); - ireq->request_daddr = dma; - ireq->isci_host = ihost; - ihost->reqs[i] = ireq; - } - return 0; } @@ -2654,7 +2412,7 @@ void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport, } } -static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) +bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) { u32 index; @@ -2680,7 +2438,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost, } if (!sci_controller_has_remote_devices_stopping(ihost)) - sci_change_state(&ihost->sm, SCIC_STOPPED); + isci_host_stop_complete(ihost); } void sci_controller_post_request(struct isci_host *ihost, u32 request) @@ -2842,7 +2600,8 @@ enum sci_status sci_controller_start_io(struct isci_host *ihost, enum sci_status status; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, "invalid state to start I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2866,22 +2625,26 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, enum sci_status status; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, - "invalid state to terminate request\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } - status = sci_io_request_terminate(ireq); - if (status != SCI_SUCCESS) - return status; - /* - * Utilize the original post context command and or in the POST_TC_ABORT - * request sub-type. - */ - sci_controller_post_request(ihost, - ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); - return SCI_SUCCESS; + dev_dbg(&ihost->pdev->dev, "%s: status=%d; ireq=%p; flags=%lx\n", + __func__, status, ireq, ireq->flags); + + if ((status == SCI_SUCCESS) && + !test_bit(IREQ_PENDING_ABORT, &ireq->flags) && + !test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) { + /* Utilize the original post context command and or in the + * POST_TC_ABORT request sub-type. + */ + sci_controller_post_request( + ihost, ireq->post_context | + SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); + } + return status; } /** @@ -2915,7 +2678,8 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost, clear_bit(IREQ_ACTIVE, &ireq->flags); return SCI_SUCCESS; default: - dev_warn(&ihost->pdev->dev, "invalid state to complete I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2926,7 +2690,8 @@ enum sci_status sci_controller_continue_io(struct isci_request *ireq) struct isci_host *ihost = ireq->owning_controller; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, "invalid state to continue I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index adbad69d106..9ab58e0540e 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -55,6 +55,7 @@ #ifndef _SCI_HOST_H_ #define _SCI_HOST_H_ +#include <scsi/sas_ata.h> #include "remote_device.h" #include "phy.h" #include "isci.h" @@ -108,6 +109,8 @@ struct sci_port_configuration_agent; typedef void (*port_config_fn)(struct isci_host *, struct sci_port_configuration_agent *, struct isci_port *, struct isci_phy *); +bool is_port_config_apc(struct isci_host *ihost); +bool is_controller_start_complete(struct isci_host *ihost); struct sci_port_configuration_agent { u16 phy_configured_mask; @@ -157,13 +160,17 @@ struct isci_host { struct sci_power_control power_control; u8 io_request_sequence[SCI_MAX_IO_REQUESTS]; struct scu_task_context *task_context_table; - dma_addr_t task_context_dma; + dma_addr_t tc_dma; union scu_remote_node_context *remote_node_context_table; + dma_addr_t rnc_dma; u32 *completion_queue; + dma_addr_t cq_dma; u32 completion_queue_get; u32 logical_port_entries; u32 remote_node_entries; u32 task_context_entries; + void *ufi_buf; + dma_addr_t ufi_dma; struct sci_unsolicited_frame_control uf_control; /* phy startup */ @@ -190,17 +197,13 @@ struct isci_host { struct asd_sas_port sas_ports[SCI_MAX_PORTS]; struct sas_ha_struct sas_ha; - spinlock_t state_lock; struct pci_dev *pdev; - enum isci_status status; #define IHOST_START_PENDING 0 #define IHOST_STOP_PENDING 1 + #define IHOST_IRQ_ENABLED 2 unsigned long flags; wait_queue_head_t eventq; - struct Scsi_Host *shost; struct tasklet_struct completion_tasklet; - struct list_head requests_to_complete; - struct list_head requests_to_errorback; spinlock_t scic_lock; struct isci_request *reqs[SCI_MAX_IO_REQUESTS]; struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES]; @@ -274,13 +277,6 @@ enum sci_controller_states { SCIC_STOPPING, /** - * This state indicates that the controller has successfully been stopped. - * In this state no new IO operations are permitted. - * This state is entered from the STOPPING state. - */ - SCIC_STOPPED, - - /** * This state indicates that the controller could not successfully be * initialized. In this state no new IO operations are permitted. * This state is entered from the INITIALIZING state. @@ -309,32 +305,16 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) return pci_get_drvdata(pdev); } +static inline struct Scsi_Host *to_shost(struct isci_host *ihost) +{ + return ihost->sas_ha.core.shost; +} + #define for_each_isci_host(id, ihost, pdev) \ for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \ id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \ ihost = to_pci_info(pdev)->hosts[++id]) -static inline enum isci_status isci_host_get_state(struct isci_host *isci_host) -{ - return isci_host->status; -} - -static inline void isci_host_change_state(struct isci_host *isci_host, - enum isci_status status) -{ - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_host = %p, state = 0x%x", - __func__, - isci_host, - status); - spin_lock_irqsave(&isci_host->state_lock, flags); - isci_host->status = status; - spin_unlock_irqrestore(&isci_host->state_lock, flags); - -} - static inline void wait_for_start(struct isci_host *ihost) { wait_event(ihost->eventq, !test_bit(IHOST_START_PENDING, &ihost->flags)); @@ -360,6 +340,11 @@ static inline struct isci_host *dev_to_ihost(struct domain_device *dev) return dev->port->ha->lldd_ha; } +static inline struct isci_host *idev_to_ihost(struct isci_remote_device *idev) +{ + return dev_to_ihost(idev->domain_dev); +} + /* we always use protocol engine group zero */ #define ISCI_PEG 0 @@ -378,8 +363,7 @@ static inline int sci_remote_device_node_count(struct isci_remote_device *idev) { struct domain_device *dev = idev->domain_dev; - if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) && - !idev->is_direct_attached) + if (dev_is_sata(dev) && dev->parent) return SCU_STP_REMOTE_NODE_COUNT; return SCU_SSP_REMOTE_NODE_COUNT; } @@ -475,36 +459,17 @@ void sci_controller_free_remote_node_context( struct isci_remote_device *idev, u16 node_id); -struct isci_request *sci_request_by_tag(struct isci_host *ihost, - u16 io_tag); - -void sci_controller_power_control_queue_insert( - struct isci_host *ihost, - struct isci_phy *iphy); - -void sci_controller_power_control_queue_remove( - struct isci_host *ihost, - struct isci_phy *iphy); - -void sci_controller_link_up( - struct isci_host *ihost, - struct isci_port *iport, - struct isci_phy *iphy); - -void sci_controller_link_down( - struct isci_host *ihost, - struct isci_port *iport, - struct isci_phy *iphy); - -void sci_controller_remote_device_stopped( - struct isci_host *ihost, - struct isci_remote_device *idev); - -void sci_controller_copy_task_context( - struct isci_host *ihost, - struct isci_request *ireq); - -void sci_controller_register_setup(struct isci_host *ihost); +struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag); +void sci_controller_power_control_queue_insert(struct isci_host *ihost, + struct isci_phy *iphy); +void sci_controller_power_control_queue_remove(struct isci_host *ihost, + struct isci_phy *iphy); +void sci_controller_link_up(struct isci_host *ihost, struct isci_port *iport, + struct isci_phy *iphy); +void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport, + struct isci_phy *iphy); +void sci_controller_remote_device_stopped(struct isci_host *ihost, + struct isci_remote_device *idev); enum sci_status sci_controller_continue_io(struct isci_request *ireq); int isci_host_scan_finished(struct Scsi_Host *, unsigned long); @@ -512,29 +477,14 @@ void isci_host_scan_start(struct Scsi_Host *); u16 isci_alloc_tag(struct isci_host *ihost); enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag); void isci_tci_free(struct isci_host *ihost, u16 tci); +void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task); int isci_host_init(struct isci_host *); - -void isci_host_init_controller_names( - struct isci_host *isci_host, - unsigned int controller_idx); - -void isci_host_deinit( - struct isci_host *); - -void isci_host_port_link_up( - struct isci_host *, - struct isci_port *, - struct isci_phy *); -int isci_host_dev_found(struct domain_device *); - -void isci_host_remote_device_start_complete( - struct isci_host *, - struct isci_remote_device *, - enum sci_status); - -void sci_controller_disable_interrupts( - struct isci_host *ihost); +void isci_host_completion_routine(unsigned long data); +void isci_host_deinit(struct isci_host *); +void sci_controller_disable_interrupts(struct isci_host *ihost); +bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost); +void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status); enum sci_status sci_controller_start_io( struct isci_host *ihost, diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 5137db5a5d8..47e28b55502 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -271,13 +271,12 @@ static void isci_unregister(struct isci_host *isci_host) if (!isci_host) return; - shost = isci_host->shost; - sas_unregister_ha(&isci_host->sas_ha); - sas_remove_host(isci_host->shost); - scsi_remove_host(isci_host->shost); - scsi_host_put(isci_host->shost); + shost = to_shost(isci_host); + sas_remove_host(shost); + scsi_remove_host(shost); + scsi_host_put(shost); } static int __devinit isci_pci_init(struct pci_dev *pdev) @@ -397,38 +396,199 @@ static int isci_setup_interrupts(struct pci_dev *pdev) return err; } +static void isci_user_parameters_get(struct sci_user_parameters *u) +{ + int i; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + struct sci_phy_user_params *u_phy = &u->phys[i]; + + u_phy->max_speed_generation = phy_gen; + + /* we are not exporting these for now */ + u_phy->align_insertion_frequency = 0x7f; + u_phy->in_connection_align_insertion_frequency = 0xff; + u_phy->notify_enable_spin_up_insertion_frequency = 0x33; + } + + u->stp_inactivity_timeout = stp_inactive_to; + u->ssp_inactivity_timeout = ssp_inactive_to; + 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_concurr_spinup = max_concurr_spinup; +} + +static enum sci_status sci_user_parameters_set(struct isci_host *ihost, + struct sci_user_parameters *sci_parms) +{ + u16 index; + + /* + * Validate the user parameters. If they are not legal, then + * return a failure. + */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + struct sci_phy_user_params *u; + + u = &sci_parms->phys[index]; + + if (!((u->max_speed_generation <= SCIC_SDS_PARM_MAX_SPEED) && + (u->max_speed_generation > SCIC_SDS_PARM_NO_SPEED))) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + if (u->in_connection_align_insertion_frequency < 3) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + if ((u->in_connection_align_insertion_frequency < 3) || + (u->align_insertion_frequency == 0) || + (u->notify_enable_spin_up_insertion_frequency == 0)) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + + if ((sci_parms->stp_inactivity_timeout == 0) || + (sci_parms->ssp_inactivity_timeout == 0) || + (sci_parms->stp_max_occupancy_timeout == 0) || + (sci_parms->ssp_max_occupancy_timeout == 0) || + (sci_parms->no_outbound_task_timeout == 0)) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms)); + + return SCI_SUCCESS; +} + +static void sci_oem_defaults(struct isci_host *ihost) +{ + /* these defaults are overridden by the platform / firmware */ + struct sci_user_parameters *user = &ihost->user_parameters; + struct sci_oem_params *oem = &ihost->oem_parameters; + int i; + + /* Default to APC mode. */ + oem->controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; + + /* Default to APC mode. */ + oem->controller.max_concurr_spin_up = 1; + + /* Default to no SSC operation. */ + oem->controller.do_enable_ssc = false; + + /* Default to short cables on all phys. */ + oem->controller.cable_selection_mask = 0; + + /* Initialize all of the port parameter information to narrow ports. */ + for (i = 0; i < SCI_MAX_PORTS; i++) + oem->ports[i].phy_mask = 0; + + /* Initialize all of the phy parameter information. */ + for (i = 0; i < SCI_MAX_PHYS; i++) { + /* Default to 3G (i.e. Gen 2). */ + user->phys[i].max_speed_generation = SCIC_SDS_PARM_GEN2_SPEED; + + /* the frequencies cannot be 0 */ + user->phys[i].align_insertion_frequency = 0x7f; + user->phys[i].in_connection_align_insertion_frequency = 0xff; + user->phys[i].notify_enable_spin_up_insertion_frequency = 0x33; + + /* Previous Vitesse based expanders had a arbitration issue that + * is worked around by having the upper 32-bits of SAS address + * with a value greater then the Vitesse company identifier. + * Hence, usage of 0x5FCFFFFF. + */ + oem->phys[i].sas_address.low = 0x1 + ihost->id; + oem->phys[i].sas_address.high = 0x5FCFFFFF; + } + + user->stp_inactivity_timeout = 5; + user->ssp_inactivity_timeout = 5; + user->stp_max_occupancy_timeout = 5; + user->ssp_max_occupancy_timeout = 20; + user->no_outbound_task_timeout = 2; +} + static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) { - struct isci_host *isci_host; + struct isci_orom *orom = to_pci_info(pdev)->orom; + struct sci_user_parameters sci_user_params; + u8 oem_version = ISCI_ROM_VER_1_0; + struct isci_host *ihost; struct Scsi_Host *shost; - int err; + int err, i; - isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL); - if (!isci_host) + ihost = devm_kzalloc(&pdev->dev, sizeof(*ihost), GFP_KERNEL); + if (!ihost) return NULL; - isci_host->pdev = pdev; - isci_host->id = id; + ihost->pdev = pdev; + ihost->id = id; + spin_lock_init(&ihost->scic_lock); + init_waitqueue_head(&ihost->eventq); + ihost->sas_ha.dev = &ihost->pdev->dev; + ihost->sas_ha.lldd_ha = ihost; + tasklet_init(&ihost->completion_tasklet, + isci_host_completion_routine, (unsigned long)ihost); + + /* validate module parameters */ + /* TODO: kill struct sci_user_parameters and reference directly */ + sci_oem_defaults(ihost); + isci_user_parameters_get(&sci_user_params); + if (sci_user_parameters_set(ihost, &sci_user_params)) { + dev_warn(&pdev->dev, + "%s: sci_user_parameters_set failed\n", __func__); + return NULL; + } + + /* sanity check platform (or 'firmware') oem parameters */ + if (orom) { + if (id < 0 || id >= SCI_MAX_CONTROLLERS || id > orom->hdr.num_elements) { + dev_warn(&pdev->dev, "parsing firmware oem parameters failed\n"); + return NULL; + } + ihost->oem_parameters = orom->ctrl[id]; + oem_version = orom->hdr.version; + } + + /* validate oem parameters (platform, firmware, or built-in defaults) */ + if (sci_oem_parameters_validate(&ihost->oem_parameters, oem_version)) { + dev_warn(&pdev->dev, "oem parameter validation failed\n"); + return NULL; + } + + for (i = 0; i < SCI_MAX_PORTS; i++) { + struct isci_port *iport = &ihost->ports[i]; + + INIT_LIST_HEAD(&iport->remote_dev_list); + iport->isci_host = ihost; + } + + for (i = 0; i < SCI_MAX_PHYS; i++) + isci_phy_init(&ihost->phys[i], ihost, i); + + for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { + struct isci_remote_device *idev = &ihost->devices[i]; + + INIT_LIST_HEAD(&idev->node); + } shost = scsi_host_alloc(&isci_sht, sizeof(void *)); if (!shost) return NULL; - isci_host->shost = shost; dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: " "{%s, %s, %s, %s}\n", - (is_cable_select_overridden() ? "* " : ""), isci_host->id, - lookup_cable_names(decode_cable_selection(isci_host, 3)), - lookup_cable_names(decode_cable_selection(isci_host, 2)), - lookup_cable_names(decode_cable_selection(isci_host, 1)), - lookup_cable_names(decode_cable_selection(isci_host, 0))); + (is_cable_select_overridden() ? "* " : ""), ihost->id, + lookup_cable_names(decode_cable_selection(ihost, 3)), + lookup_cable_names(decode_cable_selection(ihost, 2)), + lookup_cable_names(decode_cable_selection(ihost, 1)), + lookup_cable_names(decode_cable_selection(ihost, 0))); - err = isci_host_init(isci_host); + err = isci_host_init(ihost); if (err) goto err_shost; - SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha; - isci_host->sas_ha.core.shost = shost; + SHOST_TO_SAS_HA(shost) = &ihost->sas_ha; + ihost->sas_ha.core.shost = shost; shost->transportt = isci_transport_template; shost->max_id = ~0; @@ -439,11 +599,11 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) if (err) goto err_shost; - err = isci_register_sas_ha(isci_host); + err = isci_register_sas_ha(ihost); if (err) goto err_shost_remove; - return isci_host; + return ihost; err_shost_remove: scsi_remove_host(shost); @@ -476,7 +636,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic if (!orom) orom = isci_request_oprom(pdev); - for (i = 0; orom && i < ARRAY_SIZE(orom->ctrl); i++) { + for (i = 0; orom && i < num_controllers(pdev); i++) { if (sci_oem_parameters_validate(&orom->ctrl[i], orom->hdr.version)) { dev_warn(&pdev->dev, @@ -525,11 +685,11 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic pci_info->hosts[i] = h; /* turn on DIF support */ - scsi_host_set_prot(h->shost, + scsi_host_set_prot(to_shost(h), SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | SHOST_DIF_TYPE3_PROTECTION); - scsi_host_set_guard(h->shost, SHOST_DIX_GUARD_CRC); + scsi_host_set_guard(to_shost(h), SHOST_DIX_GUARD_CRC); } err = isci_setup_interrupts(pdev); @@ -537,7 +697,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic goto err_host_alloc; for_each_isci_host(i, isci_host, pdev) - scsi_scan_host(isci_host->shost); + scsi_scan_host(to_shost(isci_host)); return 0; diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index fab3586840b..18f43d4c30b 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -580,7 +580,7 @@ static void sci_phy_start_sas_link_training(struct isci_phy *iphy) sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SAS_SPEED_EN); - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SAS; + iphy->protocol = SAS_PROTOCOL_SSP; } static void sci_phy_start_sata_link_training(struct isci_phy *iphy) @@ -591,7 +591,7 @@ static void sci_phy_start_sata_link_training(struct isci_phy *iphy) */ sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_POWER); - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; + iphy->protocol = SAS_PROTOCOL_SATA; } /** @@ -668,6 +668,19 @@ static const char *phy_event_name(u32 event_code) phy_to_host(iphy)->id, iphy->phy_index, \ phy_state_name(state), phy_event_name(code), code) + +void scu_link_layer_set_txcomsas_timeout(struct isci_phy *iphy, u32 timeout) +{ + u32 val; + + /* Extend timeout */ + val = readl(&iphy->link_layer_registers->transmit_comsas_signal); + val &= ~SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK); + val |= SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, timeout); + + writel(val, &iphy->link_layer_registers->transmit_comsas_signal); +} + enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) { enum sci_phy_states state = iphy->sm.current_state_id; @@ -683,6 +696,13 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); iphy->is_in_link_training = true; break; + case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; default: phy_event_dbg(iphy, state, event_code); return SCI_FAILURE; @@ -717,9 +737,19 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; + case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend the timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; default: phy_event_warn(iphy, state, event_code); return SCI_FAILURE; @@ -740,7 +770,14 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); break; case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend the timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; case SCU_EVENT_LINK_FAILURE: + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); case SCU_EVENT_HARD_RESET_RECEIVED: /* Start the oob/sn state machine over again */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); @@ -753,6 +790,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SAS_POWER: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -764,6 +804,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SATA_POWER: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -788,6 +831,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SATA_PHY_EN: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -797,7 +843,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) */ break; case SCU_EVENT_SATA_PHY_DETECTED: - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; + iphy->protocol = SAS_PROTOCOL_SATA; /* We have received the SATA PHY notification change state */ sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_SPEED_EN); @@ -836,6 +882,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) SCI_PHY_SUB_AWAIT_SIG_FIS_UF); break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -859,6 +908,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -871,16 +923,26 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_READY: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Set default timeout */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; case SCU_EVENT_BROADCAST_CHANGE: + case SCU_EVENT_BROADCAST_SES: + case SCU_EVENT_BROADCAST_RESERVED0: + case SCU_EVENT_BROADCAST_RESERVED1: + case SCU_EVENT_BROADCAST_EXPANDER: + case SCU_EVENT_BROADCAST_AEN: /* Broadcast change received. Notify the port. */ if (phy_get_non_dummy_port(iphy) != NULL) sci_port_broadcast_change_received(iphy->owning_port, iphy); else iphy->bcn_received_while_port_unassigned = true; break; + case SCU_EVENT_BROADCAST_RESERVED3: + case SCU_EVENT_BROADCAST_RESERVED4: default: phy_event_warn(iphy, state, event_code); return SCI_FAILURE_INVALID_STATE; @@ -1215,7 +1277,7 @@ static void sci_phy_starting_state_enter(struct sci_base_state_machine *sm) scu_link_layer_start_oob(iphy); /* We don't know what kind of phy we are going to be just yet */ - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + iphy->protocol = SAS_PROTOCOL_NONE; iphy->bcn_received_while_port_unassigned = false; if (iphy->sm.previous_state_id == SCI_PHY_READY) @@ -1250,7 +1312,7 @@ static void sci_phy_resetting_state_enter(struct sci_base_state_machine *sm) */ sci_port_deactivate_phy(iphy->owning_port, iphy, false); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + if (iphy->protocol == SAS_PROTOCOL_SSP) { scu_link_layer_tx_hard_reset(iphy); } else { /* The SCU does not need to have a discrete reset state so @@ -1316,7 +1378,7 @@ void sci_phy_construct(struct isci_phy *iphy, iphy->owning_port = iport; iphy->phy_index = phy_index; iphy->bcn_received_while_port_unassigned = false; - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + iphy->protocol = SAS_PROTOCOL_NONE; iphy->link_layer_registers = NULL; iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN; @@ -1380,12 +1442,14 @@ int isci_phy_control(struct asd_sas_phy *sas_phy, switch (func) { case PHY_FUNC_DISABLE: spin_lock_irqsave(&ihost->scic_lock, flags); + scu_link_layer_start_oob(iphy); sci_phy_stop(iphy); spin_unlock_irqrestore(&ihost->scic_lock, flags); break; case PHY_FUNC_LINK_RESET: spin_lock_irqsave(&ihost->scic_lock, flags); + scu_link_layer_start_oob(iphy); sci_phy_stop(iphy); sci_phy_start(iphy); spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h index 0e45833ba06..45fecfa36a9 100644 --- a/drivers/scsi/isci/phy.h +++ b/drivers/scsi/isci/phy.h @@ -76,13 +76,6 @@ */ #define SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT 250 -enum sci_phy_protocol { - SCIC_SDS_PHY_PROTOCOL_UNKNOWN, - SCIC_SDS_PHY_PROTOCOL_SAS, - SCIC_SDS_PHY_PROTOCOL_SATA, - SCIC_SDS_MAX_PHY_PROTOCOLS -}; - /** * isci_phy - hba local phy infrastructure * @sm: @@ -95,7 +88,7 @@ struct isci_phy { struct sci_base_state_machine sm; struct isci_port *owning_port; enum sas_linkrate max_negotiated_speed; - enum sci_phy_protocol protocol; + enum sas_protocol protocol; u8 phy_index; bool bcn_received_while_port_unassigned; bool is_in_link_training; diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 5fada73b71f..2fb85bf7544 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -184,7 +184,7 @@ static void isci_port_link_up(struct isci_host *isci_host, sci_port_get_properties(iport, &properties); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) { + if (iphy->protocol == SAS_PROTOCOL_SATA) { u64 attached_sas_address; iphy->sas_phy.oob_mode = SATA_OOB_MODE; @@ -204,7 +204,7 @@ static void isci_port_link_up(struct isci_host *isci_host, memcpy(&iphy->sas_phy.attached_sas_addr, &attached_sas_address, sizeof(attached_sas_address)); - } else if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + } else if (iphy->protocol == SAS_PROTOCOL_SSP) { iphy->sas_phy.oob_mode = SAS_OOB_MODE; iphy->sas_phy.frame_rcvd_size = sizeof(struct sas_identify_frame); @@ -251,10 +251,10 @@ static void isci_port_link_down(struct isci_host *isci_host, if (isci_phy->sas_phy.port && isci_phy->sas_phy.port->num_phys == 1) { /* 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 - * remove the target - */ + * next task sent to this device will be returned as + * SAS_TASK_UNDELIVERED, and the scsi mid layer will + * remove the target + */ list_for_each_entry(isci_device, &isci_port->remote_dev_list, node) { @@ -517,7 +517,7 @@ void sci_port_get_attached_sas_address(struct isci_port *iport, struct sci_sas_a */ iphy = sci_port_get_a_connected_phy(iport); if (iphy) { - if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA) { + if (iphy->protocol != SAS_PROTOCOL_SATA) { sci_phy_get_attached_sas_address(iphy, sas); } else { sci_phy_get_sas_address(iphy, sas); @@ -624,7 +624,7 @@ static void sci_port_activate_phy(struct isci_port *iport, { struct isci_host *ihost = iport->owning_controller; - if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA && (flags & PF_RESUME)) + if (iphy->protocol != SAS_PROTOCOL_SATA && (flags & PF_RESUME)) sci_phy_resume(iphy); iport->active_phy_mask |= 1 << iphy->phy_index; @@ -751,12 +751,10 @@ static bool sci_port_is_wide(struct isci_port *iport) * wide ports and direct attached phys. Since there are no wide ported SATA * devices this could become an invalid port configuration. */ -bool sci_port_link_detected( - struct isci_port *iport, - struct isci_phy *iphy) +bool sci_port_link_detected(struct isci_port *iport, struct isci_phy *iphy) { if ((iport->logical_port_index != SCIC_SDS_DUMMY_PORT) && - (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA)) { + (iphy->protocol == SAS_PROTOCOL_SATA)) { if (sci_port_is_wide(iport)) { sci_port_invalid_link_up(iport, iphy); return false; @@ -1201,6 +1199,8 @@ enum sci_status sci_port_add_phy(struct isci_port *iport, enum sci_status status; enum sci_port_states state; + sci_port_bcn_enable(iport); + state = iport->sm.current_state_id; switch (state) { case SCI_PORT_STOPPED: { @@ -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; @@ -1608,13 +1632,6 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->phy_table[index] = NULL; } -void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index) -{ - INIT_LIST_HEAD(&iport->remote_dev_list); - INIT_LIST_HEAD(&iport->domain_dev_list); - iport->isci_host = ihost; -} - void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy *iphy) { struct isci_host *ihost = iport->owning_controller; @@ -1671,17 +1688,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor __func__, iport, status); } - - /* If the hard reset for the port has failed, consider this - * the same as link failures on all phys in the port. - */ - if (ret != TMF_RESP_FUNC_COMPLETE) { - - dev_err(&ihost->pdev->dev, - "%s: iport = %p; hard reset failed " - "(0x%x) - driving explicit link fail for all phys\n", - __func__, iport, iport->hard_reset_status); - } return ret; } @@ -1740,7 +1746,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; diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index 6b56240c205..861e8f72811 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -97,7 +97,6 @@ enum isci_status { struct isci_port { struct isci_host *isci_host; struct list_head remote_dev_list; - struct list_head domain_dev_list; #define IPORT_RESET_PENDING 0 unsigned long state; enum sci_status hard_reset_status; @@ -112,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; @@ -270,14 +270,13 @@ 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 *); -void isci_port_init( - struct isci_port *port, - struct isci_host *host, - int index); - int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport, struct isci_phy *iphy); int isci_ata_check_ready(struct domain_device *dev); diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 6d1e9544cbe..cd962da4a57 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -57,7 +57,7 @@ #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10) #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10) -#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (250) +#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000) enum SCIC_SDS_APC_ACTIVITY { SCIC_SDS_APC_SKIP_PHY, @@ -472,13 +472,9 @@ sci_apc_agent_validate_phy_configuration(struct isci_host *ihost, * down event or a link up event where we can not yet tell to which a phy * belongs. */ -static void sci_apc_agent_start_timer( - struct sci_port_configuration_agent *port_agent, - u32 timeout) +static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent, + u32 timeout) { - if (port_agent->timer_pending) - sci_del_timer(&port_agent->timer); - port_agent->timer_pending = true; sci_mod_timer(&port_agent->timer, timeout); } @@ -697,6 +693,9 @@ static void apc_agent_timeout(unsigned long data) &ihost->phys[index], false); } + if (is_controller_start_complete(ihost)) + sci_controller_transition_to_ready(ihost, SCI_SUCCESS); + done: spin_unlock_irqrestore(&ihost->scic_lock, flags); } @@ -732,6 +731,11 @@ void sci_port_configuration_agent_construct( } } +bool is_port_config_apc(struct isci_host *ihost) +{ + return ihost->port_agent.link_up_handler == sci_apc_agent_link_up; +} + enum sci_status sci_port_configuration_agent_initialize( struct isci_host *ihost, struct sci_port_configuration_agent *port_agent) diff --git a/drivers/scsi/isci/probe_roms.c b/drivers/scsi/isci/probe_roms.c index 9b8117b9d75..4d95654c3fd 100644 --- a/drivers/scsi/isci/probe_roms.c +++ b/drivers/scsi/isci/probe_roms.c @@ -112,18 +112,6 @@ struct isci_orom *isci_request_oprom(struct pci_dev *pdev) return rom; } -enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem, - struct isci_orom *orom, int scu_index) -{ - /* check for valid inputs */ - if (scu_index < 0 || scu_index >= SCI_MAX_CONTROLLERS || - scu_index > orom->hdr.num_elements || !oem) - return -EINVAL; - - *oem = orom->ctrl[scu_index]; - return 0; -} - struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw) { struct isci_orom *orom = NULL, *data; diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index bb0e9d4d97c..e08b578241f 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -156,8 +156,6 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version); struct isci_orom; struct isci_orom *isci_request_oprom(struct pci_dev *pdev); -enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem, - struct isci_orom *orom, int scu_index); struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw); struct isci_orom *isci_get_efi_var(struct pci_dev *pdev); diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h index 7eb0ccd45fe..97f3ceb8d72 100644 --- a/drivers/scsi/isci/registers.h +++ b/drivers/scsi/isci/registers.h @@ -1239,6 +1239,14 @@ struct scu_transport_layer_registers { #define SCU_SAS_LLCTL_GEN_BIT(name) \ SCU_GEN_BIT(SCU_SAS_LINK_LAYER_CONTROL_ ## name) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT (0xF0) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED (0x1FF) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_SHIFT (0) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK (0x3FF) + +#define SCU_SAS_LLTXCOMSAS_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SAS_LINK_LAYER_TXCOMSAS_ ## name, value) + /* #define SCU_FRXHECR_DCNT_OFFSET 0x00B0 */ #define SCU_PSZGCR_OFFSET 0x00E4 diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 8f501b0a81d..c3aa6c5457b 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -72,46 +72,11 @@ const char *dev_state_name(enum sci_remote_device_states state) } #undef C -/** - * isci_remote_device_not_ready() - This function is called by the ihost when - * the remote device is not ready. We mark the isci device as ready (not - * "ready_for_io") and signal the waiting proccess. - * @isci_host: This parameter specifies the isci host object. - * @isci_device: This parameter specifies the remote device - * - * sci_lock is held on entrance to this function. - */ -static void isci_remote_device_not_ready(struct isci_host *ihost, - struct isci_remote_device *idev, u32 reason) +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, + enum sci_remote_node_suspension_reasons reason) { - struct isci_request *ireq; - - dev_dbg(&ihost->pdev->dev, - "%s: isci_device = %p\n", __func__, idev); - - switch (reason) { - case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: - set_bit(IDEV_GONE, &idev->flags); - break; - case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: - set_bit(IDEV_IO_NCQERROR, &idev->flags); - - /* Kill all outstanding requests for the device. */ - list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) { - - dev_dbg(&ihost->pdev->dev, - "%s: isci_device = %p request = %p\n", - __func__, idev, ireq); - - sci_controller_terminate_request(ihost, - idev, - ireq); - } - /* Fall through into the default case... */ - default: - clear_bit(IDEV_IO_READY, &idev->flags); - break; - } + return sci_remote_node_context_suspend(&idev->rnc, reason, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); } /** @@ -133,18 +98,29 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote wake_up(&ihost->eventq); } -/* called once the remote node context is ready to be freed. - * The remote device can now report that its stop operation is complete. none - */ -static void rnc_destruct_done(void *_dev) +static enum sci_status sci_remote_device_terminate_req( + struct isci_host *ihost, + struct isci_remote_device *idev, + int check_abort, + struct isci_request *ireq) { - struct isci_remote_device *idev = _dev; + if (!test_bit(IREQ_ACTIVE, &ireq->flags) || + (ireq->target_device != idev) || + (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags))) + return SCI_SUCCESS; - BUG_ON(idev->started_request_count != 0); - sci_change_state(&idev->sm, SCI_DEV_STOPPED); + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p; flags=%lx; req=%p; req target=%p\n", + __func__, idev, idev->flags, ireq, ireq->target_device); + + set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); + + return sci_controller_terminate_request(ihost, idev, ireq); } -static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev) +static enum sci_status sci_remote_device_terminate_reqs_checkabort( + struct isci_remote_device *idev, + int chk) { struct isci_host *ihost = idev->owning_port->owning_controller; enum sci_status status = SCI_SUCCESS; @@ -154,18 +130,210 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d struct isci_request *ireq = ihost->reqs[i]; enum sci_status s; - if (!test_bit(IREQ_ACTIVE, &ireq->flags) || - ireq->target_device != idev) - continue; - - s = sci_controller_terminate_request(ihost, idev, ireq); + s = sci_remote_device_terminate_req(ihost, idev, chk, ireq); if (s != SCI_SUCCESS) status = s; } + return status; +} + +static bool isci_compare_suspendcount( + struct isci_remote_device *idev, + u32 localcount) +{ + smp_rmb(); + + /* Check for a change in the suspend count, or the RNC + * being destroyed. + */ + return (localcount != idev->rnc.suspend_count) + || sci_remote_node_context_is_being_destroyed(&idev->rnc); +} + +static bool isci_check_reqterm( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq, + u32 localcount) +{ + unsigned long flags; + bool res; + spin_lock_irqsave(&ihost->scic_lock, flags); + res = isci_compare_suspendcount(idev, localcount) + && !test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return res; +} + +static bool isci_check_devempty( + struct isci_host *ihost, + struct isci_remote_device *idev, + u32 localcount) +{ + unsigned long flags; + bool res; + + spin_lock_irqsave(&ihost->scic_lock, flags); + res = isci_compare_suspendcount(idev, localcount) + && idev->started_request_count == 0; + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return res; +} + +enum sci_status isci_remote_device_terminate_requests( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq) +{ + enum sci_status status = SCI_SUCCESS; + unsigned long flags; + u32 rnc_suspend_count; + + spin_lock_irqsave(&ihost->scic_lock, flags); + + if (isci_get_device(idev) == NULL) { + dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", + __func__, idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + status = SCI_FAILURE; + } else { + /* If already suspended, don't wait for another suspension. */ + smp_rmb(); + rnc_suspend_count + = sci_remote_node_context_is_suspended(&idev->rnc) + ? 0 : idev->rnc.suspend_count; + + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p, ireq=%p; started_request_count=%d, " + "rnc_suspend_count=%d, rnc.suspend_count=%d" + "about to wait\n", + __func__, idev, ireq, idev->started_request_count, + rnc_suspend_count, idev->rnc.suspend_count); + + #define MAX_SUSPEND_MSECS 10000 + if (ireq) { + /* Terminate a specific TC. */ + set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); + sci_remote_device_terminate_req(ihost, idev, 0, ireq); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (!wait_event_timeout(ihost->eventq, + isci_check_reqterm(ihost, idev, ireq, + rnc_suspend_count), + msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + + dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n", + __func__, ihost->id); + dev_dbg(&ihost->pdev->dev, + "%s: ******* Timeout waiting for " + "suspend; idev=%p, current state %s; " + "started_request_count=%d, flags=%lx\n\t" + "rnc_suspend_count=%d, rnc.suspend_count=%d " + "RNC: current state %s, current " + "suspend_type %x dest state %d;\n" + "ireq=%p, ireq->flags = %lx\n", + __func__, idev, + dev_state_name(idev->sm.current_state_id), + idev->started_request_count, idev->flags, + rnc_suspend_count, idev->rnc.suspend_count, + rnc_state_name(idev->rnc.sm.current_state_id), + idev->rnc.suspend_type, + idev->rnc.destination_state, + ireq, ireq->flags); + } + spin_lock_irqsave(&ihost->scic_lock, flags); + clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); + if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + } else { + /* Terminate all TCs. */ + sci_remote_device_terminate_requests(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (!wait_event_timeout(ihost->eventq, + isci_check_devempty(ihost, idev, + rnc_suspend_count), + msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + + dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n", + __func__, ihost->id); + dev_dbg(&ihost->pdev->dev, + "%s: ******* Timeout waiting for " + "suspend; idev=%p, current state %s; " + "started_request_count=%d, flags=%lx\n\t" + "rnc_suspend_count=%d, " + "RNC: current state %s, " + "rnc.suspend_count=%d, current " + "suspend_type %x dest state %d\n", + __func__, idev, + dev_state_name(idev->sm.current_state_id), + idev->started_request_count, idev->flags, + rnc_suspend_count, + rnc_state_name(idev->rnc.sm.current_state_id), + idev->rnc.suspend_count, + idev->rnc.suspend_type, + idev->rnc.destination_state); + } + } + dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", + __func__, idev); + isci_put_device(idev); + } return status; } +/** +* isci_remote_device_not_ready() - This function is called by the ihost when +* the remote device is not ready. We mark the isci device as ready (not +* "ready_for_io") and signal the waiting proccess. +* @isci_host: This parameter specifies the isci host object. +* @isci_device: This parameter specifies the remote device +* +* sci_lock is held on entrance to this function. +*/ +static void isci_remote_device_not_ready(struct isci_host *ihost, + struct isci_remote_device *idev, + u32 reason) +{ + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p; reason = %d\n", __func__, idev, reason); + + switch (reason) { + case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: + set_bit(IDEV_IO_NCQERROR, &idev->flags); + + /* Suspend the remote device so the I/O can be terminated. */ + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL); + + /* Kill all outstanding requests for the device. */ + sci_remote_device_terminate_requests(idev); + + /* Fall through into the default case... */ + default: + clear_bit(IDEV_IO_READY, &idev->flags); + break; + } +} + +/* called once the remote node context is ready to be freed. + * The remote device can now report that its stop operation is complete. none + */ +static void rnc_destruct_done(void *_dev) +{ + struct isci_remote_device *idev = _dev; + + BUG_ON(idev->started_request_count != 0); + sci_change_state(&idev->sm, SCI_DEV_STOPPED); +} + +enum sci_status sci_remote_device_terminate_requests( + struct isci_remote_device *idev) +{ + return sci_remote_device_terminate_reqs_checkabort(idev, 0); +} + enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, u32 timeout) { @@ -201,13 +369,16 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, case SCI_SMP_DEV_IDLE: case SCI_SMP_DEV_CMD: sci_change_state(sm, SCI_DEV_STOPPING); - if (idev->started_request_count == 0) { + if (idev->started_request_count == 0) sci_remote_node_context_destruct(&idev->rnc, - rnc_destruct_done, idev); - return SCI_SUCCESS; - } else - return sci_remote_device_terminate_requests(idev); - break; + rnc_destruct_done, + idev); + else { + sci_remote_device_suspend( + idev, SCI_SW_SUSPEND_LINKHANG_DETECT); + sci_remote_device_terminate_requests(idev); + } + return SCI_SUCCESS; case SCI_DEV_STOPPING: /* All requests should have been terminated, but if there is an * attempt to stop a device already in the stopping state, then @@ -265,22 +436,6 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev return SCI_SUCCESS; } -enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, - u32 suspend_type) -{ - struct sci_base_state_machine *sm = &idev->sm; - enum sci_remote_device_states state = sm->current_state_id; - - if (state != SCI_STP_DEV_CMD) { - dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n", - __func__, dev_state_name(state)); - return SCI_FAILURE_INVALID_STATE; - } - - return sci_remote_node_context_suspend(&idev->rnc, - suspend_type, NULL, NULL); -} - enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev, u32 frame_index) { @@ -412,9 +567,9 @@ static void atapi_remote_device_resume_done(void *_dev) enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, u32 event_code) { + enum sci_status status; struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id; - enum sci_status status; switch (scu_get_event_type(event_code)) { case SCU_EVENT_TYPE_RNC_OPS_MISC: @@ -427,9 +582,7 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, status = SCI_SUCCESS; /* Suspend the associated RNC */ - sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, - NULL, NULL); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL); dev_dbg(scirdev_to_dev(idev), "%s: device: %p event code: %x: %s\n", @@ -455,6 +608,10 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, if (status != SCI_SUCCESS) return status; + /* Decode device-specific states that may require an RNC resume during + * normal operation. When the abort path is active, these resumes are + * managed when the abort path exits. + */ if (state == SCI_STP_DEV_ATAPI_ERROR) { /* For ATAPI error state resume the RNC right away. */ if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || @@ -743,10 +900,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, if (status != SCI_SUCCESS) return status; - status = sci_remote_node_context_start_task(&idev->rnc, ireq); - if (status != SCI_SUCCESS) - goto out; - status = sci_request_start(ireq); if (status != SCI_SUCCESS) goto out; @@ -765,11 +918,11 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, * the correct action when the remote node context is suspended * and later resumed. */ - sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, NULL, NULL); - sci_remote_node_context_resume(&idev->rnc, - sci_remote_device_continue_request, - idev); + sci_remote_device_suspend(idev, + SCI_SW_SUSPEND_LINKHANG_DETECT); + + status = sci_remote_node_context_start_task(&idev->rnc, ireq, + sci_remote_device_continue_request, idev); out: sci_remote_device_start_request(idev, ireq, status); @@ -783,7 +936,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, if (status != SCI_SUCCESS) return status; - status = sci_remote_node_context_start_task(&idev->rnc, ireq); + /* Resume the RNC as needed: */ + status = sci_remote_node_context_start_task(&idev->rnc, ireq, + NULL, NULL); if (status != SCI_SUCCESS) break; @@ -892,7 +1047,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_ * here should go through isci_remote_device_nuke_requests. * If we hit this condition, we will need a way to complete * io requests in process */ - BUG_ON(!list_empty(&idev->reqs_in_process)); + BUG_ON(idev->started_request_count > 0); sci_remote_device_destruct(idev); list_del_init(&idev->node); @@ -954,14 +1109,21 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); + struct isci_host *ihost = idev->owning_port->owning_controller; - sci_remote_node_context_suspend( - &idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL); + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p\n", __func__, idev); + + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); } static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); + struct isci_host *ihost = idev->owning_port->owning_controller; + + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p\n", __func__, idev); sci_remote_node_context_resume(&idev->rnc, NULL, NULL); } @@ -1113,33 +1275,20 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport, { enum sci_status status; struct sci_port_properties properties; - struct domain_device *dev = idev->domain_dev; sci_remote_device_construct(iport, idev); - /* - * This information is request to determine how many remote node context - * entries will be needed to store the remote node. - */ - idev->is_direct_attached = true; - sci_port_get_properties(iport, &properties); /* Get accurate port width from port's phy mask for a DA device. */ idev->device_port_width = hweight32(properties.phy_mask); status = sci_controller_allocate_remote_node_context(iport->owning_controller, - idev, - &idev->rnc.remote_node_index); + idev, + &idev->rnc.remote_node_index); if (status != SCI_SUCCESS) return status; - if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || - (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) - /* pass */; - else - return SCI_FAILURE_UNSUPPORTED_PROTOCOL; - idev->connection_rate = sci_port_get_max_allowed_speed(iport); return SCI_SUCCESS; @@ -1171,19 +1320,13 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, if (status != SCI_SUCCESS) return status; - if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || - (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) - /* pass */; - else - return SCI_FAILURE_UNSUPPORTED_PROTOCOL; - - /* - * For SAS-2 the physical link rate is actually a logical link + /* For SAS-2 the physical link rate is actually a logical link * rate that incorporates multiplexing. The SCU doesn't * incorporate multiplexing and for the purposes of the * connection the logical link rate is that same as the * physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay - * one another, so this code works for both situations. */ + * one another, so this code works for both situations. + */ idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport), dev->linkrate); @@ -1193,6 +1336,105 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, return SCI_SUCCESS; } +enum sci_status sci_remote_device_resume( + struct isci_remote_device *idev, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) +{ + enum sci_status status; + + status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p); + if (status != SCI_SUCCESS) + dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n", + __func__, status); + return status; +} + +static void isci_remote_device_resume_from_abort_complete(void *cbparam) +{ + struct isci_remote_device *idev = cbparam; + struct isci_host *ihost = idev->owning_port->owning_controller; + scics_sds_remote_node_context_callback abort_resume_cb = + idev->abort_resume_cb; + + dev_dbg(scirdev_to_dev(idev), "%s: passing-along resume: %p\n", + __func__, abort_resume_cb); + + if (abort_resume_cb != NULL) { + idev->abort_resume_cb = NULL; + abort_resume_cb(idev->abort_resume_cbparam); + } + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + wake_up(&ihost->eventq); +} + +static bool isci_remote_device_test_resume_done( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + unsigned long flags; + bool done; + + spin_lock_irqsave(&ihost->scic_lock, flags); + done = !test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags) + || test_bit(IDEV_STOP_PENDING, &idev->flags) + || sci_remote_node_context_is_being_destroyed(&idev->rnc); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return done; +} + +void isci_remote_device_wait_for_resume_from_abort( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + dev_dbg(&ihost->pdev->dev, "%s: starting resume wait: %p\n", + __func__, idev); + + #define MAX_RESUME_MSECS 10000 + if (!wait_event_timeout(ihost->eventq, + isci_remote_device_test_resume_done(ihost, idev), + msecs_to_jiffies(MAX_RESUME_MSECS))) { + + dev_warn(&ihost->pdev->dev, "%s: #### Timeout waiting for " + "resume: %p\n", __func__, idev); + } + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + + dev_dbg(&ihost->pdev->dev, "%s: resume wait done: %p\n", + __func__, idev); +} + +enum sci_status isci_remote_device_resume_from_abort( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + unsigned long flags; + enum sci_status status = SCI_SUCCESS; + int destroyed; + + spin_lock_irqsave(&ihost->scic_lock, flags); + /* Preserve any current resume callbacks, for instance from other + * resumptions. + */ + idev->abort_resume_cb = idev->rnc.user_callback; + idev->abort_resume_cbparam = idev->rnc.user_cookie; + set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); + destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc); + if (!destroyed) + status = sci_remote_device_resume( + idev, isci_remote_device_resume_from_abort_complete, + idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (!destroyed && (status == SCI_SUCCESS)) + isci_remote_device_wait_for_resume_from_abort(ihost, idev); + else + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + + return status; +} + /** * sci_remote_device_start() - This method will start the supplied remote * device. This method enables normal IO requests to flow through to the @@ -1207,7 +1449,7 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, * the device when there have been no phys added to it. */ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, - u32 timeout) + u32 timeout) { struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id; @@ -1219,9 +1461,8 @@ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, return SCI_FAILURE_INVALID_STATE; } - status = sci_remote_node_context_resume(&idev->rnc, - remote_device_resume_done, - idev); + status = sci_remote_device_resume(idev, remote_device_resume_done, + idev); if (status != SCI_SUCCESS) return status; @@ -1259,20 +1500,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport, return status; } -void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev) -{ - DECLARE_COMPLETION_ONSTACK(aborted_task_completion); - - dev_dbg(&ihost->pdev->dev, - "%s: idev = %p\n", __func__, idev); - - /* Cleanup all requests pending for this device. */ - isci_terminate_pending_requests(ihost, idev); - - dev_dbg(&ihost->pdev->dev, - "%s: idev = %p, done\n", __func__, idev); -} - /** * This function builds the isci_remote_device when a libsas dev_found message * is received. @@ -1297,10 +1524,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport) dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__); return NULL; } - - if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n")) - return NULL; - if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n")) return NULL; @@ -1342,14 +1565,8 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem spin_lock_irqsave(&ihost->scic_lock, flags); idev->domain_dev->lldd_dev = NULL; /* disable new lookups */ set_bit(IDEV_GONE, &idev->flags); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - /* Kill all outstanding requests. */ - isci_remote_device_nuke_requests(ihost, idev); set_bit(IDEV_STOP_PENDING, &idev->flags); - - spin_lock_irqsave(&ihost->scic_lock, flags); status = sci_remote_device_stop(idev, 50); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1359,6 +1576,9 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem else wait_for_device_stop(ihost, idev); + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p, waiting done.\n", __func__, idev); + return status; } @@ -1434,3 +1654,73 @@ int isci_remote_device_found(struct domain_device *dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } + +enum sci_status isci_remote_device_suspend_terminate( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq) +{ + unsigned long flags; + enum sci_status status; + + /* Put the device into suspension. */ + spin_lock_irqsave(&ihost->scic_lock, flags); + set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + /* Terminate and wait for the completions. */ + status = isci_remote_device_terminate_requests(ihost, idev, ireq); + if (status != SCI_SUCCESS) + dev_dbg(&ihost->pdev->dev, + "%s: isci_remote_device_terminate_requests(%p) " + "returned %d!\n", + __func__, idev, status); + + /* NOTE: RNC resumption is left to the caller! */ + return status; +} + +int isci_remote_device_is_safe_to_abort( + struct isci_remote_device *idev) +{ + return sci_remote_node_context_is_safe_to_abort(&idev->rnc); +} + +enum sci_status sci_remote_device_abort_requests_pending_abort( + struct isci_remote_device *idev) +{ + return sci_remote_device_terminate_reqs_checkabort(idev, 1); +} + +enum sci_status isci_remote_device_reset_complete( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + unsigned long flags; + enum sci_status status; + + spin_lock_irqsave(&ihost->scic_lock, flags); + status = sci_remote_device_reset_complete(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return status; +} + +void isci_dev_set_hang_detection_timeout( + struct isci_remote_device *idev, + u32 timeout) +{ + if (dev_is_sata(idev->domain_dev)) { + if (timeout) { + if (test_and_set_bit(IDEV_RNC_LLHANG_ENABLED, + &idev->flags)) + return; /* Already enabled. */ + } else if (!test_and_clear_bit(IDEV_RNC_LLHANG_ENABLED, + &idev->flags)) + return; /* Not enabled. */ + + sci_port_set_hang_detection_timeout(idev->owning_port, + timeout); + } +} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 58637ee08f5..7674caae1d8 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -85,27 +85,38 @@ struct isci_remote_device { #define IDEV_GONE 3 #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 + #define IDEV_RNC_LLHANG_ENABLED 6 + #define IDEV_ABORT_PATH_ACTIVE 7 + #define IDEV_ABORT_PATH_RESUME_PENDING 8 unsigned long flags; struct kref kref; struct isci_port *isci_port; struct domain_device *domain_dev; struct list_head node; - struct list_head reqs_in_process; struct sci_base_state_machine sm; u32 device_port_width; enum sas_linkrate connection_rate; - bool is_direct_attached; struct isci_port *owning_port; struct sci_remote_node_context rnc; /* XXX unify with device reference counting and delete */ u32 started_request_count; struct isci_request *working_request; u32 not_ready_reason; + scics_sds_remote_node_context_callback abort_resume_cb; + void *abort_resume_cbparam; }; #define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000 /* device reference routines must be called under sci_lock */ +static inline struct isci_remote_device *isci_get_device( + struct isci_remote_device *idev) +{ + if (idev) + kref_get(&idev->kref); + return idev; +} + static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev) { struct isci_remote_device *idev = dev->lldd_dev; @@ -302,6 +313,8 @@ static inline void sci_remote_device_decrement_request_count(struct isci_remote_ idev->started_request_count--; } +void isci_dev_set_hang_detection_timeout(struct isci_remote_device *idev, u32 timeout); + enum sci_status sci_remote_device_frame_handler( struct isci_remote_device *idev, u32 frame_index); @@ -325,12 +338,50 @@ enum sci_status sci_remote_device_complete_io( struct isci_remote_device *idev, struct isci_request *ireq); -enum sci_status sci_remote_device_suspend( - struct isci_remote_device *idev, - u32 suspend_type); - void sci_remote_device_post_request( struct isci_remote_device *idev, u32 request); +enum sci_status sci_remote_device_terminate_requests( + struct isci_remote_device *idev); + +int isci_remote_device_is_safe_to_abort( + struct isci_remote_device *idev); + +enum sci_status +sci_remote_device_abort_requests_pending_abort( + struct isci_remote_device *idev); + +enum sci_status isci_remote_device_suspend( + struct isci_host *ihost, + struct isci_remote_device *idev); + +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 isci_remote_device_resume_from_abort( + struct isci_host *ihost, + struct isci_remote_device *idev); + +enum sci_status isci_remote_device_reset( + struct isci_host *ihost, + struct isci_remote_device *idev); + +enum sci_status isci_remote_device_reset_complete( + struct isci_host *ihost, + struct isci_remote_device *idev); + +enum sci_status isci_remote_device_suspend_terminate( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq); + +enum sci_status isci_remote_device_terminate_requests( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq); +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, + enum sci_remote_node_suspension_reasons reason); #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 3a9463481f3..1910100638a 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -52,7 +52,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#include <scsi/sas_ata.h> #include "host.h" #include "isci.h" #include "remote_device.h" @@ -90,6 +90,15 @@ bool sci_remote_node_context_is_ready( return false; } +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc) +{ + u32 current_state = sci_rnc->sm.current_state_id; + + if (current_state == SCI_RNC_TX_RX_SUSPENDED) + return true; + return false; +} + static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id) { if (id < ihost->remote_node_entries && @@ -131,7 +140,7 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont rnc->ssp.arbitration_wait_time = 0; - if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { + if (dev_is_sata(dev)) { rnc->ssp.connection_occupancy_timeout = ihost->user_parameters.stp_max_occupancy_timeout; rnc->ssp.connection_inactivity_timeout = @@ -151,7 +160,6 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont rnc->ssp.oaf_source_zone_group = 0; rnc->ssp.oaf_more_compatibility_features = 0; } - /** * * @sci_rnc: @@ -165,23 +173,30 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont static void sci_remote_node_context_setup_to_resume( struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback callback, - void *callback_parameter) + void *callback_parameter, + enum sci_remote_node_context_destination_state dest_param) { - if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) { - sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY; - sci_rnc->user_callback = callback; - sci_rnc->user_cookie = callback_parameter; + if (sci_rnc->destination_state != RNC_DEST_FINAL) { + sci_rnc->destination_state = dest_param; + if (callback != NULL) { + sci_rnc->user_callback = callback; + sci_rnc->user_cookie = callback_parameter; + } } } -static void sci_remote_node_context_setup_to_destory( +static void sci_remote_node_context_setup_to_destroy( struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback callback, void *callback_parameter) { - sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL; + struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc)); + + sci_rnc->destination_state = RNC_DEST_FINAL; sci_rnc->user_callback = callback; sci_rnc->user_cookie = callback_parameter; + + wake_up(&ihost->eventq); } /** @@ -203,9 +218,19 @@ static void sci_remote_node_context_notify_user( static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc) { - if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) + switch (rnc->destination_state) { + case RNC_DEST_READY: + case RNC_DEST_SUSPENDED_RESUME: + rnc->destination_state = RNC_DEST_READY; + /* Fall through... */ + case RNC_DEST_FINAL: sci_remote_node_context_resume(rnc, rnc->user_callback, - rnc->user_cookie); + rnc->user_cookie); + break; + default: + rnc->destination_state = RNC_DEST_UNSPECIFIED; + break; + } } static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc) @@ -219,13 +244,12 @@ static void sci_remote_node_context_validate_context_buffer(struct sci_remote_no rnc_buffer->ssp.is_valid = true; - if (!idev->is_direct_attached && - (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) { + if (dev_is_sata(dev) && dev->parent) { sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96); } else { sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32); - if (idev->is_direct_attached) + if (!dev->parent) sci_port_setup_transports(idev->owning_port, sci_rnc->remote_node_index); } @@ -248,13 +272,18 @@ static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_ static void sci_remote_node_context_initial_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + struct isci_host *ihost = idev->owning_port->owning_controller; /* Check to see if we have gotten back to the initial state because * someone requested to destroy the remote node context object. */ if (sm->previous_state_id == SCI_RNC_INVALIDATING) { - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + rnc->destination_state = RNC_DEST_UNSPECIFIED; sci_remote_node_context_notify_user(rnc); + + smp_wmb(); + wake_up(&ihost->eventq); } } @@ -269,6 +298,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + /* Terminate all outstanding requests. */ + sci_remote_device_terminate_requests(rnc_to_dev(rnc)); sci_remote_node_context_invalidate_context_buffer(rnc); } @@ -287,10 +318,8 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m * resume because of a target reset we also need to update * the STPTLDARNI register with the RNi of the device */ - if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) && - idev->is_direct_attached) - sci_port_setup_transports(idev->owning_port, - rnc->remote_node_index); + if (dev_is_sata(dev) && !dev->parent) + sci_port_setup_transports(idev->owning_port, rnc->remote_node_index); sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME); } @@ -298,10 +327,22 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + enum sci_remote_node_context_destination_state dest_select; + int tell_user = 1; + + dest_select = rnc->destination_state; + rnc->destination_state = RNC_DEST_UNSPECIFIED; - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + if ((dest_select == RNC_DEST_SUSPENDED) || + (dest_select == RNC_DEST_SUSPENDED_RESUME)) { + sci_remote_node_context_suspend( + rnc, rnc->suspend_reason, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); - if (rnc->user_callback) + if (dest_select == RNC_DEST_SUSPENDED_RESUME) + tell_user = 0; /* Wait until ready again. */ + } + if (tell_user) sci_remote_node_context_notify_user(rnc); } @@ -315,10 +356,34 @@ static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_sta static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + struct isci_host *ihost = idev->owning_port->owning_controller; + u32 new_count = rnc->suspend_count + 1; + + if (new_count == 0) + rnc->suspend_count = 1; + else + rnc->suspend_count = new_count; + smp_wmb(); + /* Terminate outstanding requests pending abort. */ + sci_remote_device_abort_requests_pending_abort(idev); + + wake_up(&ihost->eventq); sci_remote_node_context_continue_state_transitions(rnc); } +static void sci_remote_node_context_await_suspend_state_exit( + struct sci_base_state_machine *sm) +{ + struct sci_remote_node_context *rnc + = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + + if (dev_is_sata(idev->domain_dev)) + isci_dev_set_hang_detection_timeout(idev, 0); +} + static const struct sci_base_state sci_remote_node_context_state_table[] = { [SCI_RNC_INITIAL] = { .enter_state = sci_remote_node_context_initial_state_enter, @@ -341,7 +406,9 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = { [SCI_RNC_TX_RX_SUSPENDED] = { .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, }, - [SCI_RNC_AWAIT_SUSPENSION] = { }, + [SCI_RNC_AWAIT_SUSPENSION] = { + .exit_state = sci_remote_node_context_await_suspend_state_exit, + }, }; void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, @@ -350,7 +417,7 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, memset(rnc, 0, sizeof(struct sci_remote_node_context)); rnc->remote_node_index = remote_node_index; - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + rnc->destination_state = RNC_DEST_UNSPECIFIED; sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL); } @@ -359,6 +426,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con u32 event_code) { enum scis_sds_remote_node_context_states state; + u32 next_state; state = sci_rnc->sm.current_state_id; switch (state) { @@ -373,18 +441,18 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con break; case SCI_RNC_INVALIDATING: if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) { - if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) - state = SCI_RNC_INITIAL; + if (sci_rnc->destination_state == RNC_DEST_FINAL) + next_state = SCI_RNC_INITIAL; else - state = SCI_RNC_POSTING; - sci_change_state(&sci_rnc->sm, state); + next_state = SCI_RNC_POSTING; + sci_change_state(&sci_rnc->sm, next_state); } else { switch (scu_get_event_type(event_code)) { case SCU_EVENT_TYPE_RNC_SUSPEND_TX: case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: /* We really dont care if the hardware is going to suspend * the device since it's being invalidated anyway */ - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: SCIC Remote Node Context 0x%p was " "suspeneded by hardware while being " "invalidated.\n", __func__, sci_rnc); @@ -403,7 +471,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: /* We really dont care if the hardware is going to suspend * the device since it's being resumed anyway */ - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: SCIC Remote Node Context 0x%p was " "suspeneded by hardware while being resumed.\n", __func__, sci_rnc); @@ -417,11 +485,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con switch (scu_get_event_type(event_code)) { case SCU_EVENT_TL_RNC_SUSPEND_TX: sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + sci_rnc->suspend_type = scu_get_event_type(event_code); break; case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + sci_rnc->suspend_type = scu_get_event_type(event_code); break; default: goto out; @@ -430,27 +498,29 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con case SCI_RNC_AWAIT_SUSPENSION: switch (scu_get_event_type(event_code)) { case SCU_EVENT_TL_RNC_SUSPEND_TX: - sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + next_state = SCI_RNC_TX_SUSPENDED; break; case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: - sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + next_state = SCI_RNC_TX_RX_SUSPENDED; break; default: goto out; } + if (sci_rnc->suspend_type == scu_get_event_type(event_code)) + sci_change_state(&sci_rnc->sm, next_state); break; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state: %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } return SCI_SUCCESS; out: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: code: %#x state: %d\n", __func__, event_code, state); + "%s: code: %#x state: %s\n", __func__, event_code, + rnc_state_name(state)); return SCI_FAILURE; } @@ -464,20 +534,23 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context state = sci_rnc->sm.current_state_id; switch (state) { case SCI_RNC_INVALIDATING: - sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); return SCI_SUCCESS; case SCI_RNC_POSTING: case SCI_RNC_RESUMING: case SCI_RNC_READY: case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: - case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); return SCI_SUCCESS; + case SCI_RNC_AWAIT_SUSPENSION: + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); + return SCI_SUCCESS; case SCI_RNC_INITIAL: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state: %s\n", __func__, + rnc_state_name(state)); /* We have decided that the destruct request on the remote node context * can not fail since it is either in the initial/destroyed state or is * can be destroyed. @@ -485,35 +558,101 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } } -enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc, - u32 suspend_type, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p) +enum sci_status sci_remote_node_context_suspend( + struct sci_remote_node_context *sci_rnc, + enum sci_remote_node_suspension_reasons suspend_reason, + u32 suspend_type) { - enum scis_sds_remote_node_context_states state; + enum scis_sds_remote_node_context_states state + = sci_rnc->sm.current_state_id; + struct isci_remote_device *idev = rnc_to_dev(sci_rnc); + enum sci_status status = SCI_FAILURE_INVALID_STATE; + enum sci_remote_node_context_destination_state dest_param = + RNC_DEST_UNSPECIFIED; + + dev_dbg(scirdev_to_dev(idev), + "%s: current state %s, current suspend_type %x dest state %d," + " arg suspend_reason %d, arg suspend_type %x", + __func__, rnc_state_name(state), sci_rnc->suspend_type, + sci_rnc->destination_state, suspend_reason, + suspend_type); + + /* Disable automatic state continuations if explicitly suspending. */ + if ((suspend_reason == SCI_HW_SUSPEND) || + (sci_rnc->destination_state == RNC_DEST_FINAL)) + dest_param = sci_rnc->destination_state; - state = sci_rnc->sm.current_state_id; - if (state != SCI_RNC_READY) { + switch (state) { + case SCI_RNC_READY: + break; + case SCI_RNC_INVALIDATING: + if (sci_rnc->destination_state == RNC_DEST_FINAL) { + dev_warn(scirdev_to_dev(idev), + "%s: already destroying %p\n", + __func__, sci_rnc); + return SCI_FAILURE_INVALID_STATE; + } + /* Fall through and handle like SCI_RNC_POSTING */ + case SCI_RNC_RESUMING: + /* Fall through and handle like SCI_RNC_POSTING */ + case SCI_RNC_POSTING: + /* Set the destination state to AWAIT - this signals the + * entry into the SCI_RNC_READY state that a suspension + * needs to be done immediately. + */ + if (sci_rnc->destination_state != RNC_DEST_FINAL) + sci_rnc->destination_state = RNC_DEST_SUSPENDED; + sci_rnc->suspend_type = suspend_type; + sci_rnc->suspend_reason = suspend_reason; + return SCI_SUCCESS; + + case SCI_RNC_TX_SUSPENDED: + if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX) + status = SCI_SUCCESS; + break; + case SCI_RNC_TX_RX_SUSPENDED: + if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX) + status = SCI_SUCCESS; + break; + case SCI_RNC_AWAIT_SUSPENSION: + if ((sci_rnc->suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX) + || (suspend_type == sci_rnc->suspend_type)) + return SCI_SUCCESS; + break; + default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } + sci_rnc->destination_state = dest_param; + sci_rnc->suspend_type = suspend_type; + sci_rnc->suspend_reason = suspend_reason; + + if (status == SCI_SUCCESS) { /* Already in the destination state? */ + struct isci_host *ihost = idev->owning_port->owning_controller; + + wake_up_all(&ihost->eventq); /* Let observers look. */ + return SCI_SUCCESS; + } + if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) || + (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) { - sci_rnc->user_callback = cb_fn; - sci_rnc->user_cookie = cb_p; - sci_rnc->suspension_code = suspend_type; + if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT) + isci_dev_set_hang_detection_timeout(idev, 0x00000001); - if (suspend_type == SCI_SOFTWARE_SUSPENSION) { - sci_remote_device_post_request(rnc_to_dev(sci_rnc), - SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX); + sci_remote_device_post_request( + idev, SCI_SOFTWARE_SUSPEND_CMD); } + if (state != SCI_RNC_AWAIT_SUSPENSION) + sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION); - sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION); return SCI_SUCCESS; } @@ -522,56 +661,86 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s void *cb_p) { enum scis_sds_remote_node_context_states state; + struct isci_remote_device *idev = rnc_to_dev(sci_rnc); state = sci_rnc->sm.current_state_id; + dev_dbg(scirdev_to_dev(idev), + "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; " + "dev resume path %s\n", + __func__, rnc_state_name(state), cb_fn, cb_p, + sci_rnc->destination_state, + test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags) + ? "<abort active>" : "<normal>"); + switch (state) { case SCI_RNC_INITIAL: if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) return SCI_FAILURE_INVALID_STATE; - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); - sci_remote_node_context_construct_buffer(sci_rnc); - sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); + sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) { + sci_remote_node_context_construct_buffer(sci_rnc); + sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); + } return SCI_SUCCESS; + case SCI_RNC_POSTING: case SCI_RNC_INVALIDATING: case SCI_RNC_RESUMING: - if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) - return SCI_FAILURE_INVALID_STATE; - - sci_rnc->user_callback = cb_fn; - sci_rnc->user_cookie = cb_p; + /* We are still waiting to post when a resume was + * requested. + */ + switch (sci_rnc->destination_state) { + case RNC_DEST_SUSPENDED: + case RNC_DEST_SUSPENDED_RESUME: + /* Previously waiting to suspend after posting. + * Now continue onto resumption. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); + break; + default: + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + break; + } return SCI_SUCCESS; - case SCI_RNC_TX_SUSPENDED: { - struct isci_remote_device *idev = rnc_to_dev(sci_rnc); - struct domain_device *dev = idev->domain_dev; - - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); - - /* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */ - if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev)) - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { - if (idev->is_direct_attached) { - /* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */ - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - } else { - sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); + + case SCI_RNC_TX_SUSPENDED: + case SCI_RNC_TX_RX_SUSPENDED: + { + struct domain_device *dev = idev->domain_dev; + /* If this is an expander attached SATA device we must + * invalidate and repost the RNC since this is the only + * way to clear the TCi to NCQ tag mapping table for + * the RNi. All other device types we can just resume. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, RNC_DEST_READY); + + if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) { + if ((dev_is_sata(dev) && dev->parent) || + (sci_rnc->destination_state == RNC_DEST_FINAL)) + sci_change_state(&sci_rnc->sm, + SCI_RNC_INVALIDATING); + else + sci_change_state(&sci_rnc->sm, + SCI_RNC_RESUMING); } - } else - return SCI_FAILURE; + } return SCI_SUCCESS; - } - case SCI_RNC_TX_RX_SUSPENDED: - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - return SCI_FAILURE_INVALID_STATE; + case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, RNC_DEST_SUSPENDED_RESUME); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } } @@ -590,35 +759,51 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context case SCI_RNC_TX_RX_SUSPENDED: case SCI_RNC_AWAIT_SUSPENSION: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; default: - break; + dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); + return SCI_FAILURE_INVALID_STATE; } - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: requested to start IO while still resuming, %d\n", - __func__, state); - return SCI_FAILURE_INVALID_STATE; } -enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc, - struct isci_request *ireq) +enum sci_status sci_remote_node_context_start_task( + struct sci_remote_node_context *sci_rnc, + struct isci_request *ireq, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) +{ + enum sci_status status = sci_remote_node_context_resume(sci_rnc, + cb_fn, cb_p); + if (status != SCI_SUCCESS) + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: resume failed: %d\n", __func__, status); + return status; +} + +int sci_remote_node_context_is_safe_to_abort( + struct sci_remote_node_context *sci_rnc) { enum scis_sds_remote_node_context_states state; state = sci_rnc->sm.current_state_id; switch (state) { + case SCI_RNC_INVALIDATING: + case SCI_RNC_TX_RX_SUSPENDED: + return 1; + case SCI_RNC_POSTING: case SCI_RNC_RESUMING: case SCI_RNC_READY: - case SCI_RNC_AWAIT_SUSPENSION: - return SCI_SUCCESS; case SCI_RNC_TX_SUSPENDED: - case SCI_RNC_TX_RX_SUSPENDED: - sci_remote_node_context_resume(sci_rnc, NULL, NULL); - return SCI_SUCCESS; + case SCI_RNC_AWAIT_SUSPENSION: + case SCI_RNC_INITIAL: + return 0; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: invalid state %d\n", __func__, state); - return SCI_FAILURE_INVALID_STATE; + return 0; } } diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index a241e0f4c86..a703b9ce0c2 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -75,8 +75,13 @@ */ #define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF -#define SCU_HARDWARE_SUSPENSION (0) -#define SCI_SOFTWARE_SUSPENSION (1) +enum sci_remote_node_suspension_reasons { + SCI_HW_SUSPEND, + SCI_SW_SUSPEND_NORMAL, + SCI_SW_SUSPEND_LINKHANG_DETECT +}; +#define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX +#define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX struct isci_request; struct isci_remote_device; @@ -137,9 +142,13 @@ const char *rnc_state_name(enum scis_sds_remote_node_context_states state); * node context. */ enum sci_remote_node_context_destination_state { - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED, - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY, - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL + RNC_DEST_UNSPECIFIED, + RNC_DEST_READY, + RNC_DEST_FINAL, + RNC_DEST_SUSPENDED, /* Set when suspend during post/invalidate */ + RNC_DEST_SUSPENDED_RESUME /* Set when a resume was done during posting + * or invalidating and already suspending. + */ }; /** @@ -156,10 +165,12 @@ struct sci_remote_node_context { u16 remote_node_index; /** - * This field is the recored suspension code or the reason for the remote node + * This field is the recored suspension type of the remote node * context suspension. */ - u32 suspension_code; + u32 suspend_type; + enum sci_remote_node_suspension_reasons suspend_reason; + u32 suspend_count; /** * This field is true if the remote node context is resuming from its current @@ -193,6 +204,8 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, bool sci_remote_node_context_is_ready( struct sci_remote_node_context *sci_rnc); +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc); + enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc, u32 event_code); enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc, @@ -200,14 +213,24 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context void *callback_parameter); enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc, u32 suspend_type, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p); + u32 suspension_code); enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback cb_fn, void *cb_p); enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc, - struct isci_request *ireq); + struct isci_request *ireq, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p); enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc, struct isci_request *ireq); +int sci_remote_node_context_is_safe_to_abort( + struct sci_remote_node_context *sci_rnc); +static inline bool sci_remote_node_context_is_being_destroyed( + struct sci_remote_node_context *sci_rnc) +{ + return (sci_rnc->destination_state == RNC_DEST_FINAL) + || ((sci_rnc->sm.current_state_id == SCI_RNC_INITIAL) + && (sci_rnc->destination_state == RNC_DEST_UNSPECIFIED)); +} #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 2def1e3960f..7a0431c7349 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -92,11 +92,11 @@ static dma_addr_t to_sgl_element_pair_dma(struct isci_host *ihost, if (idx == 0) { offset = (void *) &ireq->tc->sgl_pair_ab - (void *) &ihost->task_context_table[0]; - return ihost->task_context_dma + offset; + return ihost->tc_dma + offset; } else if (idx == 1) { offset = (void *) &ireq->tc->sgl_pair_cd - (void *) &ihost->task_context_table[0]; - return ihost->task_context_dma + offset; + return ihost->tc_dma + offset; } return sci_io_request_get_dma_addr(ireq, &ireq->sg_table[idx - 2]); @@ -730,7 +730,7 @@ static enum sci_status sci_io_request_construct_basic_ssp(struct isci_request *i { struct sas_task *task = isci_request_access_task(ireq); - ireq->protocol = SCIC_SSP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_SSP; scu_ssp_io_request_construct_task_context(ireq, task->data_dir, @@ -763,7 +763,7 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request * bool copy = false; struct sas_task *task = isci_request_access_task(ireq); - ireq->protocol = SCIC_STP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_STP; copy = (task->data_dir == DMA_NONE) ? false : true; @@ -863,6 +863,8 @@ sci_io_request_terminate(struct isci_request *ireq) switch (state) { case SCI_REQ_CONSTRUCTED: + /* Set to make sure no HW terminate posting is done: */ + set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags); ireq->scu_status = SCU_TASK_DONE_TASK_ABORT; ireq->sci_status = SCI_FAILURE_IO_TERMINATED; sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); @@ -883,8 +885,7 @@ sci_io_request_terminate(struct isci_request *ireq) 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; + /* Fall through and change state to ABORTING... */ case SCI_REQ_TASK_WAIT_TC_RESP: /* The task frame was already confirmed to have been * sent by the SCU HW. Since the state machine is @@ -893,20 +894,21 @@ sci_io_request_terminate(struct isci_request *ireq) * and don't wait for the task response. */ sci_change_state(&ireq->sm, SCI_REQ_ABORTING); - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); - return SCI_SUCCESS; + /* Fall through and handle like ABORTING... */ case SCI_REQ_ABORTING: - /* If a request has a termination requested twice, return - * a failure indication, since HW confirmation of the first - * abort is still outstanding. + if (!isci_remote_device_is_safe_to_abort(ireq->target_device)) + set_bit(IREQ_PENDING_ABORT, &ireq->flags); + else + clear_bit(IREQ_PENDING_ABORT, &ireq->flags); + /* If the request is only waiting on the remote device + * suspension, return SUCCESS so the caller will wait too. */ + return SCI_SUCCESS; case SCI_REQ_COMPLETED: default: dev_warn(&ireq->owning_controller->pdev->dev, "%s: SCIC IO Request requested to abort while in wrong " - "state %d\n", - __func__, - ireq->sm.current_state_id); + "state %d\n", __func__, ireq->sm.current_state_id); break; } @@ -1070,7 +1072,7 @@ request_started_state_tc_event(struct isci_request *ireq, case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_SDBFIS): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_REG_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SDB_ERR): - if (ireq->protocol == SCIC_STP_PROTOCOL) { + if (ireq->protocol == SAS_PROTOCOL_STP) { ireq->scu_status = SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT; ireq->sci_status = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; @@ -2117,7 +2119,7 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq */ if (ireq->stp.rsp.fis_type == FIS_REGD2H) { sci_remote_device_suspend(ireq->target_device, - SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code))); + SCI_SW_SUSPEND_NORMAL); ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; @@ -2138,13 +2140,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq /* TODO We can retry the command for SCU_TASK_DONE_CMD_LL_R_ERR * - this comes only for B0 */ - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_INV_FIS_LEN): - 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): - sci_remote_device_suspend(ireq->target_device, - SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code))); - /* 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); @@ -2262,15 +2257,151 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire return status; } +static int sci_request_smp_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + return 1; + } + return 0; +} + +static int sci_request_smp_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + return 0; /* There are no Tx/Rx SMP suspend conditions. */ +} + +static int sci_request_ssp_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_TX_RAW_CMD_ERR: + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + return 1; + } + return 0; +} + +static int sci_request_ssp_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + return 0; /* There are no Tx/Rx SSP suspend conditions. */ +} + +static int sci_request_stpsata_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_TX_RAW_CMD_ERR: + case SCU_TASK_DONE_LL_R_ERR: + case SCU_TASK_DONE_LL_PERR: + case SCU_TASK_DONE_REG_ERR: + case SCU_TASK_DONE_SDB_ERR: + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + return 1; + } + return 0; +} + + +static int sci_request_stpsata_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_DONE_LL_SY_TERM: + case SCU_TASK_DONE_LL_LF_TERM: + case SCU_TASK_DONE_BREAK_RCVD: + case SCU_TASK_DONE_INV_FIS_LEN: + case SCU_TASK_DONE_UNEXP_FIS: + case SCU_TASK_DONE_UNEXP_SDBFIS: + case SCU_TASK_DONE_MAX_PLD_ERR: + return 1; + } + return 0; +} + +static void sci_request_handle_suspending_completions( + struct isci_request *ireq, + u32 completion_code) +{ + int is_tx = 0; + int is_tx_rx = 0; + + switch (ireq->protocol) { + case SAS_PROTOCOL_SMP: + is_tx = sci_request_smp_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = sci_request_smp_completion_status_is_tx_rx_suspend( + completion_code); + break; + case SAS_PROTOCOL_SSP: + is_tx = sci_request_ssp_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = sci_request_ssp_completion_status_is_tx_rx_suspend( + completion_code); + break; + case SAS_PROTOCOL_STP: + is_tx = sci_request_stpsata_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = + sci_request_stpsata_completion_status_is_tx_rx_suspend( + completion_code); + break; + default: + dev_warn(&ireq->isci_host->pdev->dev, + "%s: request %p has no valid protocol\n", + __func__, ireq); + break; + } + if (is_tx || is_tx_rx) { + BUG_ON(is_tx && is_tx_rx); + + sci_remote_node_context_suspend( + &ireq->target_device->rnc, + SCI_HW_SUSPEND, + (is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX + : SCU_EVENT_TL_RNC_SUSPEND_TX); + } +} + enum sci_status sci_io_request_tc_completion(struct isci_request *ireq, - u32 completion_code) + u32 completion_code) { enum sci_base_request_states state; struct isci_host *ihost = ireq->owning_controller; state = ireq->sm.current_state_id; + /* Decode those completions that signal upcoming suspension events. */ + sci_request_handle_suspending_completions( + ireq, SCU_GET_COMPLETION_TL_STATUS(completion_code)); + switch (state) { case SCI_REQ_STARTED: return request_started_state_tc_event(ireq, completion_code); @@ -2362,9 +2493,6 @@ static void isci_request_process_response_iu( * @request: This parameter is the completed isci_request object. * @response_ptr: This parameter specifies the service response for the I/O. * @status_ptr: This parameter specifies the exec status for the I/O. - * @complete_to_host_ptr: This parameter specifies the action to be taken by - * the LLDD with respect to completing this request or forcing an abort - * condition on the I/O. * @open_rej_reason: This parameter specifies the encoded reason for the * abandon-class reject. * @@ -2375,14 +2503,12 @@ static void isci_request_set_open_reject_status( struct sas_task *task, enum service_response *response_ptr, enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr, enum sas_open_rej_reason open_rej_reason) { /* Task in the target is done. */ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); *response_ptr = SAS_TASK_UNDELIVERED; *status_ptr = SAS_OPEN_REJECT; - *complete_to_host_ptr = isci_perform_normal_io_completion; task->task_status.open_rej_reason = open_rej_reason; } @@ -2392,9 +2518,6 @@ static void isci_request_set_open_reject_status( * @request: This parameter is the completed isci_request object. * @response_ptr: This parameter specifies the service response for the I/O. * @status_ptr: This parameter specifies the exec status for the I/O. - * @complete_to_host_ptr: This parameter specifies the action to be taken by - * the LLDD with respect to completing this request or forcing an abort - * condition on the I/O. * * none. */ @@ -2403,8 +2526,7 @@ static void isci_request_handle_controller_specific_errors( struct isci_request *request, struct sas_task *task, enum service_response *response_ptr, - enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr) + enum exec_status *status_ptr) { unsigned int cstatus; @@ -2445,9 +2567,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_normal_io_completion; } else { /* Task in the target is not done. */ *response_ptr = SAS_TASK_UNDELIVERED; @@ -2458,9 +2577,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAM_STAT_TASK_ABORTED; clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_error_io_completion; } break; @@ -2489,8 +2605,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_normal_io_completion; break; @@ -2501,7 +2615,7 @@ static void isci_request_handle_controller_specific_errors( isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_WRONG_DEST); + SAS_OREJ_WRONG_DEST); break; case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: @@ -2511,56 +2625,56 @@ static void isci_request_handle_controller_specific_errors( */ isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB0); + SAS_OREJ_RESV_AB0); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB1); + SAS_OREJ_RESV_AB1); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB2); + SAS_OREJ_RESV_AB2); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB3); + SAS_OREJ_RESV_AB3); break; case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_BAD_DEST); + SAS_OREJ_BAD_DEST); break; case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_STP_NORES); + SAS_OREJ_STP_NORES); break; case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_EPROTO); + SAS_OREJ_EPROTO); break; case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_CONN_RATE); + SAS_OREJ_CONN_RATE); break; case SCU_TASK_DONE_LL_R_ERR: @@ -2592,95 +2706,12 @@ static void isci_request_handle_controller_specific_errors( *response_ptr = SAS_TASK_UNDELIVERED; *status_ptr = SAM_STAT_TASK_ABORTED; - if (task->task_proto == SAS_PROTOCOL_SMP) { + if (task->task_proto == SAS_PROTOCOL_SMP) set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_normal_io_completion; - } else { + else clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_error_io_completion; - } - break; - } -} - -/** - * isci_task_save_for_upper_layer_completion() - This function saves the - * request for later completion to the upper layer driver. - * @host: This parameter is a pointer to the host on which the the request - * should be queued (either as an error or success). - * @request: This parameter is the completed request. - * @response: This parameter is the response code for the completed task. - * @status: This parameter is the status code for the completed task. - * - * none. - */ -static void isci_task_save_for_upper_layer_completion( - struct isci_host *host, - struct isci_request *request, - enum service_response response, - enum exec_status status, - enum isci_completion_selection task_notification_selection) -{ - struct sas_task *task = isci_request_access_task(request); - - task_notification_selection - = isci_task_set_completion_status(task, response, status, - task_notification_selection); - - /* Tasks aborted specifically by a call to the lldd_abort_task - * function should not be completed to the host in the regular path. - */ - switch (task_notification_selection) { - - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - - /* Add to the completed list. */ - list_add(&request->completed_node, - &host->requests_to_complete); - - /* Take the request off the device's pending request list. */ - list_del_init(&request->dev_node); - break; - - case isci_perform_aborted_io_completion: - /* No notification to libsas because this request is - * already in the abort path. - */ - /* Wake up whatever process was waiting for this - * request to complete. - */ - WARN_ON(request->io_request_completion == NULL); - - if (request->io_request_completion != NULL) { - - /* Signal whoever is waiting that this - * request is complete. - */ - complete(request->io_request_completion); - } - break; - - case isci_perform_error_io_completion: - /* Use sas_task_abort */ - /* Add to the aborted list. */ - list_add(&request->completed_node, - &host->requests_to_errorback); - break; - - default: - /* 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) @@ -2715,295 +2746,164 @@ static void isci_request_io_request_complete(struct isci_host *ihost, 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; dev_dbg(&ihost->pdev->dev, - "%s: request = %p, task = %p,\n" + "%s: request = %p, task = %p, " "task->data_dir = %d completion_status = 0x%x\n", - __func__, - request, - task, - task->data_dir, - completion_status); + __func__, request, task, task->data_dir, completion_status); - spin_lock(&request->state_lock); - request_status = request->status; + /* The request is done from an SCU HW perspective. */ - /* Decode the request status. Note that if the request has been - * aborted by a task management function, we don't care - * what the status is. - */ - switch (request_status) { - - case aborted: - /* "aborted" indicates that the request was aborted by a task - * management function, since once a task management request is - * perfomed by the device, the request only completes because - * of the subsequent driver terminate. - * - * Aborted also means an external thread is explicitly managing - * this request, so that we do not complete it up the stack. - * - * The target is still there (since the TMF was successful). - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_COMPLETE; + /* This is an active request being completed from the core. */ + switch (completion_status) { - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; + case SCI_IO_FAILURE_RESPONSE_VALID: + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", + __func__, request, task); + + if (sas_protocol_ata(task->task_proto)) { + isci_process_stp_response(task, &request->stp.rsp); + } else if (SAS_PROTOCOL_SSP == task->task_proto) { + + /* crack the iu response buffer. */ + resp_iu = &request->ssp.rsp; + isci_request_process_response_iu(task, resp_iu, + &ihost->pdev->dev); + + } else if (SAS_PROTOCOL_SMP == task->task_proto) { + + dev_err(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID: " + "SAS_PROTOCOL_SMP protocol\n", + __func__); - complete_to_host = isci_perform_aborted_io_completion; - /* This was an aborted request. */ + } else + dev_err(&ihost->pdev->dev, + "%s: unknown protocol\n", __func__); - spin_unlock(&request->state_lock); + /* use the task status set in the task struct by the + * isci_request_process_response_iu call. + */ + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + response = task->task_status.resp; + status = task->task_status.stat; break; - case aborting: - /* aborting means that the task management function tried and - * failed to abort the request. We need to note the request - * as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the - * target as down. - * - * Aborting also means an external thread is explicitly managing - * this request, so that we do not complete it up the stack. - */ + case SCI_IO_SUCCESS: + case SCI_IO_SUCCESS_IO_DONE_EARLY: + + response = SAS_TASK_COMPLETE; + status = SAM_STAT_GOOD; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; - if (!idev) - /* The device has been /is being stopped. Note that - * we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_PHY_DOWN; + if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { - complete_to_host = isci_perform_aborted_io_completion; + /* This was an SSP / STP / SATA transfer. + * There is a possibility that less data than + * the maximum was transferred. + */ + u32 transferred_length = sci_req_tx_bytes(request); - /* This was an aborted request. */ + task->task_status.residual + = task->total_xfer_len - transferred_length; + + /* If there were residual bytes, call this an + * underrun. + */ + if (task->task_status.residual != 0) + status = SAS_DATA_UNDERRUN; - spin_unlock(&request->state_lock); + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", + __func__, status); + + } else + dev_dbg(&ihost->pdev->dev, "%s: SCI_IO_SUCCESS\n", + __func__); break; - case terminating: + case SCI_IO_FAILURE_TERMINATED: - /* This was an terminated request. This happens when - * the I/O is being terminated because of an action on - * the device (reset, tear down, etc.), and the I/O needs - * to be completed up the stack. - */ + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", + __func__, request, task); + + /* The request was terminated explicitly. */ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); response = SAS_TASK_UNDELIVERED; /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ if (!idev) status = SAS_DEVICE_UNKNOWN; else status = SAS_ABORTED_TASK; - - complete_to_host = isci_perform_aborted_io_completion; - - /* This was a terminated request. */ - - spin_unlock(&request->state_lock); break; - case dead: - /* This was a terminated request that timed-out during the - * termination process. There is no task to complete to - * libsas. - */ - complete_to_host = isci_perform_normal_io_completion; - spin_unlock(&request->state_lock); - break; + case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: - default: - - /* The request is done from an SCU HW perspective. */ - request->status = completed; - - spin_unlock(&request->state_lock); - - /* This is an active request being completed from the core. */ - switch (completion_status) { - - case SCI_IO_FAILURE_RESPONSE_VALID: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", - __func__, - request, - task); - - if (sas_protocol_ata(task->task_proto)) { - isci_process_stp_response(task, &request->stp.rsp); - } else if (SAS_PROTOCOL_SSP == task->task_proto) { - - /* crack the iu response buffer. */ - resp_iu = &request->ssp.rsp; - isci_request_process_response_iu(task, resp_iu, - &ihost->pdev->dev); - - } else if (SAS_PROTOCOL_SMP == task->task_proto) { - - dev_err(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID: " - "SAS_PROTOCOL_SMP protocol\n", - __func__); - - } else - dev_err(&ihost->pdev->dev, - "%s: unknown protocol\n", __func__); - - /* use the task status set in the task struct by the - * isci_request_process_response_iu call. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = task->task_status.resp; - status = task->task_status.stat; - break; + isci_request_handle_controller_specific_errors(idev, request, + task, &response, + &status); + break; - case SCI_IO_SUCCESS: - case SCI_IO_SUCCESS_IO_DONE_EARLY: + case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: + /* This is a special case, in that the I/O completion + * is telling us that the device needs a reset. + * In order for the device reset condition to be + * noticed, the I/O has to be handled in the error + * handler. Set the reset flag and cause the + * SCSI error thread to be scheduled. + */ + spin_lock_irqsave(&task->task_state_lock, task_flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, task_flags); - response = SAS_TASK_COMPLETE; - status = SAM_STAT_GOOD; - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + /* Fail the I/O. */ + response = SAS_TASK_UNDELIVERED; + status = SAM_STAT_TASK_ABORTED; - if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - /* This was an SSP / STP / SATA transfer. - * There is a possibility that less data than - * the maximum was transferred. - */ - u32 transferred_length = sci_req_tx_bytes(request); + case SCI_FAILURE_RETRY_REQUIRED: - task->task_status.residual - = task->total_xfer_len - transferred_length; + /* Fail the I/O so it can be retried. */ + response = SAS_TASK_UNDELIVERED; + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; - /* If there were residual bytes, call this an - * underrun. - */ - if (task->task_status.residual != 0) - status = SAS_DATA_UNDERRUN; + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", - __func__, - status); - } else - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS\n", - __func__); + default: + /* Catch any otherwise unhandled error codes here. */ + dev_dbg(&ihost->pdev->dev, + "%s: invalid completion code: 0x%x - " + "isci_request = %p\n", + __func__, completion_status, request); - break; + response = SAS_TASK_UNDELIVERED; - case SCI_IO_FAILURE_TERMINATED: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", - __func__, - request, - task); + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; - /* The request was terminated explicitly. No handling - * is needed in the SCSI error handler path. - */ + if (SAS_PROTOCOL_SMP == task->task_proto) set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; - - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; - - complete_to_host = isci_perform_normal_io_completion; - break; - - case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: - - isci_request_handle_controller_specific_errors( - idev, request, task, &response, &status, - &complete_to_host); - - break; - - case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: - /* This is a special case, in that the I/O completion - * is telling us that the device needs a reset. - * In order for the device reset condition to be - * noticed, the I/O has to be handled in the error - * handler. Set the reset flag and cause the - * SCSI error thread to be scheduled. - */ - spin_lock_irqsave(&task->task_state_lock, task_flags); - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, task_flags); - - /* Fail the I/O. */ - response = SAS_TASK_UNDELIVERED; - status = SAM_STAT_TASK_ABORTED; - - complete_to_host = isci_perform_error_io_completion; + else clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; - - case SCI_FAILURE_RETRY_REQUIRED: - - /* Fail the I/O so it can be retried. */ - response = SAS_TASK_UNDELIVERED; - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; - - complete_to_host = isci_perform_normal_io_completion; - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; - - - default: - /* Catch any otherwise unhandled error codes here. */ - dev_dbg(&ihost->pdev->dev, - "%s: invalid completion code: 0x%x - " - "isci_request = %p\n", - __func__, completion_status, request); - - response = SAS_TASK_UNDELIVERED; - - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; - - if (SAS_PROTOCOL_SMP == task->task_proto) { - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_normal_io_completion; - } else { - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_error_io_completion; - } - break; - } break; } @@ -3038,10 +2938,18 @@ static void isci_request_io_request_complete(struct isci_host *ihost, break; } - /* Put the completed request on the correct list */ - isci_task_save_for_upper_layer_completion(ihost, request, response, - status, complete_to_host - ); + spin_lock_irqsave(&task->task_state_lock, task_flags); + + task->task_status.resp = response; + task->task_status.stat = status; + + if (test_bit(IREQ_COMPLETE_IN_TARGET, &request->flags)) { + /* Normal notification (task_done) */ + 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, task_flags); /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); @@ -3051,6 +2959,8 @@ static void isci_request_io_request_complete(struct isci_host *ihost, * task to recognize the already completed case. */ set_bit(IREQ_TERMINATED, &request->flags); + + ireq_done(ihost, request, task); } static void sci_request_started_state_enter(struct sci_base_state_machine *sm) @@ -3169,7 +3079,7 @@ sci_general_request_construct(struct isci_host *ihost, sci_init_sm(&ireq->sm, sci_request_state_table, SCI_REQ_INIT); ireq->target_device = idev; - ireq->protocol = SCIC_NO_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_NONE; ireq->saved_rx_frame_index = SCU_INVALID_FRAME_INDEX; ireq->sci_status = SCI_SUCCESS; @@ -3193,7 +3103,7 @@ sci_io_request_construct(struct isci_host *ihost, if (dev->dev_type == SAS_END_DEV) /* pass */; - else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) + else if (dev_is_sata(dev)) memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd)); else if (dev_is_expander(dev)) /* pass */; @@ -3215,10 +3125,15 @@ enum sci_status sci_task_request_construct(struct isci_host *ihost, /* Build the common part of the request */ sci_general_request_construct(ihost, idev, ireq); - if (dev->dev_type == SAS_END_DEV || - dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { + if (dev->dev_type == SAS_END_DEV || dev_is_sata(dev)) { set_bit(IREQ_TMF, &ireq->flags); memset(ireq->tc, 0, sizeof(struct scu_task_context)); + + /* Set the protocol indicator. */ + if (dev_is_sata(dev)) + ireq->protocol = SAS_PROTOCOL_STP; + else + ireq->protocol = SAS_PROTOCOL_SSP; } else status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; @@ -3311,7 +3226,7 @@ sci_io_request_construct_smp(struct device *dev, if (!dma_map_sg(dev, sg, 1, DMA_TO_DEVICE)) return SCI_FAILURE; - ireq->protocol = SCIC_SMP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_SMP; /* byte swap the smp request. */ @@ -3496,9 +3411,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t ireq->io_request_completion = NULL; ireq->flags = 0; ireq->num_sg_entries = 0; - INIT_LIST_HEAD(&ireq->completed_node); - INIT_LIST_HEAD(&ireq->dev_node); - isci_request_change_state(ireq, allocated); return ireq; } @@ -3582,26 +3494,15 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide spin_unlock_irqrestore(&ihost->scic_lock, flags); return status; } - /* Either I/O started OK, or the core has signaled that * the device needs a target reset. - * - * In either case, hold onto the I/O for later. - * - * Update it's status and add it to the list in the - * remote device object. */ - list_add(&ireq->dev_node, &idev->reqs_in_process); - - if (status == SCI_SUCCESS) { - isci_request_change_state(ireq, started); - } else { + if (status != SCI_SUCCESS) { /* The request did not really start in the * hardware, so clear the request handle * here so no terminations will be done. */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, completed); } spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 057f2378452..aff95317fcf 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -61,30 +61,6 @@ #include "scu_task_context.h" /** - * struct isci_request_status - This enum defines the possible states of an I/O - * request. - * - * - */ -enum isci_request_status { - unallocated = 0x00, - allocated = 0x01, - started = 0x02, - completed = 0x03, - aborting = 0x04, - aborted = 0x05, - terminating = 0x06, - dead = 0x07 -}; - -enum sci_request_protocol { - SCIC_NO_PROTOCOL, - SCIC_SMP_PROTOCOL, - SCIC_SSP_PROTOCOL, - SCIC_STP_PROTOCOL -}; /* XXX remove me, use sas_task.{dev|task_proto} instead */; - -/** * isci_stp_request - extra request infrastructure to handle pio/atapi protocol * @pio_len - number of bytes requested at PIO setup * @status - pio setup ending status value to tell us if we need @@ -104,11 +80,14 @@ struct isci_stp_request { }; struct isci_request { - enum isci_request_status status; #define IREQ_COMPLETE_IN_TARGET 0 #define IREQ_TERMINATED 1 #define IREQ_TMF 2 #define IREQ_ACTIVE 3 + #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ + #define IREQ_TC_ABORT_POSTED 5 + #define IREQ_ABORT_PATH_ACTIVE 6 + #define IREQ_NO_AUTO_FREE_TAG 7 /* Set when being explicitly managed */ unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { @@ -116,11 +95,6 @@ struct isci_request { struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */ } ttype_ptr; struct isci_host *isci_host; - /* For use in the requests_to_{complete|abort} lists: */ - struct list_head completed_node; - /* For use in the reqs_in_process list: */ - struct list_head dev_node; - spinlock_t state_lock; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; unsigned int num_sg_entries; @@ -140,7 +114,7 @@ struct isci_request { struct isci_host *owning_controller; struct isci_remote_device *target_device; u16 io_tag; - enum sci_request_protocol protocol; + enum sas_protocol protocol; u32 scu_status; /* hardware result */ u32 sci_status; /* upper layer disposition */ u32 post_context; @@ -309,92 +283,6 @@ sci_io_request_get_dma_addr(struct isci_request *ireq, void *virt_addr) return ireq->request_daddr + (requested_addr - base_addr); } -/** - * isci_request_change_state() - This function sets the status of the request - * object. - * @request: This parameter points to the isci_request object - * @status: This Parameter is the new status of the object - * - */ -static inline enum isci_request_status -isci_request_change_state(struct isci_request *isci_request, - enum isci_request_status status) -{ - enum isci_request_status old_state; - unsigned long flags; - - dev_dbg(&isci_request->isci_host->pdev->dev, - "%s: isci_request = %p, state = 0x%x\n", - __func__, - isci_request, - status); - - BUG_ON(isci_request == NULL); - - spin_lock_irqsave(&isci_request->state_lock, flags); - old_state = isci_request->status; - isci_request->status = status; - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - return old_state; -} - -/** - * isci_request_change_started_to_newstate() - This function sets the status of - * the request object. - * @request: This parameter points to the isci_request object - * @status: This Parameter is the new status of the object - * - * state previous to any change. - */ -static inline enum isci_request_status -isci_request_change_started_to_newstate(struct isci_request *isci_request, - struct completion *completion_ptr, - enum isci_request_status newstate) -{ - enum isci_request_status old_state; - unsigned long flags; - - spin_lock_irqsave(&isci_request->state_lock, flags); - - old_state = isci_request->status; - - if (old_state == started || old_state == aborting) { - BUG_ON(isci_request->io_request_completion != NULL); - - isci_request->io_request_completion = completion_ptr; - isci_request->status = newstate; - } - - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - dev_dbg(&isci_request->isci_host->pdev->dev, - "%s: isci_request = %p, old_state = 0x%x\n", - __func__, - isci_request, - old_state); - - return old_state; -} - -/** - * isci_request_change_started_to_aborted() - This function sets the status of - * the request object. - * @request: This parameter points to the isci_request object - * @completion_ptr: This parameter is saved as the kernel completion structure - * signalled when the old request completes. - * - * state previous to any change. - */ -static inline enum isci_request_status -isci_request_change_started_to_aborted(struct isci_request *isci_request, - struct completion *completion_ptr) -{ - return isci_request_change_started_to_newstate(isci_request, - completion_ptr, - aborted); -} - #define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr) #define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr) @@ -404,8 +292,6 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost, u16 tag); int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev, struct sas_task *task, u16 tag); -void isci_terminate_pending_requests(struct isci_host *ihost, - struct isci_remote_device *idev); enum sci_status sci_task_request_construct(struct isci_host *ihost, struct isci_remote_device *idev, @@ -421,5 +307,4 @@ static inline int isci_task_is_ncq_recovery(struct sas_task *task) task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ); } - #endif /* !defined(_ISCI_REQUEST_H_) */ diff --git a/drivers/scsi/isci/scu_completion_codes.h b/drivers/scsi/isci/scu_completion_codes.h index c8b329c695f..071cb74a211 100644 --- a/drivers/scsi/isci/scu_completion_codes.h +++ b/drivers/scsi/isci/scu_completion_codes.h @@ -224,6 +224,7 @@ * 32-bit value like we want, each immediate value must be cast to a u32. */ #define SCU_TASK_DONE_GOOD ((u32)0x00) +#define SCU_TASK_DONE_TX_RAW_CMD_ERR ((u32)0x08) #define SCU_TASK_DONE_CRC_ERR ((u32)0x14) #define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14) #define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15) @@ -237,6 +238,7 @@ #define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A) #define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A) #define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B) +#define SCU_TASK_DONE_BREAK_RCVD ((u32)0x1B) #define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B) #define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C) #define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 374254ede9d..6bc74eb012c 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -78,54 +78,25 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, enum exec_status status) { - enum isci_completion_selection disposition; + unsigned long flags; - disposition = isci_perform_normal_io_completion; - disposition = isci_task_set_completion_status(task, response, status, - disposition); + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n", + __func__, task, response, status); - /* Tasks aborted specifically by a call to the lldd_abort_task - * function should not be completed to the host in the regular path. - */ - switch (disposition) { - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - dev_dbg(&ihost->pdev->dev, - "%s: Normal - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - - task->lldd_task = NULL; - task->task_done(task); - break; - - case isci_perform_aborted_io_completion: - /* - * No notification because this request is already in the - * abort path. - */ - dev_dbg(&ihost->pdev->dev, - "%s: Aborted - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - break; + spin_lock_irqsave(&task->task_state_lock, flags); - case isci_perform_error_io_completion: - /* Use sas_task_abort */ - dev_dbg(&ihost->pdev->dev, - "%s: Error - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - sas_task_abort(task); - break; + task->task_status.resp = response; + task->task_status.stat = status; - default: - dev_dbg(&ihost->pdev->dev, - "%s: isci task notification default case!", - __func__); - sas_task_abort(task); - break; - } + /* Normal notification (task_done) */ + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + task->lldd_task = NULL; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + task->task_done(task); } #define for_each_sas_task(num, task) \ @@ -289,60 +260,6 @@ 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) @@ -400,17 +317,11 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - - if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data); - - isci_request_change_state(ireq, started); - - /* add the request to the remote device request list. */ - list_add(&ireq->dev_node, &idev->reqs_in_process); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* The RNC must be unsuspended before the TMF can get a response. */ + isci_remote_device_resume_from_abort(ihost, idev); + /* Wait for the TMF to complete, or a timeout. */ timeleft = wait_for_completion_timeout(&completion, msecs_to_jiffies(timeout_ms)); @@ -419,32 +330,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost, /* 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); - - sci_controller_terminate_request(ihost, idev, ireq); - - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - 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_remote_device_suspend_terminate(ihost, idev, ireq); } isci_print_tmf(ihost, tmf); @@ -476,315 +362,21 @@ static int isci_task_execute_tmf(struct isci_host *ihost, } static void isci_task_build_tmf(struct isci_tmf *tmf, - enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), - void *cb_data) + enum isci_tmf_function_codes code) { memset(tmf, 0, sizeof(*tmf)); - - tmf->tmf_code = code; - tmf->cb_state_func = tmf_sent_cb; - tmf->cb_data = cb_data; + tmf->tmf_code = code; } static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf, enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), struct isci_request *old_request) { - isci_task_build_tmf(tmf, code, tmf_sent_cb, old_request); + isci_task_build_tmf(tmf, code); tmf->io_tag = old_request->io_tag; } /** - * isci_task_validate_request_to_abort() - This function checks the given I/O - * against the "started" state. If the request is still "started", it's - * state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD - * BEFORE CALLING THIS FUNCTION. - * @isci_request: This parameter specifies the request object to control. - * @isci_host: This parameter specifies the ISCI host object - * @isci_device: This is the device to which the request is pending. - * @aborted_io_completion: This is a completion structure that will be added to - * the request in case it is changed to aborting; this completion is - * triggered when the request is fully completed. - * - * Either "started" on successful change of the task status to "aborted", or - * "unallocated" if the task cannot be controlled. - */ -static enum isci_request_status isci_task_validate_request_to_abort( - struct isci_request *isci_request, - struct isci_host *isci_host, - struct isci_remote_device *isci_device, - struct completion *aborted_io_completion) -{ - enum isci_request_status old_state = unallocated; - - /* Only abort the task if it's in the - * device's request_in_process list - */ - if (isci_request && !list_empty(&isci_request->dev_node)) { - old_state = isci_request_change_started_to_aborted( - isci_request, aborted_io_completion); - - } - - return old_state; -} - -static int isci_request_is_dealloc_managed(enum isci_request_status stat) -{ - switch (stat) { - case aborted: - case aborting: - case terminating: - case completed: - case dead: - return true; - default: - return false; - } -} - -/** - * isci_terminate_request_core() - This function will terminate the given - * request, and wait for it to complete. This function must only be called - * from a thread that can wait. Note that the request is terminated and - * completed (back to the host, if started there). - * @ihost: This SCU. - * @idev: The target. - * @isci_request: The I/O request to be terminated. - * - */ -static void isci_terminate_request_core(struct isci_host *ihost, - struct isci_remote_device *idev, - struct isci_request *isci_request) -{ - enum sci_status status = SCI_SUCCESS; - bool was_terminated = false; - bool needs_cleanup_handling = false; - unsigned long flags; - unsigned long termination_completed = 1; - struct completion *io_request_completion; - - dev_dbg(&ihost->pdev->dev, - "%s: device = %p; request = %p\n", - __func__, idev, isci_request); - - spin_lock_irqsave(&ihost->scic_lock, flags); - - io_request_completion = isci_request->io_request_completion; - - /* Note that we are not going to control - * the target to abort the request. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &isci_request->flags); - - /* Make sure the request wasn't just sitting around signalling - * device condition (if the request handle is NULL, then the - * request completed but needed additional handling here). - */ - if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) { - was_terminated = true; - needs_cleanup_handling = true; - status = sci_controller_terminate_request(ihost, - idev, - isci_request); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - /* - * The only time the request to terminate will - * fail is when the io request is completed and - * being aborted. - */ - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_controller_terminate_request" - " returned = 0x%x\n", - __func__, status); - - isci_request->io_request_completion = NULL; - - } else { - if (was_terminated) { - dev_dbg(&ihost->pdev->dev, - "%s: before completion wait (%p/%p)\n", - __func__, isci_request, io_request_completion); - - /* Wait here for the request to complete. */ - termination_completed - = wait_for_completion_timeout( - io_request_completion, - msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); - - if (!termination_completed) { - - /* The request to terminate has timed out. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - - /* Check for state changes. */ - 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. - */ - isci_request_mark_zombie(ihost, - isci_request); - needs_cleanup_handling = true; - } else - termination_completed = 1; - - spin_unlock_irqrestore(&ihost->scic_lock, - flags); - - if (!termination_completed) { - - dev_dbg(&ihost->pdev->dev, - "%s: *** Timeout waiting for " - "termination(%p/%p)\n", - __func__, io_request_completion, - isci_request); - - /* The request can no longer be referenced - * safely since it may go away if the - * termination every really does complete. - */ - isci_request = NULL; - } - } - if (termination_completed) - dev_dbg(&ihost->pdev->dev, - "%s: after completion wait (%p/%p)\n", - __func__, isci_request, io_request_completion); - } - - if (termination_completed) { - - isci_request->io_request_completion = NULL; - - /* Peek at the status of the request. This will tell - * us if there was special handling on the request such that it - * needs to be detached and freed here. - */ - spin_lock_irqsave(&isci_request->state_lock, flags); - - needs_cleanup_handling - = isci_request_is_dealloc_managed( - isci_request->status); - - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - } - 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); - } - } - } -} - -/** - * isci_terminate_pending_requests() - This function will change the all of the - * requests on the given device's state to "aborting", will terminate the - * requests, and wait for them to complete. This function must only be - * called from a thread that can wait. Note that the requests are all - * terminated and completed (back to the host, if started there). - * @isci_host: This parameter specifies SCU. - * @idev: This parameter specifies the target. - * - */ -void isci_terminate_pending_requests(struct isci_host *ihost, - struct isci_remote_device *idev) -{ - struct completion request_completion; - enum isci_request_status old_state; - unsigned long flags; - LIST_HEAD(list); - - spin_lock_irqsave(&ihost->scic_lock, flags); - list_splice_init(&idev->reqs_in_process, &list); - - /* assumes that isci_terminate_request_core deletes from the list */ - while (!list_empty(&list)) { - struct isci_request *ireq = list_entry(list.next, typeof(*ireq), dev_node); - - /* Change state to "terminating" if it is currently - * "started". - */ - old_state = isci_request_change_started_to_newstate(ireq, - &request_completion, - terminating); - switch (old_state) { - case started: - case completed: - case aborting: - break; - default: - /* termination in progress, or otherwise dispositioned. - * We know the request was on 'list' so should be safe - * to move it back to reqs_in_process - */ - list_move(&ireq->dev_node, &idev->reqs_in_process); - ireq = NULL; - break; - } - - if (!ireq) - continue; - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - init_completion(&request_completion); - - dev_dbg(&ihost->pdev->dev, - "%s: idev=%p request=%p; task=%p old_state=%d\n", - __func__, idev, ireq, - (!test_bit(IREQ_TMF, &ireq->flags) - ? isci_request_access_task(ireq) - : NULL), - old_state); - - /* If the old_state is started: - * This request was not already being aborted. If it had been, - * then the aborting I/O (ie. the TMF request) would not be in - * the aborting state, and thus would be terminated here. Note - * that since the TMF completion's call to the kernel function - * "complete()" does not happen until the pending I/O request - * terminate fully completes, we do not have to implement a - * special wait here for already aborting requests - the - * termination of the TMF request will force the request - * to finish it's already started terminate. - * - * If old_state == completed: - * This request completed from the SCU hardware perspective - * and now just needs cleaning up in terms of freeing the - * request and potentially calling up to libsas. - * - * If old_state == aborting: - * This request has already gone through a TMF timeout, but may - * not have been terminated; needs cleaning up at least. - */ - isci_terminate_request_core(ihost, idev, ireq); - spin_lock_irqsave(&ihost->scic_lock, flags); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); -} - -/** * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain * Template functions. * @lun: This parameter specifies the lun to be reset. @@ -807,7 +399,7 @@ static int isci_task_send_lu_reset_sas( * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED"). */ - isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset, NULL, NULL); + isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset); #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS); @@ -826,42 +418,44 @@ static int isci_task_send_lu_reset_sas( int isci_task_lu_reset(struct domain_device *dev, u8 *lun) { - struct isci_host *isci_host = dev_to_ihost(dev); - struct isci_remote_device *isci_device; + struct isci_host *ihost = dev_to_ihost(dev); + struct isci_remote_device *idev; unsigned long flags; - int ret; + int ret = TMF_RESP_FUNC_COMPLETE; - spin_lock_irqsave(&isci_host->scic_lock, flags); - isci_device = isci_lookup_device(dev); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_lock_irqsave(&ihost->scic_lock, flags); + idev = isci_get_device(dev->lldd_dev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_dbg(&ihost->pdev->dev, "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", - __func__, dev, isci_host, isci_device); + __func__, dev, ihost, idev); - if (!isci_device) { - /* If the device is gone, stop the escalations. */ - dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__); + if (!idev) { + /* If the device is gone, escalate to I_T_Nexus_Reset. */ + dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); - ret = TMF_RESP_FUNC_COMPLETE; + ret = TMF_RESP_FUNC_FAILED; goto out; } - /* Send the task management part of the reset. */ - if (dev_is_sata(dev)) { - sas_ata_schedule_reset(dev); - ret = TMF_RESP_FUNC_COMPLETE; - } else - ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun); - - /* If the LUN reset worked, all the I/O can now be terminated. */ - if (ret == TMF_RESP_FUNC_COMPLETE) - /* Terminate all I/O now. */ - isci_terminate_pending_requests(isci_host, - isci_device); - + /* Suspend the RNC, kill all TCs */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { + /* The suspend/terminate only fails if isci_get_device fails */ + ret = TMF_RESP_FUNC_FAILED; + goto out; + } + /* All pending I/Os have been terminated and cleaned up. */ + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (dev_is_sata(dev)) + sas_ata_schedule_reset(dev); + else + /* Send the task management part of the reset. */ + ret = isci_task_send_lu_reset_sas(ihost, idev, lun); + } out: - isci_put_device(isci_device); + isci_put_device(idev); return ret; } @@ -882,63 +476,6 @@ int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) /* Task Management Functions. Must be called from process context. */ /** - * isci_abort_task_process_cb() - This is a helper function for the abort task - * TMF command. It manages the request state with respect to the successful - * transmission / completion of the abort task request. - * @cb_state: This parameter specifies when this function was called - after - * the TMF request has been started and after it has timed-out. - * @tmf: This parameter specifies the TMF in progress. - * - * - */ -static void isci_abort_task_process_cb( - enum isci_tmf_cb_state cb_state, - struct isci_tmf *tmf, - void *cb_data) -{ - struct isci_request *old_request; - - old_request = (struct isci_request *)cb_data; - - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: tmf=%p, old_request=%p\n", - __func__, tmf, old_request); - - switch (cb_state) { - - case isci_tmf_started: - /* The TMF has been started. Nothing to do here, since the - * request state was already set to "aborted" by the abort - * task function. - */ - if ((old_request->status != aborted) - && (old_request->status != completed)) - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: Bad request status (%d): tmf=%p, old_request=%p\n", - __func__, old_request->status, tmf, old_request); - break; - - case isci_tmf_timed_out: - - /* Set the task's state to "aborting", since the abort task - * function thread set it to "aborted" (above) in anticipation - * of the task management request working correctly. Since the - * timeout has now fired, the TMF request failed. We set the - * state such that the request completion will indicate the - * device is no longer present. - */ - isci_request_change_state(old_request, aborting); - break; - - default: - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: Bad cb_state (%d): tmf=%p, old_request=%p\n", - __func__, cb_state, tmf, old_request); - break; - } -} - -/** * isci_task_abort_task() - This function is one of the SAS Domain Template * functions. This function is called by libsas to abort a specified task. * @task: This parameter specifies the SAS task to abort. @@ -947,22 +484,20 @@ static void isci_abort_task_process_cb( */ int isci_task_abort_task(struct sas_task *task) { - struct isci_host *isci_host = dev_to_ihost(task->dev); + struct isci_host *ihost = dev_to_ihost(task->dev); DECLARE_COMPLETION_ONSTACK(aborted_io_completion); struct isci_request *old_request = NULL; - enum isci_request_status old_state; - struct isci_remote_device *isci_device = NULL; + struct isci_remote_device *idev = NULL; struct isci_tmf tmf; int ret = TMF_RESP_FUNC_FAILED; unsigned long flags; - int perform_termination = 0; /* Get the isci_request reference from the task. Note that * this check does not depend on the pending request list * in the device, because tasks driving resets may land here * after completion in the core. */ - spin_lock_irqsave(&isci_host->scic_lock, flags); + spin_lock_irqsave(&ihost->scic_lock, flags); spin_lock(&task->task_state_lock); old_request = task->lldd_task; @@ -971,20 +506,29 @@ int isci_task_abort_task(struct sas_task *task) if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && (task->task_state_flags & SAS_TASK_AT_INITIATOR) && old_request) - isci_device = isci_lookup_device(task->dev); + idev = isci_get_device(task->dev->lldd_dev); spin_unlock(&task->task_state_lock); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, - "%s: dev = %p, task = %p, old_request == %p\n", - __func__, isci_device, task, old_request); + dev_warn(&ihost->pdev->dev, + "%s: dev = %p (%s%s), task = %p, old_request == %p\n", + __func__, idev, + (dev_is_sata(task->dev) ? "STP/SATA" + : ((dev_is_expander(task->dev)) + ? "SMP" + : "SSP")), + ((idev) ? ((test_bit(IDEV_GONE, &idev->flags)) + ? " IDEV_GONE" + : "") + : " <NULL>"), + task, old_request); /* Device reset conditions signalled in task_state_flags are the * responsbility of libsas to observe at the start of the error * handler thread. */ - if (!isci_device || !old_request) { + if (!idev || !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 @@ -998,108 +542,72 @@ int isci_task_abort_task(struct sas_task *task) ret = TMF_RESP_FUNC_COMPLETE; - dev_dbg(&isci_host->pdev->dev, - "%s: abort task not needed for %p\n", - __func__, task); + dev_warn(&ihost->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); goto out; } - - spin_lock_irqsave(&isci_host->scic_lock, flags); - - /* Check the request status and change to "aborted" if currently - * "starting"; if true then set the I/O kernel completion - * struct that will be triggered when the request completes. - */ - old_state = isci_task_validate_request_to_abort( - old_request, isci_host, isci_device, - &aborted_io_completion); - if ((old_state != started) && - (old_state != completed) && - (old_state != aborting)) { - - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - - /* The request was already being handled by someone else (because - * they got to set the state away from started). - */ - dev_dbg(&isci_host->pdev->dev, - "%s: device = %p; old_request %p already being aborted\n", - __func__, - isci_device, old_request); - ret = TMF_RESP_FUNC_COMPLETE; + /* Suspend the RNC, kill the TC */ + if (isci_remote_device_suspend_terminate(ihost, idev, old_request) + != SCI_SUCCESS) { + dev_warn(&ihost->pdev->dev, + "%s: isci_remote_device_reset_terminate(dev=%p, " + "req=%p, task=%p) failed\n", + __func__, idev, old_request, task); + ret = TMF_RESP_FUNC_FAILED; goto out; } + spin_lock_irqsave(&ihost->scic_lock, flags); + if (task->task_proto == SAS_PROTOCOL_SMP || sas_protocol_ata(task->task_proto) || - test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { + test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) || + test_bit(IDEV_GONE, &idev->flags)) { - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, - "%s: %s request" - " or complete_in_target (%d), thus no TMF\n", - __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)); - - 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); - } + /* No task to send, so explicitly resume the device here */ + isci_remote_device_resume_from_abort(ihost, idev); - /* 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; + dev_warn(&ihost->pdev->dev, + "%s: %s request" + " or complete_in_target (%d), " + "or IDEV_GONE (%d), thus no TMF\n", + __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), + test_bit(IDEV_GONE, &idev->flags)); + + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + task->task_state_flags |= SAS_TASK_STATE_DONE; + spin_unlock_irqrestore(&task->task_state_lock, flags); + ret = TMF_RESP_FUNC_COMPLETE; } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, - isci_abort_task_process_cb, old_request); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* Send the task management request. */ #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ - ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, + ret = isci_task_execute_tmf(ihost, idev, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); - - if (ret == TMF_RESP_FUNC_COMPLETE) - perform_termination = 1; - else - dev_dbg(&isci_host->pdev->dev, - "%s: isci_task_send_tmf failed\n", __func__); } - 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); - } - - /* Make sure we do not leave a reference to aborted_io_completion */ - old_request->io_request_completion = NULL; - out: - isci_put_device(isci_device); +out: + dev_warn(&ihost->pdev->dev, + "%s: Done; dev = %p, task = %p , old_request == %p\n", + __func__, idev, task, old_request); + isci_put_device(idev); return ret; } @@ -1195,14 +703,11 @@ isci_task_request_complete(struct isci_host *ihost, { struct isci_tmf *tmf = isci_request_access_tmf(ireq); struct completion *tmf_complete = NULL; - struct completion *request_complete = ireq->io_request_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, status=%d\n", __func__, ireq, completion_status); - isci_request_change_state(ireq, completed); - set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); if (tmf) { @@ -1226,20 +731,11 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - /* 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); - } + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); - /* "request_complete" is set if the task was being terminated. */ - if (request_complete) - complete(request_complete); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); /* The task management part completes last. */ if (tmf_complete) @@ -1250,48 +746,38 @@ static int isci_reset_device(struct isci_host *ihost, struct domain_device *dev, struct isci_remote_device *idev) { - int rc; - unsigned long flags; - enum sci_status status; + int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); - 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); + /* Suspend the RNC, terminate all outstanding TCs. */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { rc = TMF_RESP_FUNC_FAILED; goto out; } - - if (scsi_is_sas_phy_local(phy)) { - struct isci_phy *iphy = &ihost->phys[phy->number]; - - rc = isci_port_perform_hard_reset(ihost, iport, iphy); - } else - rc = sas_phy_reset(phy, !dev_is_sata(dev)); - - /* Terminate in-progress I/O now. */ - isci_remote_device_nuke_requests(ihost, idev); - - /* Since all pending TCs have been cleaned, resume the RNC. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset_complete(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset_complete(%p) " - "returned %d!\n", __func__, idev, status); + /* Note that since the termination for outstanding requests succeeded, + * this function will return success. This is because the resets will + * only fail if the device has been removed (ie. hotplug), and the + * primary duty of this function is to cleanup tasks, so that is the + * relevant status. + */ + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (scsi_is_sas_phy_local(phy)) { + struct isci_phy *iphy = &ihost->phys[phy->number]; + + reset_stat = isci_port_perform_hard_reset(ihost, iport, + iphy); + } else + reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); } + /* Explicitly resume the RNC here, since there was no task sent. */ + isci_remote_device_resume_from_abort(ihost, idev); - dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); + dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", + __func__, idev, reset_stat); out: sas_put_local_phy(phy); return rc; @@ -1305,7 +791,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(dev); + idev = isci_get_device(dev->lldd_dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!idev) { diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 7b6d0e32fd9..9c06cbad1d2 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -63,19 +63,6 @@ struct isci_request; /** - * enum isci_tmf_cb_state - This enum defines the possible states in which the - * TMF callback function is invoked during the TMF execution process. - * - * - */ -enum isci_tmf_cb_state { - - isci_tmf_init_state = 0, - isci_tmf_started, - isci_tmf_timed_out -}; - -/** * enum isci_tmf_function_codes - This enum defines the possible preparations * of task management requests. * @@ -87,6 +74,7 @@ enum isci_tmf_function_codes { isci_tmf_ssp_task_abort = TMF_ABORT_TASK, isci_tmf_ssp_lun_reset = TMF_LU_RESET, }; + /** * struct isci_tmf - This class represents the task management object which * acts as an interface to libsas for processing task management requests @@ -106,15 +94,6 @@ struct isci_tmf { u16 io_tag; enum isci_tmf_function_codes tmf_code; int status; - - /* The optional callback function allows the user process to - * track the TMF transmit / timeout conditions. - */ - void (*cb_state_func)( - enum isci_tmf_cb_state, - struct isci_tmf *, void *); - void *cb_data; - }; static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf) @@ -208,113 +187,4 @@ int isci_queuecommand( struct scsi_cmnd *scsi_cmd, void (*donefunc)(struct scsi_cmnd *)); -/** - * enum isci_completion_selection - This enum defines the possible actions to - * take with respect to a given request's notification back to libsas. - * - * - */ -enum isci_completion_selection { - - isci_perform_normal_io_completion, /* Normal notify (task_done) */ - isci_perform_aborted_io_completion, /* No notification. */ - isci_perform_error_io_completion /* Use sas_task_abort */ -}; - -/** - * isci_task_set_completion_status() - This function sets the completion status - * for the request. - * @task: This parameter is the completed request. - * @response: This parameter is the response code for the completed task. - * @status: This parameter is the status code for the completed task. - * -* @return The new notification mode for the request. -*/ -static inline enum isci_completion_selection -isci_task_set_completion_status( - struct sas_task *task, - enum service_response response, - enum exec_status status, - enum isci_completion_selection task_notification_selection) -{ - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - - /* If a device reset is being indicated, make sure the I/O - * is in the error path. - */ - if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) { - /* Fail the I/O to make sure it goes into the error path. */ - response = SAS_TASK_UNDELIVERED; - status = SAM_STAT_TASK_ABORTED; - - task_notification_selection = isci_perform_error_io_completion; - } - 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: - - if (task->task_proto == SAS_PROTOCOL_SMP) { - /* There is no error escalation in the SMP case. - * Convert to a normal completion to avoid the - * timeout in the discovery path and to let the - * next action take place quickly. - */ - task_notification_selection - = isci_perform_normal_io_completion; - - /* Fall through to the normal case... */ - } else { - /* Use sas_task_abort */ - /* Leave SAS_TASK_STATE_DONE clear - * Leave SAS_TASK_AT_INITIATOR set. - */ - break; - } - - case isci_perform_aborted_io_completion: - /* This path can occur with task-managed requests as well as - * requests terminated because of LUN or device resets. - */ - /* Fall through to the normal case... */ - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - 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", - task_notification_selection); - break; - } - - spin_unlock_irqrestore(&task->task_state_lock, flags); - - return task_notification_selection; - -} #endif /* !defined(_SCI_TASK_H_) */ diff --git a/drivers/scsi/isci/unsolicited_frame_control.c b/drivers/scsi/isci/unsolicited_frame_control.c index 16f88ab939c..04a6d0d59a2 100644 --- a/drivers/scsi/isci/unsolicited_frame_control.c +++ b/drivers/scsi/isci/unsolicited_frame_control.c @@ -57,31 +57,19 @@ #include "unsolicited_frame_control.h" #include "registers.h" -int sci_unsolicited_frame_control_construct(struct isci_host *ihost) +void sci_unsolicited_frame_control_construct(struct isci_host *ihost) { struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control; struct sci_unsolicited_frame *uf; - u32 buf_len, header_len, i; - dma_addr_t dma; - size_t size; - void *virt; - - /* - * Prepare all of the memory sizes for the UF headers, UF address - * table, and UF buffers themselves. - */ - buf_len = SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE; - header_len = SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header); - size = buf_len + header_len + SCU_MAX_UNSOLICITED_FRAMES * sizeof(uf_control->address_table.array[0]); + dma_addr_t dma = ihost->ufi_dma; + void *virt = ihost->ufi_buf; + int i; /* * The Unsolicited Frame buffers are set at the start of the UF * memory descriptor entry. The headers and address table will be * placed after the buffers. */ - virt = dmam_alloc_coherent(&ihost->pdev->dev, size, &dma, GFP_KERNEL); - if (!virt) - return -ENOMEM; /* * Program the location of the UF header table into the SCU. @@ -93,8 +81,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) * headers, since we program the UF address table pointers to * NULL. */ - uf_control->headers.physical_address = dma + buf_len; - uf_control->headers.array = virt + buf_len; + uf_control->headers.physical_address = dma + SCI_UFI_BUF_SIZE; + uf_control->headers.array = virt + SCI_UFI_BUF_SIZE; /* * Program the location of the UF address table into the SCU. @@ -103,8 +91,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) * byte boundary already due to above programming headers being on a * 64-bit boundary and headers are on a 64-bytes in size. */ - uf_control->address_table.physical_address = dma + buf_len + header_len; - uf_control->address_table.array = virt + buf_len + header_len; + uf_control->address_table.physical_address = dma + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE; + uf_control->address_table.array = virt + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE; uf_control->get = 0; /* @@ -135,8 +123,6 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; } - - return 0; } enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control, diff --git a/drivers/scsi/isci/unsolicited_frame_control.h b/drivers/scsi/isci/unsolicited_frame_control.h index 75d896686f5..1bc551ec611 100644 --- a/drivers/scsi/isci/unsolicited_frame_control.h +++ b/drivers/scsi/isci/unsolicited_frame_control.h @@ -257,9 +257,13 @@ struct sci_unsolicited_frame_control { }; +#define SCI_UFI_BUF_SIZE (SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE) +#define SCI_UFI_HDR_SIZE (SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header)) +#define SCI_UFI_TOTAL_SIZE (SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE + SCU_MAX_UNSOLICITED_FRAMES * sizeof(u64)) + struct isci_host; -int sci_unsolicited_frame_control_construct(struct isci_host *ihost); +void sci_unsolicited_frame_control_construct(struct isci_host *ihost); enum sci_status sci_unsolicited_frame_control_get_header( struct sci_unsolicited_frame_control *uf_control, |