summaryrefslogtreecommitdiff
path: root/drivers/scsi/libsas/sas_discover.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2012-06-22 10:41:51 +0400
committerJames Bottomley <JBottomley@Parallels.com>2012-08-24 13:10:23 +0400
commit303694eeee5eacad5b84105a15afd9e351e1891b (patch)
treeba8e16274ffbde39b21100ced62b5b11e8c926d6 /drivers/scsi/libsas/sas_discover.c
parent2fcbdcb4c802fe40d6827dbc365dac90cfe8c0a3 (diff)
downloadlinux-303694eeee5eacad5b84105a15afd9e351e1891b.tar.xz
[SCSI] libsas: suspend / resume support
libsas power management routines to suspend and recover the sas domain based on a model where the lldd is allowed and expected to be "forgetful". sas_suspend_ha - disable event processing allowing the lldd to take down links without concern for causing hotplug events. Regardless of whether the lldd actually posts link down messages libsas notifies the lldd that all domain_devices are gone. sas_prep_resume_ha - on the way back up before the lldd starts link training clean out any spurious events that were generated on the way down, and re-enable event processing sas_resume_ha - after the lldd has started and decided that all phys have posted link-up events this routine is called to let libsas start it's own timeout of any phys that did not resume. After the timeout an lldd can cancel the phy teardown by posting a link-up event. Storage for ex_change_count (u16) and phy_change_count (u8) are changed to int so they can be set to -1 to indicate 'invalidated'. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Reviewed-by: Jacek Danecki <jacek.danecki@intel.com> Tested-by: Maciej Patelczyk <maciej.patelczyk@intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas/sas_discover.c')
-rw-r--r--drivers/scsi/libsas/sas_discover.c69
1 files changed, 59 insertions, 10 deletions
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 3e9dc1a84358..a0c3003e0c7d 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -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"
@@ -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;
}
@@ -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);
}
@@ -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
@@ -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,
};