diff options
-rw-r--r-- | drivers/ata/libata-eh.c | 1 | ||||
-rw-r--r-- | drivers/ata/libata.h | 1 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 11 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 2 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 56 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 1 | ||||
-rw-r--r-- | include/linux/libata.h | 1 | ||||
-rw-r--r-- | include/scsi/sas_ata.h | 4 |
8 files changed, 74 insertions, 3 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a9b282038000..c61316e9d2f7 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -863,6 +863,7 @@ void ata_port_wait_eh(struct ata_port *ap) | |||
863 | goto retry; | 863 | goto retry; |
864 | } | 864 | } |
865 | } | 865 | } |
866 | EXPORT_SYMBOL_GPL(ata_port_wait_eh); | ||
866 | 867 | ||
867 | static int ata_eh_nr_in_flight(struct ata_port *ap) | 868 | static int ata_eh_nr_in_flight(struct ata_port *ap) |
868 | { | 869 | { |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 814486d35c44..1fab235ee516 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
@@ -151,7 +151,6 @@ extern void ata_eh_acquire(struct ata_port *ap); | |||
151 | extern void ata_eh_release(struct ata_port *ap); | 151 | extern void ata_eh_release(struct ata_port *ap); |
152 | extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); | 152 | extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); |
153 | extern void ata_scsi_error(struct Scsi_Host *host); | 153 | extern void ata_scsi_error(struct Scsi_Host *host); |
154 | extern void ata_port_wait_eh(struct ata_port *ap); | ||
155 | extern void ata_eh_fastdrain_timerfn(unsigned long arg); | 154 | extern void ata_eh_fastdrain_timerfn(unsigned long arg); |
156 | extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); | 155 | extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); |
157 | extern void ata_dev_disable(struct ata_device *dev); | 156 | extern void ata_dev_disable(struct ata_device *dev); |
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 | |||
702 | void 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 */ |
231 | static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) | 231 | struct 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 | */ | ||
205 | static 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 | |||
198 | int sas_phy_enable(struct sas_phy *phy, int enable) | 252 | int 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 | ||
306 | static int sas_phy_setup(struct sas_phy *phy) | 360 | static 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, | |||
85 | int sas_smp_get_phy_events(struct sas_phy *phy); | 85 | int sas_smp_get_phy_events(struct sas_phy *phy); |
86 | 86 | ||
87 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); | 87 | struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); |
88 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); | ||
88 | 89 | ||
89 | void sas_hae_reset(struct work_struct *work); | 90 | void sas_hae_reset(struct work_struct *work); |
90 | 91 | ||
diff --git a/include/linux/libata.h b/include/linux/libata.h index cafc09a64fe4..aa4270477563 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -1147,6 +1147,7 @@ static inline int ata_acpi_cbl_80wire(struct ata_port *ap, | |||
1147 | * EH - drivers/ata/libata-eh.c | 1147 | * EH - drivers/ata/libata-eh.c |
1148 | */ | 1148 | */ |
1149 | extern void ata_port_schedule_eh(struct ata_port *ap); | 1149 | extern void ata_port_schedule_eh(struct ata_port *ap); |
1150 | extern void ata_port_wait_eh(struct ata_port *ap); | ||
1150 | extern int ata_link_abort(struct ata_link *link); | 1151 | extern int ata_link_abort(struct ata_link *link); |
1151 | extern int ata_port_abort(struct ata_port *ap); | 1152 | extern int ata_port_abort(struct ata_port *ap); |
1152 | extern int ata_port_freeze(struct ata_port *ap); | 1153 | extern int ata_port_freeze(struct ata_port *ap); |
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c0bcd30eec56..da3f37727387 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h | |||
@@ -45,6 +45,7 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, | |||
45 | struct list_head *done_q); | 45 | struct list_head *done_q); |
46 | void sas_probe_sata(struct work_struct *work); | 46 | void sas_probe_sata(struct work_struct *work); |
47 | void sas_ata_schedule_reset(struct domain_device *dev); | 47 | void sas_ata_schedule_reset(struct domain_device *dev); |
48 | void sas_ata_wait_eh(struct domain_device *dev); | ||
48 | #else | 49 | #else |
49 | 50 | ||
50 | 51 | ||
@@ -79,6 +80,9 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) | |||
79 | { | 80 | { |
80 | } | 81 | } |
81 | 82 | ||
83 | static inline void sas_ata_wait_eh(struct domain_device *dev) | ||
84 | { | ||
85 | } | ||
82 | #endif | 86 | #endif |
83 | 87 | ||
84 | #endif /* _SAS_ATA_H_ */ | 88 | #endif /* _SAS_ATA_H_ */ |