aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/libsas/sas_ata.c19
-rw-r--r--drivers/scsi/libsas/sas_expander.c44
-rw-r--r--include/scsi/sas_ata.h6
3 files changed, 63 insertions, 6 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index a8ace8d24e66..48cadf88c399 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
679 679
680 return rtn; 680 return rtn;
681} 681}
682
683void sas_ata_schedule_reset(struct domain_device *dev)
684{
685 struct ata_eh_info *ehi;
686 struct ata_port *ap;
687 unsigned long flags;
688
689 if (!dev_is_sata(dev))
690 return;
691
692 ap = dev->sata_dev.ap;
693 ehi = &ap->link.eh_info;
694
695 spin_lock_irqsave(ap->lock, flags);
696 ehi->err_mask |= AC_ERR_TIMEOUT;
697 ehi->action |= ATA_EH_RESET;
698 ata_port_schedule_eh(ap);
699 spin_unlock_irqrestore(ap->lock, flags);
700}
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index e45b259dac4c..f4894b0f537b 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -28,6 +28,7 @@
28 28
29#include "sas_internal.h" 29#include "sas_internal.h"
30 30
31#include <scsi/sas_ata.h>
31#include <scsi/scsi_transport.h> 32#include <scsi/scsi_transport.h>
32#include <scsi/scsi_transport_sas.h> 33#include <scsi/scsi_transport_sas.h>
33#include "../scsi_sas_internal.h" 34#include "../scsi_sas_internal.h"
@@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
226 return; 227 return;
227} 228}
228 229
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)
232{
233 struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
234 struct domain_device *dev;
235 struct sas_rphy *rphy;
236
237 if (!ex_phy->port)
238 return NULL;
239
240 rphy = ex_phy->port->rphy;
241 if (!rphy)
242 return NULL;
243
244 dev = sas_find_dev_by_rphy(rphy);
245
246 if (dev && dev_is_sata(dev))
247 return dev;
248
249 return NULL;
250}
251
229#define DISCOVER_REQ_SIZE 16 252#define DISCOVER_REQ_SIZE 16
230#define DISCOVER_RESP_SIZE 56 253#define DISCOVER_RESP_SIZE 56
231 254
232static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, 255static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
233 u8 *disc_resp, int single) 256 u8 *disc_resp, int single)
234{ 257{
258 struct domain_device *ata_dev = sas_ex_to_ata(dev, single);
235 int i, res; 259 int i, res;
236 260
237 disc_req[9] = single; 261 disc_req[9] = single;
@@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
242 disc_resp, DISCOVER_RESP_SIZE); 266 disc_resp, DISCOVER_RESP_SIZE);
243 if (res) 267 if (res)
244 return res; 268 return res;
245 /* This is detecting a failure to transmit initial
246 * dev to host FIS as described in section G.5 of
247 * sas-2 r 04b */
248 dr = &((struct smp_resp *)disc_resp)->disc; 269 dr = &((struct smp_resp *)disc_resp)->disc;
249 if (memcmp(dev->sas_addr, dr->attached_sas_addr, 270 if (memcmp(dev->sas_addr, dr->attached_sas_addr,
250 SAS_ADDR_SIZE) == 0) { 271 SAS_ADDR_SIZE) == 0) {
251 sas_printk("Found loopback topology, just ignore it!\n"); 272 sas_printk("Found loopback topology, just ignore it!\n");
252 return 0; 273 return 0;
253 } 274 }
275
276 /* This is detecting a failure to transmit initial
277 * dev to host FIS as described in section J.5 of
278 * sas-2 r16
279 */
254 if (!(dr->attached_dev_type == 0 && 280 if (!(dr->attached_dev_type == 0 &&
255 dr->attached_sata_dev)) 281 dr->attached_sata_dev))
256 break; 282 break;
257 /* In order to generate the dev to host FIS, we 283
258 * send a link reset to the expander port */ 284 /* In order to generate the dev to host FIS, we send a
285 * link reset to the expander port. If a device was
286 * previously detected on this port we ask libata to
287 * manage the reset and link recovery.
288 */
289 if (ata_dev) {
290 sas_ata_schedule_reset(ata_dev);
291 break;
292 }
259 sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); 293 sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
260 /* Wait for the reset to trigger the negotiation */ 294 /* Wait for the reset to trigger the negotiation */
261 msleep(500); 295 msleep(500);
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h
index 9f7a23d1146d..c0bcd30eec56 100644
--- a/include/scsi/sas_ata.h
+++ b/include/scsi/sas_ata.h
@@ -44,7 +44,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost);
44int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, 44int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
45 struct list_head *done_q); 45 struct list_head *done_q);
46void sas_probe_sata(struct work_struct *work); 46void sas_probe_sata(struct work_struct *work);
47 47void sas_ata_schedule_reset(struct domain_device *dev);
48#else 48#else
49 49
50 50
@@ -75,6 +75,10 @@ static inline void sas_probe_sata(struct work_struct *work)
75{ 75{
76} 76}
77 77
78static inline void sas_ata_schedule_reset(struct domain_device *dev)
79{
80}
81
78#endif 82#endif
79 83
80#endif /* _SAS_ATA_H_ */ 84#endif /* _SAS_ATA_H_ */