Skip to content

Commit

Permalink
isci: filter broadcast change notifications during SMP phy resets
Browse files Browse the repository at this point in the history
When resetting a sata device in the domain we have seen occasions where
libsas prematurely marks a device gone in the time it takes for the
device to re-establish the link.  This plays badly with software raid
arrays.  Other libsas drivers have non-uniform delays in their reset
handlers to try to cover this condition, but not sufficient to close the
hole.  Given that a sata device can take many seconds to recover we
filter bcns and poll for the device reattach state before notifying
libsas that the port needs the domain to be rediscovered.  Once this has
been proven out at the lldd level we can think about uplevelling this
feature to a common implementation in libsas.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
[ use kzalloc instead of kmem_cache ]
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
[ use eventq and time macros ]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
  • Loading branch information
Jeff Skirvin authored and Dan Williams committed Jul 3, 2011
1 parent ff717ab commit 61aaff4
Show file tree
Hide file tree
Showing 3 changed files with 338 additions and 41 deletions.
115 changes: 75 additions & 40 deletions drivers/scsi/isci/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,71 @@ static enum sci_status scic_port_get_properties(struct scic_sds_port *port,
return SCI_SUCCESS;
}

static void scic_port_bcn_enable(struct scic_sds_port *sci_port)
{
struct scic_sds_phy *sci_phy;
u32 val;
int i;

for (i = 0; i < ARRAY_SIZE(sci_port->phy_table); i++) {
sci_phy = sci_port->phy_table[i];
if (!sci_phy)
continue;
val = readl(&sci_phy->link_layer_registers->link_layer_control);
/* clear the bit by writing 1. */
writel(val, &sci_phy->link_layer_registers->link_layer_control);
}
}

/* called under scic_lock to stabilize phy:port associations */
void isci_port_bcn_enable(struct isci_host *ihost, struct isci_port *iport)
{
int i;

clear_bit(IPORT_BCN_BLOCKED, &iport->flags);
wake_up(&ihost->eventq);

if (!test_and_clear_bit(IPORT_BCN_PENDING, &iport->flags))
return;

for (i = 0; i < ARRAY_SIZE(iport->sci.phy_table); i++) {
struct scic_sds_phy *sci_phy = iport->sci.phy_table[i];
struct isci_phy *iphy = sci_phy_to_iphy(sci_phy);

if (!sci_phy)
continue;

ihost->sas_ha.notify_port_event(&iphy->sas_phy,
PORTE_BROADCAST_RCVD);
break;
}
}

void isci_port_bc_change_received(struct isci_host *ihost,
struct scic_sds_port *sci_port,
struct scic_sds_phy *sci_phy)
{
struct isci_phy *iphy = sci_phy_to_iphy(sci_phy);
struct isci_port *iport = iphy->isci_port;

if (iport && test_bit(IPORT_BCN_BLOCKED, &iport->flags)) {
dev_dbg(&ihost->pdev->dev,
"%s: disabled BCN; isci_phy = %p, sas_phy = %p\n",
__func__, iphy, &iphy->sas_phy);
set_bit(IPORT_BCN_PENDING, &iport->flags);
atomic_inc(&iport->event);
wake_up(&ihost->eventq);
} else {
dev_dbg(&ihost->pdev->dev,
"%s: isci_phy = %p, sas_phy = %p\n",
__func__, iphy, &iphy->sas_phy);

ihost->sas_ha.notify_port_event(&iphy->sas_phy,
PORTE_BROADCAST_RCVD);
}
scic_port_bcn_enable(sci_port);
}

static void isci_port_link_up(struct isci_host *isci_host,
struct scic_sds_port *port,
struct scic_sds_phy *phy)
Expand Down Expand Up @@ -240,13 +305,15 @@ static void isci_port_link_down(struct isci_host *isci_host,
if (isci_port) {

/* check to see if this is the last phy on this port. */
if (isci_phy->sas_phy.port
&& isci_phy->sas_phy.port->num_phys == 1) {

/* 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
if (isci_phy->sas_phy.port &&
isci_phy->sas_phy.port->num_phys == 1) {
atomic_inc(&isci_port->event);
isci_port_bcn_enable(isci_host, isci_port);

/* change the state for all devices on this port. The
* next task sent to this device will be returned as
* SAS_TASK_UNDELIVERED, and the scsi mid layer will
* remove the target
*/
list_for_each_entry(isci_device,
&isci_port->remote_dev_list,
Expand Down Expand Up @@ -1033,26 +1100,6 @@ enum sas_linkrate scic_sds_port_get_max_allowed_speed(
return max_allowed_speed;
}

static void scic_port_enable_broadcast_change_notification(struct scic_sds_port *port)
{
struct scic_sds_phy *phy;
u32 register_value;
u8 index;

/* Loop through all of the phys to enable BCN. */
for (index = 0; index < SCI_MAX_PHYS; index++) {
phy = port->phy_table[index];
if (phy != NULL) {
register_value =
readl(&phy->link_layer_registers->link_layer_control);

/* clear the bit by writing 1. */
writel(register_value,
&phy->link_layer_registers->link_layer_control);
}
}
}

/**
*
* @sci_port: This is the struct scic_sds_port object to suspend.
Expand Down Expand Up @@ -1838,6 +1885,7 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index)
init_completion(&iport->start_complete);
iport->isci_host = ihost;
isci_port_change_state(iport, isci_freed);
atomic_set(&iport->event, 0);
}

/**
Expand All @@ -1852,19 +1900,6 @@ enum isci_status isci_port_get_state(
return isci_port->status;
}

static void isci_port_bc_change_received(struct isci_host *ihost,
struct scic_sds_port *sci_port,
struct scic_sds_phy *sci_phy)
{
struct isci_phy *iphy = sci_phy_to_iphy(sci_phy);

dev_dbg(&ihost->pdev->dev, "%s: iphy = %p, sas_phy = %p\n",
__func__, iphy, &iphy->sas_phy);

ihost->sas_ha.notify_port_event(&iphy->sas_phy, PORTE_BROADCAST_RCVD);
scic_port_enable_broadcast_change_notification(sci_port);
}

void scic_sds_port_broadcast_change_received(
struct scic_sds_port *sci_port,
struct scic_sds_phy *sci_phy)
Expand Down
5 changes: 5 additions & 0 deletions drivers/scsi/isci/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ struct scic_sds_port {
*/
struct isci_port {
enum isci_status status;
#define IPORT_BCN_BLOCKED 0
#define IPORT_BCN_PENDING 1
unsigned long flags;
atomic_t event;
struct isci_host *isci_host;
struct asd_sas_port sas_port;
struct list_head remote_dev_list;
Expand Down Expand Up @@ -334,6 +338,7 @@ void scic_sds_port_setup_transports(
struct scic_sds_port *sci_port,
u32 device_id);

void isci_port_bcn_enable(struct isci_host *, struct isci_port *);

void scic_sds_port_deactivate_phy(
struct scic_sds_port *sci_port,
Expand Down
Loading

0 comments on commit 61aaff4

Please sign in to comment.