mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
isci update for 3.5
1/ Rework remote-node-context (RNC) handling for proper management of
the silicon state machine in error handling and hot-plug conditions.
Further details below, suffice to say if the RNC is mismanaged the
silicon state machines may lock up.
2/ Refactor the initialization code to be reused for suspend/resume support
3/ Miscellaneous bug fixes to address discovery issues and hardware
compatibility.
RNC rework details from Jeff Skirvin:
In the controller, devices as they appear on a SAS domain (or
direct-attached SATA devices) are represented by memory structures known
as "Remote Node Contexts" (RNCs). These structures are transferred from
main memory to the controller using a set of register commands; these
commands include setting up the context ("posting"), removing the
context ("invalidating"), and commands to control the scheduling of
commands and connections to that remote device ("suspensions" and
"resumptions"). There is a similar path to control RNC scheduling from
the protocol engine, which interprets the results of command and data
transmission and reception.
In general, the controller chooses among non-suspended RNCs to find one
that has work requiring scheduling the transmission of command and data
frames to a target. Likewise, when a target tries to return data back
to the initiator, the state of the RNC is used by the controller to
determine how to treat the incoming request. As an example, if the RNC
is in the state "TX/RX Suspended", incoming SSP connection requests from
the target will be rejected by the controller hardware. When an RNC is
"TX Suspended", it will not be selected by the controller hardware to
start outgoing command or data operations (with certain priority-based
exceptions).
As mentioned above, there are two sources for management of the RNC
states: commands from driver software, and the result of transmission
and reception conditions of commands and data signaled by the controller
hardware. As an example of the latter, if an outgoing SSP command ends
with a OPEN_REJECT(BAD_DESTINATION) status, the RNC state will
transition to the "TX Suspended" state, and this is signaled by the
controller hardware in the status to the completion of the pending
command as well as signaled in a controller hardware event. Examples of
the former are included in the patch changelogs.
Driver software is required to suspend the RNC in a "TX/RX Suspended"
condition before any outstanding commands can be terminated. Failure to
guarantee this can lead to a complete hardware hang condition. Earlier
versions of the driver software did not guarantee that an RNC was
correctly managed before I/O termination, and so operated in an unsafe
way.
Further, the driver performed unnecessary contortions to preserve the
remote device command state and so was more complicated than it needed
to be. A simplifying driver assumption is that once an I/O has entered
the error handler path without having completed in the target, the
requirement on the driver is that all use of the sas_task must end.
Beyond that, recovery of operation is dependent on libsas and other
components to reset, rediscover and reconfigure the device before normal
operation can restart. In the driver, this simplifying assumption meant
that the RNC management could be reduced to entry into the suspended
state, terminating the targeted I/O request, and resuming the RNC as
needed for device-specific management such as an SSP Abort Task or LUN
Reset Management request.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)
iQIcBAABAgAGBQJPtYFXAAoJEB7SkWpmfYgCJkcP/1VvsuuitNy/YM9P1tb/RvQ7
ytJzjGtWiAABHVWwjgB+Ng7hUTaP2r6l8KeNfwxpwXyNdBAUNysYEUBHAfPsKzKz
espTmw3wVCnREajgKXZwFp9aTj8DcYFB6vKcC/ddACt3uRNjjA9En36+6797r8Vg
YdebyjFX2FxwoUj0icTUiV/OXgb8w723imnCl8bfOhhFRi4eFZ4EJ23AdMkUya1i
uYePAPJPSQJuU/87gNIx4JcR0qHJ1ziGPEY+XC47CzEeXbBTSPgWOwanQ6KPoXRJ
XVxamfcKAjRdtwQ4m1vYBSE32RTdrjhujVbkGiPi6QaEbCLjhLSCIYyuS3XMckV+
TCZ16o5kd/I6ZtZOeP4zZRGnNBkOPzY44qiJeKffjWDhTrFacx4XWJB/ftWPgEA5
N2zFH3RM4sY0FUJ3I/Qe5CERNdCXMtcj+UAf3nHpAIVcv46Lp+qoSkdEx05uuuiN
+D/dSlubktuvuzmB5WisL3qrjNEkkLTAGQpZs1j0ojLEBm0XAgV5EzqmHiZ0GOPD
OQNFxeei9SlqgtKIIP0bymRispPrG2HVCOvExYMxzKR6fjxofZLAs/aWOsdhxgMq
TlAyZJ6OmGI+KX68HHzoMpT9iquvmP64WGkfHzCx296BfSKiruLh/Jzt5gGwv+Z1
5tlpnUr9dUTxx7qkQXvj
=HYvO
-----END PGP SIGNATURE-----
Merge tag 'isci-for-3.5' into misc
isci update for 3.5
1/ Rework remote-node-context (RNC) handling for proper management of
the silicon state machine in error handling and hot-plug conditions.
Further details below, suffice to say if the RNC is mismanaged the
silicon state machines may lock up.
2/ Refactor the initialization code to be reused for suspend/resume support
3/ Miscellaneous bug fixes to address discovery issues and hardware
compatibility.
RNC rework details from Jeff Skirvin:
In the controller, devices as they appear on a SAS domain (or
direct-attached SATA devices) are represented by memory structures known
as "Remote Node Contexts" (RNCs). These structures are transferred from
main memory to the controller using a set of register commands; these
commands include setting up the context ("posting"), removing the
context ("invalidating"), and commands to control the scheduling of
commands and connections to that remote device ("suspensions" and
"resumptions"). There is a similar path to control RNC scheduling from
the protocol engine, which interprets the results of command and data
transmission and reception.
In general, the controller chooses among non-suspended RNCs to find one
that has work requiring scheduling the transmission of command and data
frames to a target. Likewise, when a target tries to return data back
to the initiator, the state of the RNC is used by the controller to
determine how to treat the incoming request. As an example, if the RNC
is in the state "TX/RX Suspended", incoming SSP connection requests from
the target will be rejected by the controller hardware. When an RNC is
"TX Suspended", it will not be selected by the controller hardware to
start outgoing command or data operations (with certain priority-based
exceptions).
As mentioned above, there are two sources for management of the RNC
states: commands from driver software, and the result of transmission
and reception conditions of commands and data signaled by the controller
hardware. As an example of the latter, if an outgoing SSP command ends
with a OPEN_REJECT(BAD_DESTINATION) status, the RNC state will
transition to the "TX Suspended" state, and this is signaled by the
controller hardware in the status to the completion of the pending
command as well as signaled in a controller hardware event. Examples of
the former are included in the patch changelogs.
Driver software is required to suspend the RNC in a "TX/RX Suspended"
condition before any outstanding commands can be terminated. Failure to
guarantee this can lead to a complete hardware hang condition. Earlier
versions of the driver software did not guarantee that an RNC was
correctly managed before I/O termination, and so operated in an unsafe
way.
Further, the driver performed unnecessary contortions to preserve the
remote device command state and so was more complicated than it needed
to be. A simplifying driver assumption is that once an I/O has entered
the error handler path without having completed in the target, the
requirement on the driver is that all use of the sas_task must end.
Beyond that, recovery of operation is dependent on libsas and other
components to reset, rediscover and reconfigure the device before normal
operation can restart. In the driver, this simplifying assumption meant
that the RNC management could be reduced to entry into the suspended
state, terminating the targeted I/O request, and resuming the RNC as
needed for device-specific management such as an SSP Abort Task or LUN
Reset Management request.
This commit is contained in:
commit
e346933365
749 changed files with 8598 additions and 6602 deletions
|
|
@ -218,6 +218,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
|
|||
|
||||
if (!shost->shost_gendev.parent)
|
||||
shost->shost_gendev.parent = dev ? dev : &platform_bus;
|
||||
if (!dma_dev)
|
||||
dma_dev = shost->shost_gendev.parent;
|
||||
|
||||
shost->dma_dev = dma_dev;
|
||||
|
||||
error = device_add(&shost->shost_gendev);
|
||||
|
|
|
|||
|
|
@ -4549,8 +4549,12 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev)
|
|||
ENTER;
|
||||
if (sdev->sdev_target)
|
||||
sata_port = sdev->sdev_target->hostdata;
|
||||
if (sata_port)
|
||||
if (sata_port) {
|
||||
rc = ata_sas_port_init(sata_port->ap);
|
||||
if (rc == 0)
|
||||
rc = ata_sas_sync_probe(sata_port->ap);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
ipr_slave_destroy(sdev);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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];
|
||||
|
|
@ -273,13 +276,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.
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,6 +98,225 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote
|
|||
wake_up(&ihost->eventq);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!test_bit(IREQ_ACTIVE, &ireq->flags) ||
|
||||
(ireq->target_device != idev) ||
|
||||
(check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags)))
|
||||
return SCI_SUCCESS;
|
||||
|
||||
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_reqs_checkabort(
|
||||
struct isci_remote_device *idev,
|
||||
int chk)
|
||||
{
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
enum sci_status status = SCI_SUCCESS;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
|
||||
struct isci_request *ireq = ihost->reqs[i];
|
||||
enum sci_status s;
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
@ -144,26 +328,10 @@ static void rnc_destruct_done(void *_dev)
|
|||
sci_change_state(&idev->sm, SCI_DEV_STOPPED);
|
||||
}
|
||||
|
||||
static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev)
|
||||
enum sci_status sci_remote_device_terminate_requests(
|
||||
struct isci_remote_device *idev)
|
||||
{
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
enum sci_status status = SCI_SUCCESS;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) {
|
||||
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);
|
||||
if (s != SCI_SUCCESS)
|
||||
status = s;
|
||||
}
|
||||
|
||||
return status;
|
||||
return sci_remote_device_terminate_reqs_checkabort(idev, 0);
|
||||
}
|
||||
|
||||
enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_) */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED;
|
||||
dest_select = rnc->destination_state;
|
||||
rnc->destination_state = RNC_DEST_UNSPECIFIED;
|
||||
|
||||
if (rnc->user_callback)
|
||||
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 (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;
|
||||
|
||||
state = sci_rnc->sm.current_state_id;
|
||||
if (state != SCI_RNC_READY) {
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
sci_rnc->user_callback = cb_fn;
|
||||
sci_rnc->user_cookie = cb_p;
|
||||
sci_rnc->suspension_code = suspend_type;
|
||||
if (status == SCI_SUCCESS) { /* Already in the destination state? */
|
||||
struct isci_host *ihost = idev->owning_port->owning_controller;
|
||||
|
||||
if (suspend_type == SCI_SOFTWARE_SUSPENSION) {
|
||||
sci_remote_device_post_request(rnc_to_dev(sci_rnc),
|
||||
SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX);
|
||||
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)) {
|
||||
|
||||
if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)
|
||||
isci_dev_set_hang_detection_timeout(idev, 0x00000001);
|
||||
|
||||
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);
|
||||
}
|
||||
} else
|
||||
return SCI_FAILURE;
|
||||
return SCI_SUCCESS;
|
||||
}
|
||||
case SCI_RNC_TX_SUSPENDED:
|
||||
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;
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
return SCI_SUCCESS;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -60,30 +60,6 @@
|
|||
#include "host.h"
|
||||
#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
|
||||
|
|
@ -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_) */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
|
||||
task->lldd_task = NULL;
|
||||
task->task_done(task);
|
||||
break;
|
||||
task->task_status.resp = response;
|
||||
task->task_status.stat = status;
|
||||
|
||||
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;
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
dev_dbg(&ihost->pdev->dev,
|
||||
"%s: isci task notification default case!",
|
||||
__func__);
|
||||
sas_task_abort(task);
|
||||
break;
|
||||
}
|
||||
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,314 +362,20 @@ 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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -881,63 +475,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.
|
||||
|
|
@ -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));
|
||||
/* No task to send, so explicitly resume the device here */
|
||||
isci_remote_device_resume_from_abort(ihost, idev);
|
||||
|
||||
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);
|
||||
}
|
||||
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));
|
||||
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
/* 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];
|
||||
|
||||
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);
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -62,19 +62,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_) */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1742,17 +1742,19 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
|
|||
|
||||
mfs = ntohs(flp->fl_csp.sp_bb_data) &
|
||||
FC_SP_BB_DATA_MASK;
|
||||
if (mfs >= FC_SP_MIN_MAX_PAYLOAD &&
|
||||
mfs <= lport->mfs) {
|
||||
lport->mfs = mfs;
|
||||
fc_host_maxframe_size(lport->host) = mfs;
|
||||
} else {
|
||||
|
||||
if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) {
|
||||
FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, "
|
||||
"lport->mfs:%hu\n", mfs, lport->mfs);
|
||||
fc_lport_error(lport, fp);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (mfs <= lport->mfs) {
|
||||
lport->mfs = mfs;
|
||||
fc_host_maxframe_size(lport->host) = mfs;
|
||||
}
|
||||
|
||||
csp_flags = ntohs(flp->fl_csp.sp_features);
|
||||
r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov);
|
||||
e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
|
||||
|
|
|
|||
|
|
@ -546,11 +546,12 @@ static struct ata_port_info sata_port_info = {
|
|||
.port_ops = &sas_sata_ops
|
||||
};
|
||||
|
||||
int sas_ata_init_host_and_port(struct domain_device *found_dev)
|
||||
int sas_ata_init(struct domain_device *found_dev)
|
||||
{
|
||||
struct sas_ha_struct *ha = found_dev->port->ha;
|
||||
struct Scsi_Host *shost = ha->core.shost;
|
||||
struct ata_port *ap;
|
||||
int rc;
|
||||
|
||||
ata_host_init(&found_dev->sata_dev.ata_host,
|
||||
ha->dev,
|
||||
|
|
@ -567,8 +568,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev)
|
|||
ap->private_data = found_dev;
|
||||
ap->cbl = ATA_CBL_SATA;
|
||||
ap->scsi_host = shost;
|
||||
/* publish initialized ata port */
|
||||
smp_wmb();
|
||||
rc = ata_sas_port_init(ap);
|
||||
if (rc) {
|
||||
ata_sas_port_destroy(ap);
|
||||
return rc;
|
||||
}
|
||||
found_dev->sata_dev.ap = ap;
|
||||
|
||||
return 0;
|
||||
|
|
@ -648,18 +652,13 @@ static void sas_get_ata_command_set(struct domain_device *dev)
|
|||
void sas_probe_sata(struct asd_sas_port *port)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
int err;
|
||||
|
||||
mutex_lock(&port->ha->disco_mutex);
|
||||
list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
|
||||
list_for_each_entry(dev, &port->disco_list, disco_list_node) {
|
||||
if (!dev_is_sata(dev))
|
||||
continue;
|
||||
|
||||
err = sas_ata_init_host_and_port(dev);
|
||||
if (err)
|
||||
sas_fail_probe(dev, __func__, err);
|
||||
else
|
||||
ata_sas_async_port_init(dev->sata_dev.ap);
|
||||
ata_sas_async_probe(dev->sata_dev.ap);
|
||||
}
|
||||
mutex_unlock(&port->ha->disco_mutex);
|
||||
|
||||
|
|
@ -718,18 +717,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
|
|||
sas_put_device(dev);
|
||||
}
|
||||
|
||||
static bool sas_ata_dev_eh_valid(struct domain_device *dev)
|
||||
{
|
||||
struct ata_port *ap;
|
||||
|
||||
if (!dev_is_sata(dev))
|
||||
return false;
|
||||
ap = dev->sata_dev.ap;
|
||||
/* consume fully initialized ata ports */
|
||||
smp_rmb();
|
||||
return !!ap;
|
||||
}
|
||||
|
||||
void sas_ata_strategy_handler(struct Scsi_Host *shost)
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
|
||||
|
|
@ -753,7 +740,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
|
|||
|
||||
spin_lock(&port->dev_list_lock);
|
||||
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
|
||||
if (!sas_ata_dev_eh_valid(dev))
|
||||
if (!dev_is_sata(dev))
|
||||
continue;
|
||||
async_schedule_domain(async_sas_ata_eh, dev, &async);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|||
struct asd_sas_phy *phy;
|
||||
struct sas_rphy *rphy;
|
||||
struct domain_device *dev;
|
||||
int rc = -ENODEV;
|
||||
|
||||
dev = sas_alloc_device();
|
||||
if (!dev)
|
||||
|
|
@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|||
|
||||
sas_init_dev(dev);
|
||||
|
||||
dev->port = port;
|
||||
switch (dev->dev_type) {
|
||||
case SAS_END_DEV:
|
||||
case SATA_DEV:
|
||||
rc = sas_ata_init(dev);
|
||||
if (rc) {
|
||||
rphy = NULL;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case SAS_END_DEV:
|
||||
rphy = sas_end_device_alloc(port->port);
|
||||
break;
|
||||
case EDGE_DEV:
|
||||
|
|
@ -131,19 +139,14 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|||
|
||||
if (!rphy) {
|
||||
sas_put_device(dev);
|
||||
return -ENODEV;
|
||||
return rc;
|
||||
}
|
||||
|
||||
spin_lock_irq(&port->phy_list_lock);
|
||||
list_for_each_entry(phy, &port->phy_list, port_phy_el)
|
||||
sas_phy_set_target(phy, dev);
|
||||
spin_unlock_irq(&port->phy_list_lock);
|
||||
rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
|
||||
memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
|
||||
sas_fill_in_rphy(dev, rphy);
|
||||
sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
|
||||
port->port_dev = dev;
|
||||
dev->port = port;
|
||||
dev->linkrate = port->linkrate;
|
||||
dev->min_linkrate = port->linkrate;
|
||||
dev->max_linkrate = port->linkrate;
|
||||
|
|
@ -155,6 +158,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|||
sas_device_set_phy(dev, port->port);
|
||||
|
||||
dev->rphy = rphy;
|
||||
get_device(&dev->rphy->dev);
|
||||
|
||||
if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV)
|
||||
list_add_tail(&dev->disco_list_node, &port->disco_list);
|
||||
|
|
@ -164,6 +168,11 @@ static int sas_get_port_device(struct asd_sas_port *port)
|
|||
spin_unlock_irq(&port->dev_list_lock);
|
||||
}
|
||||
|
||||
spin_lock_irq(&port->phy_list_lock);
|
||||
list_for_each_entry(phy, &port->phy_list, port_phy_el)
|
||||
sas_phy_set_target(phy, dev);
|
||||
spin_unlock_irq(&port->phy_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -205,8 +214,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
|
|||
static void sas_probe_devices(struct work_struct *work)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
struct sas_discovery_event *ev =
|
||||
container_of(work, struct sas_discovery_event, work);
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
|
||||
clear_bit(DISCE_PROBE, &port->disc.pending);
|
||||
|
|
@ -255,6 +263,9 @@ void sas_free_device(struct kref *kref)
|
|||
{
|
||||
struct domain_device *dev = container_of(kref, typeof(*dev), kref);
|
||||
|
||||
put_device(&dev->rphy->dev);
|
||||
dev->rphy = NULL;
|
||||
|
||||
if (dev->parent)
|
||||
sas_put_device(dev->parent);
|
||||
|
||||
|
|
@ -291,8 +302,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
|
|||
static void sas_destruct_devices(struct work_struct *work)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
struct sas_discovery_event *ev =
|
||||
container_of(work, struct sas_discovery_event, work);
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
|
||||
clear_bit(DISCE_DESTRUCT, &port->disc.pending);
|
||||
|
|
@ -302,7 +312,6 @@ static void sas_destruct_devices(struct work_struct *work)
|
|||
|
||||
sas_remove_children(&dev->rphy->dev);
|
||||
sas_rphy_delete(dev->rphy);
|
||||
dev->rphy = NULL;
|
||||
sas_unregister_common_dev(port, dev);
|
||||
}
|
||||
}
|
||||
|
|
@ -314,11 +323,11 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
|
|||
/* this rphy never saw sas_rphy_add */
|
||||
list_del_init(&dev->disco_list_node);
|
||||
sas_rphy_free(dev->rphy);
|
||||
dev->rphy = NULL;
|
||||
sas_unregister_common_dev(port, dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
|
||||
if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
|
||||
sas_rphy_unlink(dev->rphy);
|
||||
list_move_tail(&dev->disco_list_node, &port->destroy_list);
|
||||
sas_discover_event(dev->port, DISCE_DESTRUCT);
|
||||
|
|
@ -377,8 +386,7 @@ static void sas_discover_domain(struct work_struct *work)
|
|||
{
|
||||
struct domain_device *dev;
|
||||
int error = 0;
|
||||
struct sas_discovery_event *ev =
|
||||
container_of(work, struct sas_discovery_event, work);
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
|
||||
clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
|
||||
|
|
@ -419,8 +427,6 @@ static void sas_discover_domain(struct work_struct *work)
|
|||
|
||||
if (error) {
|
||||
sas_rphy_free(dev->rphy);
|
||||
dev->rphy = NULL;
|
||||
|
||||
list_del_init(&dev->disco_list_node);
|
||||
spin_lock_irq(&port->dev_list_lock);
|
||||
list_del_init(&dev->dev_list_node);
|
||||
|
|
@ -437,8 +443,7 @@ static void sas_discover_domain(struct work_struct *work)
|
|||
static void sas_revalidate_domain(struct work_struct *work)
|
||||
{
|
||||
int res = 0;
|
||||
struct sas_discovery_event *ev =
|
||||
container_of(work, struct sas_discovery_event, work);
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
struct sas_ha_struct *ha = port->ha;
|
||||
|
||||
|
|
@ -466,21 +471,25 @@ static void sas_revalidate_domain(struct work_struct *work)
|
|||
|
||||
/* ---------- Events ---------- */
|
||||
|
||||
static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work)
|
||||
static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)
|
||||
{
|
||||
/* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */
|
||||
scsi_queue_work(ha->core.shost, work);
|
||||
/* chained work is not subject to SA_HA_DRAINING or
|
||||
* SAS_HA_REGISTERED, because it is either submitted in the
|
||||
* workqueue, or known to be submitted from a context that is
|
||||
* not racing against draining
|
||||
*/
|
||||
scsi_queue_work(ha->core.shost, &sw->work);
|
||||
}
|
||||
|
||||
static void sas_chain_event(int event, unsigned long *pending,
|
||||
struct work_struct *work,
|
||||
struct sas_work *sw,
|
||||
struct sas_ha_struct *ha)
|
||||
{
|
||||
if (!test_and_set_bit(event, pending)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ha->state_lock, flags);
|
||||
sas_chain_work(ha, work);
|
||||
sas_chain_work(ha, sw);
|
||||
spin_unlock_irqrestore(&ha->state_lock, flags);
|
||||
}
|
||||
}
|
||||
|
|
@ -519,7 +528,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
|
|||
|
||||
disc->pending = 0;
|
||||
for (i = 0; i < DISC_NUM_EVENTS; i++) {
|
||||
INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
|
||||
INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
|
||||
disc->disc_work[i].port = port;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,19 +27,21 @@
|
|||
#include "sas_internal.h"
|
||||
#include "sas_dump.h"
|
||||
|
||||
void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work)
|
||||
void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
|
||||
{
|
||||
if (!test_bit(SAS_HA_REGISTERED, &ha->state))
|
||||
return;
|
||||
|
||||
if (test_bit(SAS_HA_DRAINING, &ha->state))
|
||||
list_add(&work->entry, &ha->defer_q);
|
||||
else
|
||||
scsi_queue_work(ha->core.shost, work);
|
||||
if (test_bit(SAS_HA_DRAINING, &ha->state)) {
|
||||
/* add it to the defer list, if not already pending */
|
||||
if (list_empty(&sw->drain_node))
|
||||
list_add(&sw->drain_node, &ha->defer_q);
|
||||
} else
|
||||
scsi_queue_work(ha->core.shost, &sw->work);
|
||||
}
|
||||
|
||||
static void sas_queue_event(int event, unsigned long *pending,
|
||||
struct work_struct *work,
|
||||
struct sas_work *work,
|
||||
struct sas_ha_struct *ha)
|
||||
{
|
||||
if (!test_and_set_bit(event, pending)) {
|
||||
|
|
@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending,
|
|||
void __sas_drain_work(struct sas_ha_struct *ha)
|
||||
{
|
||||
struct workqueue_struct *wq = ha->core.shost->work_q;
|
||||
struct work_struct *w, *_w;
|
||||
struct sas_work *sw, *_sw;
|
||||
|
||||
set_bit(SAS_HA_DRAINING, &ha->state);
|
||||
/* flush submitters */
|
||||
|
|
@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha)
|
|||
|
||||
spin_lock_irq(&ha->state_lock);
|
||||
clear_bit(SAS_HA_DRAINING, &ha->state);
|
||||
list_for_each_entry_safe(w, _w, &ha->defer_q, entry) {
|
||||
list_del_init(&w->entry);
|
||||
sas_queue_work(ha, w);
|
||||
list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
|
||||
list_del_init(&sw->drain_node);
|
||||
sas_queue_work(ha, sw);
|
||||
}
|
||||
spin_unlock_irq(&ha->state_lock);
|
||||
}
|
||||
|
|
@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < HA_NUM_EVENTS; i++) {
|
||||
INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
|
||||
INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
|
||||
sas_ha->ha_events[i].ha = sas_ha;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
u8 sas_addr[SAS_ADDR_SIZE];
|
||||
struct smp_resp *resp = rsp;
|
||||
struct discover_resp *dr = &resp->disc;
|
||||
struct sas_ha_struct *ha = dev->port->ha;
|
||||
struct expander_device *ex = &dev->ex_dev;
|
||||
struct ex_phy *phy = &ex->ex_phy[phy_id];
|
||||
struct sas_rphy *rphy = dev->rphy;
|
||||
|
|
@ -209,6 +210,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
char *type;
|
||||
|
||||
if (new_phy) {
|
||||
if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)))
|
||||
return;
|
||||
phy->phy = sas_phy_alloc(&rphy->dev, phy_id);
|
||||
|
||||
/* FIXME: error_handling */
|
||||
|
|
@ -233,6 +236,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
|
||||
|
||||
phy->attached_dev_type = to_dev_type(dr);
|
||||
if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))
|
||||
goto out;
|
||||
phy->phy_id = phy_id;
|
||||
phy->linkrate = dr->linkrate;
|
||||
phy->attached_sata_host = dr->attached_sata_host;
|
||||
|
|
@ -240,7 +245,14 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
phy->attached_sata_ps = dr->attached_sata_ps;
|
||||
phy->attached_iproto = dr->iproto << 1;
|
||||
phy->attached_tproto = dr->tproto << 1;
|
||||
memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);
|
||||
/* help some expanders that fail to zero sas_address in the 'no
|
||||
* device' case
|
||||
*/
|
||||
if (phy->attached_dev_type == NO_DEVICE ||
|
||||
phy->linkrate < SAS_LINK_RATE_1_5_GBPS)
|
||||
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
|
||||
else
|
||||
memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE);
|
||||
phy->attached_phy_id = dr->attached_phy_id;
|
||||
phy->phy_change_count = dr->change_count;
|
||||
phy->routing_attr = dr->routing_attr;
|
||||
|
|
@ -266,6 +278,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
switch (phy->attached_dev_type) {
|
||||
case SATA_PENDING:
|
||||
type = "stp pending";
|
||||
|
|
@ -304,7 +317,15 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
|||
else
|
||||
return;
|
||||
|
||||
SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n",
|
||||
/* if the attached device type changed and ata_eh is active,
|
||||
* make sure we run revalidation when eh completes (see:
|
||||
* sas_enable_revalidation)
|
||||
*/
|
||||
if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))
|
||||
set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending);
|
||||
|
||||
SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n",
|
||||
test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "",
|
||||
SAS_ADDR(dev->sas_addr), phy->phy_id,
|
||||
sas_route_char(dev, phy), phy->linkrate,
|
||||
SAS_ADDR(phy->attached_sas_addr), type);
|
||||
|
|
@ -776,13 +797,16 @@ static struct domain_device *sas_ex_discover_end_dev(
|
|||
if (res)
|
||||
goto out_free;
|
||||
|
||||
sas_init_dev(child);
|
||||
res = sas_ata_init(child);
|
||||
if (res)
|
||||
goto out_free;
|
||||
rphy = sas_end_device_alloc(phy->port);
|
||||
if (unlikely(!rphy))
|
||||
if (!rphy)
|
||||
goto out_free;
|
||||
|
||||
sas_init_dev(child);
|
||||
|
||||
child->rphy = rphy;
|
||||
get_device(&rphy->dev);
|
||||
|
||||
list_add_tail(&child->disco_list_node, &parent->port->disco_list);
|
||||
|
||||
|
|
@ -806,6 +830,7 @@ static struct domain_device *sas_ex_discover_end_dev(
|
|||
sas_init_dev(child);
|
||||
|
||||
child->rphy = rphy;
|
||||
get_device(&rphy->dev);
|
||||
sas_fill_in_rphy(child, rphy);
|
||||
|
||||
list_add_tail(&child->disco_list_node, &parent->port->disco_list);
|
||||
|
|
@ -830,8 +855,6 @@ static struct domain_device *sas_ex_discover_end_dev(
|
|||
|
||||
out_list_del:
|
||||
sas_rphy_free(child->rphy);
|
||||
child->rphy = NULL;
|
||||
|
||||
list_del(&child->disco_list_node);
|
||||
spin_lock_irq(&parent->port->dev_list_lock);
|
||||
list_del(&child->dev_list_node);
|
||||
|
|
@ -911,6 +934,7 @@ static struct domain_device *sas_ex_discover_expander(
|
|||
}
|
||||
port = parent->port;
|
||||
child->rphy = rphy;
|
||||
get_device(&rphy->dev);
|
||||
edev = rphy_to_expander_device(rphy);
|
||||
child->dev_type = phy->attached_dev_type;
|
||||
kref_get(&parent->kref);
|
||||
|
|
@ -934,6 +958,7 @@ static struct domain_device *sas_ex_discover_expander(
|
|||
|
||||
res = sas_discover_expander(child);
|
||||
if (res) {
|
||||
sas_rphy_delete(rphy);
|
||||
spin_lock_irq(&parent->port->dev_list_lock);
|
||||
list_del(&child->dev_list_node);
|
||||
spin_unlock_irq(&parent->port->dev_list_lock);
|
||||
|
|
@ -1718,9 +1743,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
|
|||
int phy_change_count = 0;
|
||||
|
||||
res = sas_get_phy_change_count(dev, i, &phy_change_count);
|
||||
if (res)
|
||||
goto out;
|
||||
else if (phy_change_count != ex->ex_phy[i].phy_change_count) {
|
||||
switch (res) {
|
||||
case SMP_RESP_PHY_VACANT:
|
||||
case SMP_RESP_NO_PHY:
|
||||
continue;
|
||||
case SMP_RESP_FUNC_ACC:
|
||||
break;
|
||||
default:
|
||||
return res;
|
||||
}
|
||||
|
||||
if (phy_change_count != ex->ex_phy[i].phy_change_count) {
|
||||
if (update)
|
||||
ex->ex_phy[i].phy_change_count =
|
||||
phy_change_count;
|
||||
|
|
@ -1728,8 +1761,7 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sas_get_ex_change_count(struct domain_device *dev, int *ecc)
|
||||
|
|
|
|||
|
|
@ -94,8 +94,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
|
|||
|
||||
void sas_hae_reset(struct work_struct *work)
|
||||
{
|
||||
struct sas_ha_event *ev =
|
||||
container_of(work, struct sas_ha_event, work);
|
||||
struct sas_ha_event *ev = to_sas_ha_event(work);
|
||||
struct sas_ha_struct *ha = ev->ha;
|
||||
|
||||
clear_bit(HAE_RESET, &ha->pending);
|
||||
|
|
@ -369,14 +368,14 @@ static void sas_phy_release(struct sas_phy *phy)
|
|||
|
||||
static void phy_reset_work(struct work_struct *work)
|
||||
{
|
||||
struct sas_phy_data *d = container_of(work, typeof(*d), reset_work);
|
||||
struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work);
|
||||
|
||||
d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
|
||||
}
|
||||
|
||||
static void phy_enable_work(struct work_struct *work)
|
||||
{
|
||||
struct sas_phy_data *d = container_of(work, typeof(*d), enable_work);
|
||||
struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work);
|
||||
|
||||
d->enable_result = sas_phy_enable(d->phy, d->enable);
|
||||
}
|
||||
|
|
@ -389,8 +388,8 @@ static int sas_phy_setup(struct sas_phy *phy)
|
|||
return -ENOMEM;
|
||||
|
||||
mutex_init(&d->event_lock);
|
||||
INIT_WORK(&d->reset_work, phy_reset_work);
|
||||
INIT_WORK(&d->enable_work, phy_enable_work);
|
||||
INIT_SAS_WORK(&d->reset_work, phy_reset_work);
|
||||
INIT_SAS_WORK(&d->enable_work, phy_enable_work);
|
||||
d->phy = phy;
|
||||
phy->hostdata = d;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ struct sas_phy_data {
|
|||
struct mutex event_lock;
|
||||
int hard_reset;
|
||||
int reset_result;
|
||||
struct work_struct reset_work;
|
||||
struct sas_work reset_work;
|
||||
int enable;
|
||||
int enable_result;
|
||||
struct work_struct enable_work;
|
||||
struct sas_work enable_work;
|
||||
};
|
||||
|
||||
void sas_scsi_recover_host(struct Scsi_Host *shost);
|
||||
|
|
@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work);
|
|||
void sas_porte_link_reset_err(struct work_struct *work);
|
||||
void sas_porte_timer_event(struct work_struct *work);
|
||||
void sas_porte_hard_reset(struct work_struct *work);
|
||||
void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work);
|
||||
void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw);
|
||||
|
||||
int sas_notify_lldd_dev_found(struct domain_device *);
|
||||
void sas_notify_lldd_dev_gone(struct domain_device *);
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@
|
|||
|
||||
static void sas_phye_loss_of_signal(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);
|
||||
|
|
@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work)
|
|||
|
||||
static void sas_phye_oob_done(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);
|
||||
|
|
@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work)
|
|||
|
||||
static void sas_phye_oob_error(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct asd_sas_port *port = phy->port;
|
||||
|
|
@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work)
|
|||
|
||||
static void sas_phye_spinup_hold(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct sas_internal *i =
|
||||
|
|
@ -127,14 +123,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
|
|||
phy->error = 0;
|
||||
INIT_LIST_HEAD(&phy->port_phy_el);
|
||||
for (k = 0; k < PORT_NUM_EVENTS; k++) {
|
||||
INIT_WORK(&phy->port_events[k].work,
|
||||
sas_port_event_fns[k]);
|
||||
INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]);
|
||||
phy->port_events[k].phy = phy;
|
||||
}
|
||||
|
||||
for (k = 0; k < PHY_NUM_EVENTS; k++) {
|
||||
INIT_WORK(&phy->phy_events[k].work,
|
||||
sas_phy_event_fns[k]);
|
||||
INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]);
|
||||
phy->phy_events[k].phy = phy;
|
||||
}
|
||||
|
||||
|
|
@ -144,8 +138,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
|
|||
spin_lock_init(&phy->sas_prim_lock);
|
||||
phy->frame_rcvd_size = 0;
|
||||
|
||||
phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev,
|
||||
i);
|
||||
phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
|
||||
if (!phy->phy)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
|
|||
spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
|
||||
|
||||
if (!port->port) {
|
||||
port->port = sas_port_alloc(phy->phy->dev.parent, phy->id);
|
||||
port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
|
||||
BUG_ON(!port->port);
|
||||
sas_port_add(port->port);
|
||||
}
|
||||
|
|
@ -208,8 +208,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
|
|||
|
||||
void sas_porte_bytes_dmaed(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);
|
||||
|
|
@ -219,8 +218,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work)
|
|||
|
||||
void sas_porte_broadcast_rcvd(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
unsigned long flags;
|
||||
u32 prim;
|
||||
|
|
@ -237,8 +235,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work)
|
|||
|
||||
void sas_porte_link_reset_err(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending);
|
||||
|
|
@ -248,8 +245,7 @@ void sas_porte_link_reset_err(struct work_struct *work)
|
|||
|
||||
void sas_porte_timer_event(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending);
|
||||
|
|
@ -259,8 +255,7 @@ void sas_porte_timer_event(struct work_struct *work)
|
|||
|
||||
void sas_porte_hard_reset(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev =
|
||||
container_of(work, struct asd_sas_event, work);
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_HARD_RESET, &phy->port_events_pending);
|
||||
|
|
|
|||
|
|
@ -1367,6 +1367,9 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job)
|
|||
struct qla_hw_data *ha = vha->hw;
|
||||
int rval = 0;
|
||||
|
||||
if (ha->flags.isp82xx_reset_hdlr_active)
|
||||
return -EBUSY;
|
||||
|
||||
rval = qla2x00_optrom_setup(bsg_job, vha, 0);
|
||||
if (rval)
|
||||
return rval;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* | Mailbox commands | 0x113e | 0x112c-0x112e |
|
||||
* | | | 0x113a |
|
||||
* | Device Discovery | 0x2086 | 0x2020-0x2022 |
|
||||
* | Queue Command and IO tracing | 0x302f | 0x3006,0x3008 |
|
||||
* | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 |
|
||||
* | | | 0x302d-0x302e |
|
||||
* | DPC Thread | 0x401c | |
|
||||
* | Async Events | 0x505d | 0x502b-0x502f |
|
||||
|
|
|
|||
|
|
@ -1715,13 +1715,24 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
|
|||
res = DID_ERROR << 16;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
} else if (lscsi_status != SAM_STAT_TASK_SET_FULL &&
|
||||
lscsi_status != SAM_STAT_BUSY) {
|
||||
/*
|
||||
* scsi status of task set and busy are considered to be
|
||||
* task not completed.
|
||||
*/
|
||||
|
||||
ql_dbg(ql_dbg_io, fcport->vha, 0x301f,
|
||||
"Dropped frame(s) detected (0x%x "
|
||||
"of 0x%x bytes).\n", resid, scsi_bufflen(cp));
|
||||
"of 0x%x bytes).\n", resid,
|
||||
scsi_bufflen(cp));
|
||||
|
||||
res = DID_ERROR << 16 | lscsi_status;
|
||||
goto check_scsi_status;
|
||||
} else {
|
||||
ql_dbg(ql_dbg_io, fcport->vha, 0x3030,
|
||||
"scsi_status: 0x%x, lscsi_status: 0x%x\n",
|
||||
scsi_status, lscsi_status);
|
||||
}
|
||||
|
||||
res = DID_OK << 16 | lscsi_status;
|
||||
|
|
|
|||
|
|
@ -3125,6 +3125,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha)
|
|||
ql_log(ql_log_info, vha, 0x00b7,
|
||||
"HW State: COLD/RE-INIT.\n");
|
||||
qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
|
||||
qla82xx_set_rst_ready(ha);
|
||||
if (ql2xmdenable) {
|
||||
if (qla82xx_md_collect(vha))
|
||||
ql_log(ql_log_warn, vha, 0xb02c,
|
||||
|
|
|
|||
|
|
@ -3577,9 +3577,25 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
|
|||
continue;
|
||||
/* Attempt a retry. */
|
||||
status = 1;
|
||||
} else
|
||||
} else {
|
||||
status = qla2x00_fabric_login(vha,
|
||||
fcport, &next_loopid);
|
||||
if (status == QLA_SUCCESS) {
|
||||
int status2;
|
||||
uint8_t opts;
|
||||
|
||||
opts = 0;
|
||||
if (fcport->flags &
|
||||
FCF_FCP2_DEVICE)
|
||||
opts |= BIT_1;
|
||||
status2 =
|
||||
qla2x00_get_port_database(
|
||||
vha, fcport,
|
||||
opts);
|
||||
if (status2 != QLA_SUCCESS)
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
} else
|
||||
status = qla2x00_local_device_login(vha,
|
||||
fcport);
|
||||
|
|
|
|||
|
|
@ -1017,6 +1017,9 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
|
|||
!IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
|
||||
return;
|
||||
|
||||
if (ha->flags.isp82xx_reset_hdlr_active)
|
||||
return;
|
||||
|
||||
ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr,
|
||||
ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header));
|
||||
if (hdr.version == __constant_cpu_to_le16(0xffff))
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
/*
|
||||
* Driver version
|
||||
*/
|
||||
#define QLA2XXX_VERSION "8.03.07.13-k"
|
||||
#define QLA2XXX_VERSION "8.04.00.03-k"
|
||||
|
||||
#define QLA_DRIVER_MAJOR_VER 8
|
||||
#define QLA_DRIVER_MINOR_VER 3
|
||||
#define QLA_DRIVER_PATCH_VER 7
|
||||
#define QLA_DRIVER_MINOR_VER 4
|
||||
#define QLA_DRIVER_PATCH_VER 0
|
||||
#define QLA_DRIVER_BETA_VER 3
|
||||
|
|
|
|||
|
|
@ -1638,7 +1638,7 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost,
|
|||
request_fn_proc *request_fn)
|
||||
{
|
||||
struct request_queue *q;
|
||||
struct device *dev = shost->shost_gendev.parent;
|
||||
struct device *dev = shost->dma_dev;
|
||||
|
||||
q = blk_init_queue(request_fn, NULL);
|
||||
if (!q)
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@ static void virtscsi_complete_free(void *buf)
|
|||
|
||||
if (cmd->comp)
|
||||
complete_all(cmd->comp);
|
||||
mempool_free(cmd, virtscsi_cmd_pool);
|
||||
else
|
||||
mempool_free(cmd, virtscsi_cmd_pool);
|
||||
}
|
||||
|
||||
static void virtscsi_ctrl_done(struct virtqueue *vq)
|
||||
|
|
@ -311,21 +312,22 @@ out:
|
|||
static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(comp);
|
||||
int ret;
|
||||
int ret = FAILED;
|
||||
|
||||
cmd->comp = ∁
|
||||
ret = virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
|
||||
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
GFP_NOIO);
|
||||
if (ret < 0)
|
||||
return FAILED;
|
||||
if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
|
||||
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||
GFP_NOIO) < 0)
|
||||
goto out;
|
||||
|
||||
wait_for_completion(&comp);
|
||||
if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK &&
|
||||
cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
|
||||
return FAILED;
|
||||
if (cmd->resp.tmf.response == VIRTIO_SCSI_S_OK ||
|
||||
cmd->resp.tmf.response == VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
|
||||
ret = SUCCESS;
|
||||
|
||||
return SUCCESS;
|
||||
out:
|
||||
mempool_free(cmd, virtscsi_cmd_pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int virtscsi_device_reset(struct scsi_cmnd *sc)
|
||||
|
|
|
|||
Loading…
Reference in a new issue