Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 328751
b: refs/heads/master
c: 303694e
h: refs/heads/master
i:
  328749: 80724fa
  328747: 80e5232
  328743: be9d890
  328735: aa8da16
v: v3
  • Loading branch information
Dan Williams authored and James Bottomley committed Aug 24, 2012
1 parent 3a4cdbb commit c13ae69
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 20 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2fcbdcb4c802fe40d6827dbc365dac90cfe8c0a3
refs/heads/master: 303694eeee5eacad5b84105a15afd9e351e1891b
86 changes: 86 additions & 0 deletions trunk/drivers/scsi/libsas/sas_ata.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,92 @@ void sas_probe_sata(struct asd_sas_port *port)
if (ata_dev_disabled(sas_to_ata_dev(dev)))
sas_fail_probe(dev, __func__, -ENODEV);
}

}

static bool sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func)
{
struct domain_device *dev, *n;
bool retry = false;

list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
int rc;

if (!dev_is_sata(dev))
continue;

sas_ata_wait_eh(dev);
rc = dev->sata_dev.pm_result;
if (rc == -EAGAIN)
retry = true;
else if (rc) {
/* since we don't have a
* ->port_{suspend|resume} routine in our
* ata_port ops, and no entanglements with
* acpi, suspend should just be mechanical trip
* through eh, catch cases where these
* assumptions are invalidated
*/
WARN_ONCE(1, "failed %s %s error: %d\n", func,
dev_name(&dev->rphy->dev), rc);
}

/* if libata failed to power manage the device, tear it down */
if (ata_dev_disabled(sas_to_ata_dev(dev)))
sas_fail_probe(dev, func, -ENODEV);
}

return retry;
}

void sas_suspend_sata(struct asd_sas_port *port)
{
struct domain_device *dev;

retry:
mutex_lock(&port->ha->disco_mutex);
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
struct sata_device *sata;

if (!dev_is_sata(dev))
continue;

sata = &dev->sata_dev;
if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND)
continue;

sata->pm_result = -EIO;
ata_sas_port_async_suspend(sata->ap, &sata->pm_result);
}
mutex_unlock(&port->ha->disco_mutex);

if (sas_ata_flush_pm_eh(port, __func__))
goto retry;
}

void sas_resume_sata(struct asd_sas_port *port)
{
struct domain_device *dev;

retry:
mutex_lock(&port->ha->disco_mutex);
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
struct sata_device *sata;

if (!dev_is_sata(dev))
continue;

sata = &dev->sata_dev;
if (sata->ap->pm_mesg.event == PM_EVENT_ON)
continue;

sata->pm_result = -EIO;
ata_sas_port_async_resume(sata->ap, &sata->pm_result);
}
mutex_unlock(&port->ha->disco_mutex);

if (sas_ata_flush_pm_eh(port, __func__))
goto retry;
}

/**
Expand Down
69 changes: 59 additions & 10 deletions trunk/drivers/scsi/libsas/sas_discover.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/async.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
#include "sas_internal.h"
Expand Down Expand Up @@ -180,16 +181,18 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)
struct Scsi_Host *shost = sas_ha->core.shost;
struct sas_internal *i = to_sas_internal(shost->transportt);

if (i->dft->lldd_dev_found) {
res = i->dft->lldd_dev_found(dev);
if (res) {
printk("sas: driver on pcidev %s cannot handle "
"device %llx, error:%d\n",
dev_name(sas_ha->dev),
SAS_ADDR(dev->sas_addr), res);
}
kref_get(&dev->kref);
if (!i->dft->lldd_dev_found)
return 0;

res = i->dft->lldd_dev_found(dev);
if (res) {
printk("sas: driver on pcidev %s cannot handle "
"device %llx, error:%d\n",
dev_name(sas_ha->dev),
SAS_ADDR(dev->sas_addr), res);
}
set_bit(SAS_DEV_FOUND, &dev->state);
kref_get(&dev->kref);
return res;
}

Expand All @@ -200,7 +203,10 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
struct Scsi_Host *shost = sas_ha->core.shost;
struct sas_internal *i = to_sas_internal(shost->transportt);

if (i->dft->lldd_dev_gone) {
if (!i->dft->lldd_dev_gone)
return;

if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) {
i->dft->lldd_dev_gone(dev);
sas_put_device(dev);
}
Expand Down Expand Up @@ -234,6 +240,47 @@ static void sas_probe_devices(struct work_struct *work)
}
}

static void sas_suspend_devices(struct work_struct *work)
{
struct asd_sas_phy *phy;
struct domain_device *dev;
struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;
struct Scsi_Host *shost = port->ha->core.shost;
struct sas_internal *si = to_sas_internal(shost->transportt);

clear_bit(DISCE_SUSPEND, &port->disc.pending);

sas_suspend_sata(port);

/* lldd is free to forget the domain_device across the
* suspension, we force the issue here to keep the reference
* counts aligned
*/
list_for_each_entry(dev, &port->dev_list, dev_list_node)
sas_notify_lldd_dev_gone(dev);

/* we are suspending, so we know events are disabled and
* phy_list is not being mutated
*/
list_for_each_entry(phy, &port->phy_list, port_phy_el) {
if (si->dft->lldd_port_formed)
si->dft->lldd_port_deformed(phy);
phy->suspended = 1;
port->suspended = 1;
}
}

static void sas_resume_devices(struct work_struct *work)
{
struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;

clear_bit(DISCE_RESUME, &port->disc.pending);

sas_resume_sata(port);
}

/**
* sas_discover_end_dev -- discover an end device (SSP, etc)
* @end: pointer to domain device of interest
Expand Down Expand Up @@ -530,6 +577,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
[DISCE_PROBE] = sas_probe_devices,
[DISCE_SUSPEND] = sas_suspend_devices,
[DISCE_RESUME] = sas_resume_devices,
[DISCE_DESTRUCT] = sas_destruct_devices,
};

Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/scsi/libsas/sas_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ static const char *sas_phye_str[] = {
[1] = "PHYE_OOB_DONE",
[2] = "PHYE_OOB_ERROR",
[3] = "PHYE_SPINUP_HOLD",
[4] = "PHYE_RESUME_TIMEOUT",
};

void sas_dprint_porte(int phyid, enum port_event pe)
Expand Down
4 changes: 2 additions & 2 deletions trunk/drivers/scsi/libsas/sas_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
&phy->port_events[event].work, ha);
}

static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
{
struct sas_ha_struct *ha = phy->ha;

Expand All @@ -159,7 +159,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha)

sas_ha->notify_ha_event = notify_ha_event;
sas_ha->notify_port_event = notify_port_event;
sas_ha->notify_phy_event = notify_phy_event;
sas_ha->notify_phy_event = sas_notify_phy_event;

return 0;
}
90 changes: 89 additions & 1 deletion trunk/drivers/scsi/libsas/sas_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
return error;
}

int sas_unregister_ha(struct sas_ha_struct *sas_ha)
static void sas_disable_events(struct sas_ha_struct *sas_ha)
{
/* Set the state to unregistered to avoid further unchained
* events to be queued, and flush any in-progress drainers
Expand All @@ -189,7 +189,11 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha)
spin_unlock_irq(&sas_ha->lock);
__sas_drain_work(sas_ha);
mutex_unlock(&sas_ha->drain_mutex);
}

int sas_unregister_ha(struct sas_ha_struct *sas_ha)
{
sas_disable_events(sas_ha);
sas_unregister_ports(sas_ha);

/* flush unregistration work */
Expand Down Expand Up @@ -381,6 +385,90 @@ int sas_set_phy_speed(struct sas_phy *phy,
return ret;
}

void sas_prep_resume_ha(struct sas_ha_struct *ha)
{
int i;

set_bit(SAS_HA_REGISTERED, &ha->state);

/* clear out any stale link events/data from the suspension path */
for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_phy *phy = ha->sas_phy[i];

memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
phy->port_events_pending = 0;
phy->phy_events_pending = 0;
phy->frame_rcvd_size = 0;
}
}
EXPORT_SYMBOL(sas_prep_resume_ha);

static int phys_suspended(struct sas_ha_struct *ha)
{
int i, rc = 0;

for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_phy *phy = ha->sas_phy[i];

if (phy->suspended)
rc++;
}

return rc;
}

void sas_resume_ha(struct sas_ha_struct *ha)
{
const unsigned long tmo = msecs_to_jiffies(25000);
int i;

/* deform ports on phys that did not resume
* at this point we may be racing the phy coming back (as posted
* by the lldd). So we post the event and once we are in the
* libsas context check that the phy remains suspended before
* tearing it down.
*/
i = phys_suspended(ha);
if (i)
dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
i, i > 1 ? "s" : "");
wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_phy *phy = ha->sas_phy[i];

if (phy->suspended) {
dev_warn(&phy->phy->dev, "resume timeout\n");
sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT);
}
}

/* all phys are back up or timed out, turn on i/o so we can
* flush out disks that did not return
*/
scsi_unblock_requests(ha->core.shost);
sas_drain_work(ha);
}
EXPORT_SYMBOL(sas_resume_ha);

void sas_suspend_ha(struct sas_ha_struct *ha)
{
int i;

sas_disable_events(ha);
scsi_block_requests(ha->core.shost);
for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_port *port = ha->sas_port[i];

sas_discover_event(port, DISCE_SUSPEND);
}

/* flush suspend events while unregistered */
mutex_lock(&ha->drain_mutex);
__sas_drain_work(ha);
mutex_unlock(&ha->drain_mutex);
}
EXPORT_SYMBOL(sas_suspend_ha);

static void sas_phy_release(struct sas_phy *phy)
{
kfree(phy->hostdata);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/scsi/libsas/sas_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy);

void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event);
void sas_device_set_phy(struct domain_device *dev, struct sas_port *port);
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id);
Expand Down
21 changes: 21 additions & 0 deletions trunk/drivers/scsi/libsas/sas_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ static void sas_phye_spinup_hold(struct work_struct *work)
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
}

static void sas_phye_resume_timeout(struct work_struct *work)
{
struct asd_sas_event *ev = to_asd_sas_event(work);
struct asd_sas_phy *phy = ev->phy;

clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending);

/* phew, lldd got the phy back in the nick of time */
if (!phy->suspended) {
dev_info(&phy->phy->dev, "resume timeout cancelled\n");
return;
}

phy->error = 0;
phy->suspended = 0;
sas_deform_port(phy, 1);
}


/* ---------- Phy class registration ---------- */

int sas_register_phys(struct sas_ha_struct *sas_ha)
Expand All @@ -105,6 +124,8 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
[PHYE_OOB_DONE] = sas_phye_oob_done,
[PHYE_OOB_ERROR] = sas_phye_oob_error,
[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,

};

static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
Expand Down
Loading

0 comments on commit c13ae69

Please sign in to comment.