Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb608c3c authored by Dan Williams's avatar Dan Williams
Browse files

isci: fix controller stop



1/ notify waiters when controller stop completes (fixes 10 second stall
   unloading the driver)
2/ make sure phy stop is after port and device stop

Cc: Richard Boyd <richard.g.boyd@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent abec912d
Loading
Loading
Loading
Loading
+54 −45
Original line number Original line Diff line number Diff line
@@ -1046,7 +1046,7 @@ void isci_host_scan_start(struct Scsi_Host *shost)
	spin_unlock_irq(&ihost->scic_lock);
	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)
{
{
	sci_controller_disable_interrupts(ihost);
	sci_controller_disable_interrupts(ihost);
	clear_bit(IHOST_STOP_PENDING, &ihost->flags);
	clear_bit(IHOST_STOP_PENDING, &ihost->flags);
@@ -1232,7 +1232,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
	switch (ihost->sm.current_state_id) {
	switch (ihost->sm.current_state_id) {
	case SCIC_RESET:
	case SCIC_RESET:
	case SCIC_READY:
	case SCIC_READY:
	case SCIC_STOPPED:
	case SCIC_STOPPING:
	case SCIC_FAILED:
	case SCIC_FAILED:
		/*
		/*
		 * The reset operation is not a graceful cleanup, just
		 * The reset operation is not a graceful cleanup, just
@@ -1247,6 +1247,44 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost)
	}
	}
}
}


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)
void isci_host_deinit(struct isci_host *ihost)
{
{
	int i;
	int i;
@@ -1255,16 +1293,6 @@ void isci_host_deinit(struct isci_host *ihost)
	for (i = 0; i < isci_gpio_count(ihost); i++)
	for (i = 0; i < isci_gpio_count(ihost); i++)
		writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);
		writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]);


	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);
	set_bit(IHOST_STOP_PENDING, &ihost->flags);


	spin_lock_irq(&ihost->scic_lock);
	spin_lock_irq(&ihost->scic_lock);
@@ -1273,6 +1301,13 @@ void isci_host_deinit(struct isci_host *ihost)


	wait_for_stop(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
	/* disable sgpio: where the above wait should give time for the
	 * enclosure to sample the gpios going inactive
	 * enclosure to sample the gpios going inactive
	 */
	 */
@@ -1476,32 +1511,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm)
	sci_controller_set_interrupt_coalescence(ihost, 0, 0);
	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)
static enum sci_status sci_controller_stop_ports(struct isci_host *ihost)
{
{
	u32 index;
	u32 index;
@@ -1561,10 +1570,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s
{
{
	struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
	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_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)
static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm)
@@ -1621,7 +1631,6 @@ static const struct sci_base_state sci_controller_state_table[] = {
		.enter_state = sci_controller_stopping_state_enter,
		.enter_state = sci_controller_stopping_state_enter,
		.exit_state = sci_controller_stopping_state_exit,
		.exit_state = sci_controller_stopping_state_exit,
	},
	},
	[SCIC_STOPPED] = {},
	[SCIC_FAILED] = {}
	[SCIC_FAILED] = {}
};
};


@@ -1641,7 +1650,7 @@ static void controller_timeout(unsigned long data)
		sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
		sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT);
	else if (sm->current_state_id == SCIC_STOPPING) {
	else if (sm->current_state_id == SCIC_STOPPING) {
		sci_change_state(sm, SCIC_FAILED);
		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? */
	} else	/* / @todo Now what do we want to do in this case? */
		dev_err(&ihost->pdev->dev,
		dev_err(&ihost->pdev->dev,
			"%s: Controller timer fired when controller was not "
			"%s: Controller timer fired when controller was not "
@@ -2452,7 +2461,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;
	u32 index;


@@ -2478,7 +2487,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost,
	}
	}


	if (!sci_controller_has_remote_devices_stopping(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)
void sci_controller_post_request(struct isci_host *ihost, u32 request)
+1 −7
Original line number Original line Diff line number Diff line
@@ -276,13 +276,6 @@ enum sci_controller_states {
	 */
	 */
	SCIC_STOPPING,
	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
	 * This state indicates that the controller could not successfully be
	 * initialized.  In this state no new IO operations are permitted.
	 * initialized.  In this state no new IO operations are permitted.
@@ -479,6 +472,7 @@ int isci_host_init(struct isci_host *);
void isci_host_completion_routine(unsigned long data);
void isci_host_completion_routine(unsigned long data);
void isci_host_deinit(struct isci_host *);
void isci_host_deinit(struct isci_host *);
void sci_controller_disable_interrupts(struct isci_host *ihost);
void sci_controller_disable_interrupts(struct isci_host *ihost);
bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost);


enum sci_status sci_controller_start_io(
enum sci_status sci_controller_start_io(
	struct isci_host *ihost,
	struct isci_host *ihost,