diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_expander.c')
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 178 |
1 files changed, 99 insertions, 79 deletions
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 4b2ecd35dc5a..7e2d3c4c6171 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c | |||
@@ -183,13 +183,27 @@ static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) | |||
183 | } | 183 | } |
184 | } | 184 | } |
185 | 185 | ||
186 | static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | 186 | static enum sas_dev_type to_dev_type(struct discover_resp *dr) |
187 | void *disc_resp) | ||
188 | { | 187 | { |
188 | /* This is detecting a failure to transmit initial dev to host | ||
189 | * FIS as described in section J.5 of sas-2 r16 | ||
190 | */ | ||
191 | if (dr->attached_dev_type == NO_DEVICE && dr->attached_sata_dev && | ||
192 | dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) | ||
193 | return SATA_PENDING; | ||
194 | else | ||
195 | return dr->attached_dev_type; | ||
196 | } | ||
197 | |||
198 | static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) | ||
199 | { | ||
200 | enum sas_dev_type dev_type; | ||
201 | enum sas_linkrate linkrate; | ||
202 | u8 sas_addr[SAS_ADDR_SIZE]; | ||
203 | struct smp_resp *resp = rsp; | ||
204 | struct discover_resp *dr = &resp->disc; | ||
189 | struct expander_device *ex = &dev->ex_dev; | 205 | struct expander_device *ex = &dev->ex_dev; |
190 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | 206 | struct ex_phy *phy = &ex->ex_phy[phy_id]; |
191 | struct smp_resp *resp = disc_resp; | ||
192 | struct discover_resp *dr = &resp->disc; | ||
193 | struct sas_rphy *rphy = dev->rphy; | 207 | struct sas_rphy *rphy = dev->rphy; |
194 | bool new_phy = !phy->phy; | 208 | bool new_phy = !phy->phy; |
195 | char *type; | 209 | char *type; |
@@ -213,8 +227,13 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
213 | break; | 227 | break; |
214 | } | 228 | } |
215 | 229 | ||
230 | /* check if anything important changed to squelch debug */ | ||
231 | dev_type = phy->attached_dev_type; | ||
232 | linkrate = phy->linkrate; | ||
233 | memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); | ||
234 | |||
235 | phy->attached_dev_type = to_dev_type(dr); | ||
216 | phy->phy_id = phy_id; | 236 | phy->phy_id = phy_id; |
217 | phy->attached_dev_type = dr->attached_dev_type; | ||
218 | phy->linkrate = dr->linkrate; | 237 | phy->linkrate = dr->linkrate; |
219 | phy->attached_sata_host = dr->attached_sata_host; | 238 | phy->attached_sata_host = dr->attached_sata_host; |
220 | phy->attached_sata_dev = dr->attached_sata_dev; | 239 | phy->attached_sata_dev = dr->attached_sata_dev; |
@@ -229,7 +248,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
229 | phy->last_da_index = -1; | 248 | phy->last_da_index = -1; |
230 | 249 | ||
231 | phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); | 250 | phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); |
232 | phy->phy->identify.device_type = phy->attached_dev_type; | 251 | phy->phy->identify.device_type = dr->attached_dev_type; |
233 | phy->phy->identify.initiator_port_protocols = phy->attached_iproto; | 252 | phy->phy->identify.initiator_port_protocols = phy->attached_iproto; |
234 | phy->phy->identify.target_port_protocols = phy->attached_tproto; | 253 | phy->phy->identify.target_port_protocols = phy->attached_tproto; |
235 | phy->phy->identify.phy_identifier = phy_id; | 254 | phy->phy->identify.phy_identifier = phy_id; |
@@ -246,6 +265,9 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
246 | } | 265 | } |
247 | 266 | ||
248 | switch (phy->attached_dev_type) { | 267 | switch (phy->attached_dev_type) { |
268 | case SATA_PENDING: | ||
269 | type = "stp pending"; | ||
270 | break; | ||
249 | case NO_DEVICE: | 271 | case NO_DEVICE: |
250 | type = "no device"; | 272 | type = "no device"; |
251 | break; | 273 | break; |
@@ -270,6 +292,16 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
270 | type = "unknown"; | 292 | type = "unknown"; |
271 | } | 293 | } |
272 | 294 | ||
295 | /* this routine is polled by libata error recovery so filter | ||
296 | * unimportant messages | ||
297 | */ | ||
298 | if (new_phy || phy->attached_dev_type != dev_type || | ||
299 | phy->linkrate != linkrate || | ||
300 | SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) | ||
301 | /* pass */; | ||
302 | else | ||
303 | return; | ||
304 | |||
273 | SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", | 305 | SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", |
274 | SAS_ADDR(dev->sas_addr), phy->phy_id, | 306 | SAS_ADDR(dev->sas_addr), phy->phy_id, |
275 | sas_route_char(dev, phy), phy->linkrate, | 307 | sas_route_char(dev, phy), phy->linkrate, |
@@ -304,50 +336,25 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) | |||
304 | static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, | 336 | static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, |
305 | u8 *disc_resp, int single) | 337 | u8 *disc_resp, int single) |
306 | { | 338 | { |
307 | struct domain_device *ata_dev = sas_ex_to_ata(dev, single); | 339 | struct discover_resp *dr; |
308 | int i, res; | 340 | int res; |
309 | 341 | ||
310 | disc_req[9] = single; | 342 | disc_req[9] = single; |
311 | for (i = 1 ; i < 3; i++) { | ||
312 | struct discover_resp *dr; | ||
313 | 343 | ||
314 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | 344 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, |
315 | disc_resp, DISCOVER_RESP_SIZE); | 345 | disc_resp, DISCOVER_RESP_SIZE); |
316 | if (res) | 346 | if (res) |
317 | return res; | 347 | return res; |
318 | dr = &((struct smp_resp *)disc_resp)->disc; | 348 | dr = &((struct smp_resp *)disc_resp)->disc; |
319 | if (memcmp(dev->sas_addr, dr->attached_sas_addr, | 349 | if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { |
320 | SAS_ADDR_SIZE) == 0) { | 350 | sas_printk("Found loopback topology, just ignore it!\n"); |
321 | sas_printk("Found loopback topology, just ignore it!\n"); | 351 | return 0; |
322 | return 0; | ||
323 | } | ||
324 | |||
325 | /* This is detecting a failure to transmit initial | ||
326 | * dev to host FIS as described in section J.5 of | ||
327 | * sas-2 r16 | ||
328 | */ | ||
329 | if (!(dr->attached_dev_type == 0 && | ||
330 | dr->attached_sata_dev)) | ||
331 | break; | ||
332 | |||
333 | /* In order to generate the dev to host FIS, we send a | ||
334 | * link reset to the expander port. If a device was | ||
335 | * previously detected on this port we ask libata to | ||
336 | * manage the reset and link recovery. | ||
337 | */ | ||
338 | if (ata_dev) { | ||
339 | sas_ata_schedule_reset(ata_dev); | ||
340 | break; | ||
341 | } | ||
342 | sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); | ||
343 | /* Wait for the reset to trigger the negotiation */ | ||
344 | msleep(500); | ||
345 | } | 352 | } |
346 | sas_set_ex_phy(dev, single, disc_resp); | 353 | sas_set_ex_phy(dev, single, disc_resp); |
347 | return 0; | 354 | return 0; |
348 | } | 355 | } |
349 | 356 | ||
350 | static int sas_ex_phy_discover(struct domain_device *dev, int single) | 357 | int sas_ex_phy_discover(struct domain_device *dev, int single) |
351 | { | 358 | { |
352 | struct expander_device *ex = &dev->ex_dev; | 359 | struct expander_device *ex = &dev->ex_dev; |
353 | int res = 0; | 360 | int res = 0; |
@@ -652,9 +659,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) | |||
652 | #define RPS_REQ_SIZE 16 | 659 | #define RPS_REQ_SIZE 16 |
653 | #define RPS_RESP_SIZE 60 | 660 | #define RPS_RESP_SIZE 60 |
654 | 661 | ||
655 | static int sas_get_report_phy_sata(struct domain_device *dev, | 662 | int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, |
656 | int phy_id, | 663 | struct smp_resp *rps_resp) |
657 | struct smp_resp *rps_resp) | ||
658 | { | 664 | { |
659 | int res; | 665 | int res; |
660 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); | 666 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); |
@@ -764,21 +770,9 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
764 | 770 | ||
765 | #ifdef CONFIG_SCSI_SAS_ATA | 771 | #ifdef CONFIG_SCSI_SAS_ATA |
766 | if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { | 772 | if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { |
767 | child->dev_type = SATA_DEV; | 773 | res = sas_get_ata_info(child, phy); |
768 | if (phy->attached_tproto & SAS_PROTOCOL_STP) | 774 | if (res) |
769 | child->tproto = phy->attached_tproto; | ||
770 | if (phy->attached_sata_dev) | ||
771 | child->tproto |= SATA_DEV; | ||
772 | res = sas_get_report_phy_sata(parent, phy_id, | ||
773 | &child->sata_dev.rps_resp); | ||
774 | if (res) { | ||
775 | SAS_DPRINTK("report phy sata to %016llx:0x%x returned " | ||
776 | "0x%x\n", SAS_ADDR(parent->sas_addr), | ||
777 | phy_id, res); | ||
778 | goto out_free; | 775 | goto out_free; |
779 | } | ||
780 | memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, | ||
781 | sizeof(struct dev_to_host_fis)); | ||
782 | 776 | ||
783 | rphy = sas_end_device_alloc(phy->port); | 777 | rphy = sas_end_device_alloc(phy->port); |
784 | if (unlikely(!rphy)) | 778 | if (unlikely(!rphy)) |
@@ -993,7 +987,8 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) | |||
993 | 987 | ||
994 | if (ex_phy->attached_dev_type != SAS_END_DEV && | 988 | if (ex_phy->attached_dev_type != SAS_END_DEV && |
995 | ex_phy->attached_dev_type != FANOUT_DEV && | 989 | ex_phy->attached_dev_type != FANOUT_DEV && |
996 | ex_phy->attached_dev_type != EDGE_DEV) { | 990 | ex_phy->attached_dev_type != EDGE_DEV && |
991 | ex_phy->attached_dev_type != SATA_PENDING) { | ||
997 | SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " | 992 | SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " |
998 | "phy 0x%x\n", ex_phy->attached_dev_type, | 993 | "phy 0x%x\n", ex_phy->attached_dev_type, |
999 | SAS_ADDR(dev->sas_addr), | 994 | SAS_ADDR(dev->sas_addr), |
@@ -1019,6 +1014,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) | |||
1019 | 1014 | ||
1020 | switch (ex_phy->attached_dev_type) { | 1015 | switch (ex_phy->attached_dev_type) { |
1021 | case SAS_END_DEV: | 1016 | case SAS_END_DEV: |
1017 | case SATA_PENDING: | ||
1022 | child = sas_ex_discover_end_dev(dev, phy_id); | 1018 | child = sas_ex_discover_end_dev(dev, phy_id); |
1023 | break; | 1019 | break; |
1024 | case FANOUT_DEV: | 1020 | case FANOUT_DEV: |
@@ -1688,8 +1684,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, | |||
1688 | return res; | 1684 | return res; |
1689 | } | 1685 | } |
1690 | 1686 | ||
1691 | int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, | 1687 | static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, |
1692 | u8 *attached_sas_addr) | 1688 | u8 *sas_addr, enum sas_dev_type *type) |
1693 | { | 1689 | { |
1694 | int res; | 1690 | int res; |
1695 | struct smp_resp *disc_resp; | 1691 | struct smp_resp *disc_resp; |
@@ -1701,10 +1697,11 @@ int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, | |||
1701 | dr = &disc_resp->disc; | 1697 | dr = &disc_resp->disc; |
1702 | 1698 | ||
1703 | res = sas_get_phy_discover(dev, phy_id, disc_resp); | 1699 | res = sas_get_phy_discover(dev, phy_id, disc_resp); |
1704 | if (!res) { | 1700 | if (res == 0) { |
1705 | memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8); | 1701 | memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8); |
1706 | if (dr->attached_dev_type == 0) | 1702 | *type = to_dev_type(dr); |
1707 | memset(attached_sas_addr, 0, 8); | 1703 | if (*type == 0) |
1704 | memset(sas_addr, 0, 8); | ||
1708 | } | 1705 | } |
1709 | kfree(disc_resp); | 1706 | kfree(disc_resp); |
1710 | return res; | 1707 | return res; |
@@ -1953,39 +1950,62 @@ out: | |||
1953 | return res; | 1950 | return res; |
1954 | } | 1951 | } |
1955 | 1952 | ||
1953 | static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old) | ||
1954 | { | ||
1955 | if (old == new) | ||
1956 | return true; | ||
1957 | |||
1958 | /* treat device directed resets as flutter, if we went | ||
1959 | * SAS_END_DEV to SATA_PENDING the link needs recovery | ||
1960 | */ | ||
1961 | if ((old == SATA_PENDING && new == SAS_END_DEV) || | ||
1962 | (old == SAS_END_DEV && new == SATA_PENDING)) | ||
1963 | return true; | ||
1964 | |||
1965 | return false; | ||
1966 | } | ||
1967 | |||
1956 | static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) | 1968 | static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) |
1957 | { | 1969 | { |
1958 | struct expander_device *ex = &dev->ex_dev; | 1970 | struct expander_device *ex = &dev->ex_dev; |
1959 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | 1971 | struct ex_phy *phy = &ex->ex_phy[phy_id]; |
1960 | u8 attached_sas_addr[8]; | 1972 | enum sas_dev_type type = NO_DEVICE; |
1973 | u8 sas_addr[8]; | ||
1961 | int res; | 1974 | int res; |
1962 | 1975 | ||
1963 | res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr); | 1976 | res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); |
1964 | switch (res) { | 1977 | switch (res) { |
1965 | case SMP_RESP_NO_PHY: | 1978 | case SMP_RESP_NO_PHY: |
1966 | phy->phy_state = PHY_NOT_PRESENT; | 1979 | phy->phy_state = PHY_NOT_PRESENT; |
1967 | sas_unregister_devs_sas_addr(dev, phy_id, last); | 1980 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
1968 | goto out; break; | 1981 | return res; |
1969 | case SMP_RESP_PHY_VACANT: | 1982 | case SMP_RESP_PHY_VACANT: |
1970 | phy->phy_state = PHY_VACANT; | 1983 | phy->phy_state = PHY_VACANT; |
1971 | sas_unregister_devs_sas_addr(dev, phy_id, last); | 1984 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
1972 | goto out; break; | 1985 | return res; |
1973 | case SMP_RESP_FUNC_ACC: | 1986 | case SMP_RESP_FUNC_ACC: |
1974 | break; | 1987 | break; |
1975 | } | 1988 | } |
1976 | 1989 | ||
1977 | if (SAS_ADDR(attached_sas_addr) == 0) { | 1990 | if (SAS_ADDR(sas_addr) == 0) { |
1978 | phy->phy_state = PHY_EMPTY; | 1991 | phy->phy_state = PHY_EMPTY; |
1979 | sas_unregister_devs_sas_addr(dev, phy_id, last); | 1992 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
1980 | } else if (SAS_ADDR(attached_sas_addr) == | 1993 | return res; |
1981 | SAS_ADDR(phy->attached_sas_addr)) { | 1994 | } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && |
1982 | SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", | 1995 | dev_type_flutter(type, phy->attached_dev_type)) { |
1983 | SAS_ADDR(dev->sas_addr), phy_id); | 1996 | struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); |
1997 | char *action = ""; | ||
1998 | |||
1984 | sas_ex_phy_discover(dev, phy_id); | 1999 | sas_ex_phy_discover(dev, phy_id); |
1985 | } else | 2000 | |
1986 | res = sas_discover_new(dev, phy_id); | 2001 | if (ata_dev && phy->attached_dev_type == SATA_PENDING) |
1987 | out: | 2002 | action = ", needs recovery"; |
1988 | return res; | 2003 | SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", |
2004 | SAS_ADDR(dev->sas_addr), phy_id, action); | ||
2005 | return res; | ||
2006 | } | ||
2007 | |||
2008 | return sas_discover_new(dev, phy_id); | ||
1989 | } | 2009 | } |
1990 | 2010 | ||
1991 | /** | 2011 | /** |