aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/isci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r--drivers/scsi/isci/host.c703
-rw-r--r--drivers/scsi/isci/host.h124
-rw-r--r--drivers/scsi/isci/init.c212
-rw-r--r--drivers/scsi/isci/phy.c76
-rw-r--r--drivers/scsi/isci/phy.h9
-rw-r--r--drivers/scsi/isci/port.c68
-rw-r--r--drivers/scsi/isci/port.h11
-rw-r--r--drivers/scsi/isci/port_config.c18
-rw-r--r--drivers/scsi/isci/probe_roms.c12
-rw-r--r--drivers/scsi/isci/probe_roms.h2
-rw-r--r--drivers/scsi/isci/registers.h8
-rw-r--r--drivers/scsi/isci/remote_device.c576
-rw-r--r--drivers/scsi/isci/remote_device.h63
-rw-r--r--drivers/scsi/isci/remote_node_context.c393
-rw-r--r--drivers/scsi/isci/remote_node_context.h43
-rw-r--r--drivers/scsi/isci/request.c715
-rw-r--r--drivers/scsi/isci/request.h125
-rw-r--r--drivers/scsi/isci/scu_completion_codes.h2
-rw-r--r--drivers/scsi/isci/task.c800
-rw-r--r--drivers/scsi/isci/task.h132
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.c30
-rw-r--r--drivers/scsi/isci/unsolicited_frame_control.h6
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,