aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-12-02 19:07:01 -0500
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 15:13:51 -0500
commit81c757bc696284f39f07766f0c2ca67af64ce9bd (patch)
treea01a13219199520c7a992f90d54cc83513a5cdb0 /drivers/scsi
parent0b3e09da1350397f3f8b6fd839ab455b0b587451 (diff)
[SCSI] libsas: execute transport link resets with libata-eh via host workqueue
Link resets leave ata affiliations intact, so arrange for libsas to make an effort to avoid dropping the device due to a slow-to-recover link. Towards this end carry out reset in the host workqueue so that it can check for ata devices and kick the reset request to libata. Hard resets, in contrast, bypass libata since they are meant for associating an ata device with another initiator in the domain (tears down affiliations). Need to add a new transport_sas_phy_reset() since the current sas_phy_reset() is a utility function to libsas lldds. They are not prepared for it to loop back into eh. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/libsas/sas_ata.c11
-rw-r--r--drivers/scsi/libsas/sas_expander.c2
-rw-r--r--drivers/scsi/libsas/sas_init.c56
-rw-r--r--drivers/scsi/libsas/sas_internal.h1
4 files changed, 68 insertions, 2 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 48cadf88c399..03930a04a679 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -698,3 +698,14 @@ void sas_ata_schedule_reset(struct domain_device *dev)
698 ata_port_schedule_eh(ap); 698 ata_port_schedule_eh(ap);
699 spin_unlock_irqrestore(ap->lock, flags); 699 spin_unlock_irqrestore(ap->lock, flags);
700} 700}
701
702void sas_ata_wait_eh(struct domain_device *dev)
703{
704 struct ata_port *ap;
705
706 if (!dev_is_sata(dev))
707 return;
708
709 ap = dev->sata_dev.ap;
710 ata_port_wait_eh(ap);
711}
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index f4894b0f537b..d3c1a29b8a2a 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -228,7 +228,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
228} 228}
229 229
230/* check if we have an existing attached ata device on this expander phy */ 230/* check if we have an existing attached ata device on this expander phy */
231static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) 231struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
232{ 232{
233 struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; 233 struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
234 struct domain_device *dev; 234 struct domain_device *dev;
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index cb65adf4ab16..a15fb861daba 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -28,6 +28,7 @@
28#include <linux/init.h> 28#include <linux/init.h>
29#include <linux/device.h> 29#include <linux/device.h>
30#include <linux/spinlock.h> 30#include <linux/spinlock.h>
31#include <scsi/sas_ata.h>
31#include <scsi/scsi_host.h> 32#include <scsi/scsi_host.h>
32#include <scsi/scsi_device.h> 33#include <scsi/scsi_device.h>
33#include <scsi/scsi_transport.h> 34#include <scsi/scsi_transport.h>
@@ -195,6 +196,59 @@ static int sas_get_linkerrors(struct sas_phy *phy)
195 return sas_smp_get_phy_events(phy); 196 return sas_smp_get_phy_events(phy);
196} 197}
197 198
199/**
200 * transport_sas_phy_reset - reset a phy and permit libata to manage the link
201 *
202 * phy reset request via sysfs in host workqueue context so we know we
203 * can block on eh and safely traverse the domain_device topology
204 */
205static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
206{
207 int ret;
208 enum phy_func reset_type;
209
210 if (hard_reset)
211 reset_type = PHY_FUNC_HARD_RESET;
212 else
213 reset_type = PHY_FUNC_LINK_RESET;
214
215 if (scsi_is_sas_phy_local(phy)) {
216 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
217 struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
218 struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
219 struct sas_internal *i =
220 to_sas_internal(sas_ha->core.shost->transportt);
221 struct domain_device *dev = NULL;
222
223 if (asd_phy->port)
224 dev = asd_phy->port->port_dev;
225
226 /* validate that dev has been probed */
227 if (dev)
228 dev = sas_find_dev_by_rphy(dev->rphy);
229
230 if (dev && dev_is_sata(dev) && !hard_reset) {
231 sas_ata_schedule_reset(dev);
232 sas_ata_wait_eh(dev);
233 ret = 0;
234 } else
235 ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
236 } else {
237 struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
238 struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
239 struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
240
241 if (ata_dev && !hard_reset) {
242 sas_ata_schedule_reset(ata_dev);
243 sas_ata_wait_eh(ata_dev);
244 ret = 0;
245 } else
246 ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
247 }
248
249 return ret;
250}
251
198int sas_phy_enable(struct sas_phy *phy, int enable) 252int sas_phy_enable(struct sas_phy *phy, int enable)
199{ 253{
200 int ret; 254 int ret;
@@ -300,7 +354,7 @@ static void phy_reset_work(struct work_struct *work)
300{ 354{
301 struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); 355 struct sas_phy_data *d = container_of(work, typeof(*d), reset_work);
302 356
303 d->reset_result = sas_phy_reset(d->phy, d->hard_reset); 357 d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
304} 358}
305 359
306static int sas_phy_setup(struct sas_phy *phy) 360static int sas_phy_setup(struct sas_phy *phy)
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 9ba65e0c6f91..ae9698d9d857 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -85,6 +85,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
85int sas_smp_get_phy_events(struct sas_phy *phy); 85int sas_smp_get_phy_events(struct sas_phy *phy);
86 86
87struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); 87struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
88struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id);
88 89
89void sas_hae_reset(struct work_struct *work); 90void sas_hae_reset(struct work_struct *work);
90 91