diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_expander.c')
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 230 |
1 files changed, 117 insertions, 113 deletions
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 23e90c5f8f35..b500f0c1449c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/scatterlist.h> | 25 | #include <linux/scatterlist.h> |
26 | #include <linux/blkdev.h> | ||
26 | 27 | ||
27 | #include "sas_internal.h" | 28 | #include "sas_internal.h" |
28 | 29 | ||
@@ -36,14 +37,6 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, | |||
36 | u8 *sas_addr, int include); | 37 | u8 *sas_addr, int include); |
37 | static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); | 38 | static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); |
38 | 39 | ||
39 | #if 0 | ||
40 | /* FIXME: smp needs to migrate into the sas class */ | ||
41 | static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *, | ||
42 | char *, loff_t, size_t); | ||
43 | static ssize_t smp_portal_write(struct kobject *, struct bin_attribute *, | ||
44 | char *, loff_t, size_t); | ||
45 | #endif | ||
46 | |||
47 | /* ---------- SMP task management ---------- */ | 40 | /* ---------- SMP task management ---------- */ |
48 | 41 | ||
49 | static void smp_task_timedout(unsigned long _task) | 42 | static void smp_task_timedout(unsigned long _task) |
@@ -220,6 +213,36 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
220 | #define DISCOVER_REQ_SIZE 16 | 213 | #define DISCOVER_REQ_SIZE 16 |
221 | #define DISCOVER_RESP_SIZE 56 | 214 | #define DISCOVER_RESP_SIZE 56 |
222 | 215 | ||
216 | static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, | ||
217 | u8 *disc_resp, int single) | ||
218 | { | ||
219 | int i, res; | ||
220 | |||
221 | disc_req[9] = single; | ||
222 | for (i = 1 ; i < 3; i++) { | ||
223 | struct discover_resp *dr; | ||
224 | |||
225 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | ||
226 | disc_resp, DISCOVER_RESP_SIZE); | ||
227 | if (res) | ||
228 | return res; | ||
229 | /* This is detecting a failure to transmit inital | ||
230 | * dev to host FIS as described in section G.5 of | ||
231 | * sas-2 r 04b */ | ||
232 | dr = &((struct smp_resp *)disc_resp)->disc; | ||
233 | if (!(dr->attached_dev_type == 0 && | ||
234 | dr->attached_sata_dev)) | ||
235 | break; | ||
236 | /* In order to generate the dev to host FIS, we | ||
237 | * send a link reset to the expander port */ | ||
238 | sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); | ||
239 | /* Wait for the reset to trigger the negotiation */ | ||
240 | msleep(500); | ||
241 | } | ||
242 | sas_set_ex_phy(dev, single, disc_resp); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
223 | static int sas_ex_phy_discover(struct domain_device *dev, int single) | 246 | static int sas_ex_phy_discover(struct domain_device *dev, int single) |
224 | { | 247 | { |
225 | struct expander_device *ex = &dev->ex_dev; | 248 | struct expander_device *ex = &dev->ex_dev; |
@@ -240,23 +263,15 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single) | |||
240 | disc_req[1] = SMP_DISCOVER; | 263 | disc_req[1] = SMP_DISCOVER; |
241 | 264 | ||
242 | if (0 <= single && single < ex->num_phys) { | 265 | if (0 <= single && single < ex->num_phys) { |
243 | disc_req[9] = single; | 266 | res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); |
244 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | ||
245 | disc_resp, DISCOVER_RESP_SIZE); | ||
246 | if (res) | ||
247 | goto out_err; | ||
248 | sas_set_ex_phy(dev, single, disc_resp); | ||
249 | } else { | 267 | } else { |
250 | int i; | 268 | int i; |
251 | 269 | ||
252 | for (i = 0; i < ex->num_phys; i++) { | 270 | for (i = 0; i < ex->num_phys; i++) { |
253 | disc_req[9] = i; | 271 | res = sas_ex_phy_discover_helper(dev, disc_req, |
254 | res = smp_execute_task(dev, disc_req, | 272 | disc_resp, i); |
255 | DISCOVER_REQ_SIZE, disc_resp, | ||
256 | DISCOVER_RESP_SIZE); | ||
257 | if (res) | 273 | if (res) |
258 | goto out_err; | 274 | goto out_err; |
259 | sas_set_ex_phy(dev, i, disc_resp); | ||
260 | } | 275 | } |
261 | } | 276 | } |
262 | out_err: | 277 | out_err: |
@@ -520,6 +535,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) | |||
520 | 535 | ||
521 | } | 536 | } |
522 | 537 | ||
538 | #ifdef CONFIG_SCSI_SAS_ATA | ||
539 | |||
523 | #define RPS_REQ_SIZE 16 | 540 | #define RPS_REQ_SIZE 16 |
524 | #define RPS_RESP_SIZE 60 | 541 | #define RPS_RESP_SIZE 60 |
525 | 542 | ||
@@ -529,6 +546,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, | |||
529 | { | 546 | { |
530 | int res; | 547 | int res; |
531 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); | 548 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); |
549 | u8 *resp = (u8 *)rps_resp; | ||
532 | 550 | ||
533 | if (!rps_req) | 551 | if (!rps_req) |
534 | return -ENOMEM; | 552 | return -ENOMEM; |
@@ -539,9 +557,30 @@ static int sas_get_report_phy_sata(struct domain_device *dev, | |||
539 | res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, | 557 | res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, |
540 | rps_resp, RPS_RESP_SIZE); | 558 | rps_resp, RPS_RESP_SIZE); |
541 | 559 | ||
560 | /* 0x34 is the FIS type for the D2H fis. There's a potential | ||
561 | * standards cockup here. sas-2 explicitly specifies the FIS | ||
562 | * should be encoded so that FIS type is in resp[24]. | ||
563 | * However, some expanders endian reverse this. Undo the | ||
564 | * reversal here */ | ||
565 | if (!res && resp[27] == 0x34 && resp[24] != 0x34) { | ||
566 | int i; | ||
567 | |||
568 | for (i = 0; i < 5; i++) { | ||
569 | int j = 24 + (i*4); | ||
570 | u8 a, b; | ||
571 | a = resp[j + 0]; | ||
572 | b = resp[j + 1]; | ||
573 | resp[j + 0] = resp[j + 3]; | ||
574 | resp[j + 1] = resp[j + 2]; | ||
575 | resp[j + 2] = b; | ||
576 | resp[j + 3] = a; | ||
577 | } | ||
578 | } | ||
579 | |||
542 | kfree(rps_req); | 580 | kfree(rps_req); |
543 | return 0; | 581 | return res; |
544 | } | 582 | } |
583 | #endif | ||
545 | 584 | ||
546 | static void sas_ex_get_linkrate(struct domain_device *parent, | 585 | static void sas_ex_get_linkrate(struct domain_device *parent, |
547 | struct domain_device *child, | 586 | struct domain_device *child, |
@@ -609,6 +648,7 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
609 | } | 648 | } |
610 | sas_ex_get_linkrate(parent, child, phy); | 649 | sas_ex_get_linkrate(parent, child, phy); |
611 | 650 | ||
651 | #ifdef CONFIG_SCSI_SAS_ATA | ||
612 | if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { | 652 | if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { |
613 | child->dev_type = SATA_DEV; | 653 | child->dev_type = SATA_DEV; |
614 | if (phy->attached_tproto & SAS_PROTO_STP) | 654 | if (phy->attached_tproto & SAS_PROTO_STP) |
@@ -625,16 +665,30 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
625 | } | 665 | } |
626 | memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, | 666 | memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, |
627 | sizeof(struct dev_to_host_fis)); | 667 | sizeof(struct dev_to_host_fis)); |
668 | |||
669 | rphy = sas_end_device_alloc(phy->port); | ||
670 | if (unlikely(!rphy)) | ||
671 | goto out_free; | ||
672 | |||
628 | sas_init_dev(child); | 673 | sas_init_dev(child); |
674 | |||
675 | child->rphy = rphy; | ||
676 | |||
677 | spin_lock_irq(&parent->port->dev_list_lock); | ||
678 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); | ||
679 | spin_unlock_irq(&parent->port->dev_list_lock); | ||
680 | |||
629 | res = sas_discover_sata(child); | 681 | res = sas_discover_sata(child); |
630 | if (res) { | 682 | if (res) { |
631 | SAS_DPRINTK("sas_discover_sata() for device %16llx at " | 683 | SAS_DPRINTK("sas_discover_sata() for device %16llx at " |
632 | "%016llx:0x%x returned 0x%x\n", | 684 | "%016llx:0x%x returned 0x%x\n", |
633 | SAS_ADDR(child->sas_addr), | 685 | SAS_ADDR(child->sas_addr), |
634 | SAS_ADDR(parent->sas_addr), phy_id, res); | 686 | SAS_ADDR(parent->sas_addr), phy_id, res); |
635 | goto out_free; | 687 | goto out_list_del; |
636 | } | 688 | } |
637 | } else if (phy->attached_tproto & SAS_PROTO_SSP) { | 689 | } else |
690 | #endif | ||
691 | if (phy->attached_tproto & SAS_PROTO_SSP) { | ||
638 | child->dev_type = SAS_END_DEV; | 692 | child->dev_type = SAS_END_DEV; |
639 | rphy = sas_end_device_alloc(phy->port); | 693 | rphy = sas_end_device_alloc(phy->port); |
640 | /* FIXME: error handling */ | 694 | /* FIXME: error handling */ |
@@ -646,9 +700,9 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
646 | child->rphy = rphy; | 700 | child->rphy = rphy; |
647 | sas_fill_in_rphy(child, rphy); | 701 | sas_fill_in_rphy(child, rphy); |
648 | 702 | ||
649 | spin_lock(&parent->port->dev_list_lock); | 703 | spin_lock_irq(&parent->port->dev_list_lock); |
650 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); | 704 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); |
651 | spin_unlock(&parent->port->dev_list_lock); | 705 | spin_unlock_irq(&parent->port->dev_list_lock); |
652 | 706 | ||
653 | res = sas_discover_end_dev(child); | 707 | res = sas_discover_end_dev(child); |
654 | if (res) { | 708 | if (res) { |
@@ -662,6 +716,7 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
662 | SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", | 716 | SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", |
663 | phy->attached_tproto, SAS_ADDR(parent->sas_addr), | 717 | phy->attached_tproto, SAS_ADDR(parent->sas_addr), |
664 | phy_id); | 718 | phy_id); |
719 | goto out_free; | ||
665 | } | 720 | } |
666 | 721 | ||
667 | list_add_tail(&child->siblings, &parent_ex->children); | 722 | list_add_tail(&child->siblings, &parent_ex->children); |
@@ -761,9 +816,9 @@ static struct domain_device *sas_ex_discover_expander( | |||
761 | sas_fill_in_rphy(child, rphy); | 816 | sas_fill_in_rphy(child, rphy); |
762 | sas_rphy_add(rphy); | 817 | sas_rphy_add(rphy); |
763 | 818 | ||
764 | spin_lock(&parent->port->dev_list_lock); | 819 | spin_lock_irq(&parent->port->dev_list_lock); |
765 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); | 820 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); |
766 | spin_unlock(&parent->port->dev_list_lock); | 821 | spin_unlock_irq(&parent->port->dev_list_lock); |
767 | 822 | ||
768 | res = sas_discover_expander(child); | 823 | res = sas_discover_expander(child); |
769 | if (res) { | 824 | if (res) { |
@@ -1359,30 +1414,6 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) | |||
1359 | return 0; | 1414 | return 0; |
1360 | } | 1415 | } |
1361 | 1416 | ||
1362 | #if 0 | ||
1363 | #define SMP_BIN_ATTR_NAME "smp_portal" | ||
1364 | |||
1365 | static void sas_ex_smp_hook(struct domain_device *dev) | ||
1366 | { | ||
1367 | struct expander_device *ex_dev = &dev->ex_dev; | ||
1368 | struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr; | ||
1369 | |||
1370 | memset(bin_attr, 0, sizeof(*bin_attr)); | ||
1371 | |||
1372 | bin_attr->attr.name = SMP_BIN_ATTR_NAME; | ||
1373 | bin_attr->attr.mode = 0600; | ||
1374 | |||
1375 | bin_attr->size = 0; | ||
1376 | bin_attr->private = NULL; | ||
1377 | bin_attr->read = smp_portal_read; | ||
1378 | bin_attr->write= smp_portal_write; | ||
1379 | bin_attr->mmap = NULL; | ||
1380 | |||
1381 | ex_dev->smp_portal_pid = -1; | ||
1382 | init_MUTEX(&ex_dev->smp_sema); | ||
1383 | } | ||
1384 | #endif | ||
1385 | |||
1386 | /** | 1417 | /** |
1387 | * sas_discover_expander -- expander discovery | 1418 | * sas_discover_expander -- expander discovery |
1388 | * @ex: pointer to expander domain device | 1419 | * @ex: pointer to expander domain device |
@@ -1844,76 +1875,49 @@ out: | |||
1844 | return res; | 1875 | return res; |
1845 | } | 1876 | } |
1846 | 1877 | ||
1847 | #if 0 | 1878 | int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, |
1848 | /* ---------- SMP portal ---------- */ | 1879 | struct request *req) |
1849 | |||
1850 | static ssize_t smp_portal_write(struct kobject *kobj, | ||
1851 | struct bin_attribute *bin_attr, | ||
1852 | char *buf, loff_t offs, size_t size) | ||
1853 | { | 1880 | { |
1854 | struct domain_device *dev = to_dom_device(kobj); | 1881 | struct domain_device *dev; |
1855 | struct expander_device *ex = &dev->ex_dev; | 1882 | int ret, type = rphy->identify.device_type; |
1856 | 1883 | struct request *rsp = req->next_rq; | |
1857 | if (offs != 0) | ||
1858 | return -EFBIG; | ||
1859 | else if (size == 0) | ||
1860 | return 0; | ||
1861 | 1884 | ||
1862 | down_interruptible(&ex->smp_sema); | 1885 | if (!rsp) { |
1863 | if (ex->smp_req) | 1886 | printk("%s: space for a smp response is missing\n", |
1864 | kfree(ex->smp_req); | 1887 | __FUNCTION__); |
1865 | ex->smp_req = kzalloc(size, GFP_USER); | 1888 | return -EINVAL; |
1866 | if (!ex->smp_req) { | ||
1867 | up(&ex->smp_sema); | ||
1868 | return -ENOMEM; | ||
1869 | } | 1889 | } |
1870 | memcpy(ex->smp_req, buf, size); | ||
1871 | ex->smp_req_size = size; | ||
1872 | ex->smp_portal_pid = current->pid; | ||
1873 | up(&ex->smp_sema); | ||
1874 | 1890 | ||
1875 | return size; | 1891 | /* seems aic94xx doesn't support */ |
1876 | } | 1892 | if (!rphy) { |
1877 | 1893 | printk("%s: can we send a smp request to a host?\n", | |
1878 | static ssize_t smp_portal_read(struct kobject *kobj, | 1894 | __FUNCTION__); |
1879 | struct bin_attribute *bin_attr, | 1895 | return -EINVAL; |
1880 | char *buf, loff_t offs, size_t size) | 1896 | } |
1881 | { | ||
1882 | struct domain_device *dev = to_dom_device(kobj); | ||
1883 | struct expander_device *ex = &dev->ex_dev; | ||
1884 | u8 *smp_resp; | ||
1885 | int res = -EINVAL; | ||
1886 | |||
1887 | /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact | ||
1888 | * it should be 0. | ||
1889 | */ | ||
1890 | 1897 | ||
1891 | down_interruptible(&ex->smp_sema); | 1898 | if (type != SAS_EDGE_EXPANDER_DEVICE && |
1892 | if (!ex->smp_req || ex->smp_portal_pid != current->pid) | 1899 | type != SAS_FANOUT_EXPANDER_DEVICE) { |
1893 | goto out; | 1900 | printk("%s: can we send a smp request to a device?\n", |
1901 | __FUNCTION__); | ||
1902 | return -EINVAL; | ||
1903 | } | ||
1894 | 1904 | ||
1895 | res = 0; | 1905 | dev = sas_find_dev_by_rphy(rphy); |
1896 | if (size == 0) | 1906 | if (!dev) { |
1897 | goto out; | 1907 | printk("%s: fail to find a domain_device?\n", __FUNCTION__); |
1908 | return -EINVAL; | ||
1909 | } | ||
1898 | 1910 | ||
1899 | res = -ENOMEM; | 1911 | /* do we need to support multiple segments? */ |
1900 | smp_resp = alloc_smp_resp(size); | 1912 | if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { |
1901 | if (!smp_resp) | 1913 | printk("%s: multiple segments req %u %u, rsp %u %u\n", |
1902 | goto out; | 1914 | __FUNCTION__, req->bio->bi_vcnt, req->data_len, |
1903 | res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size, | 1915 | rsp->bio->bi_vcnt, rsp->data_len); |
1904 | smp_resp, size); | 1916 | return -EINVAL; |
1905 | if (!res) { | ||
1906 | memcpy(buf, smp_resp, size); | ||
1907 | res = size; | ||
1908 | } | 1917 | } |
1909 | 1918 | ||
1910 | kfree(smp_resp); | 1919 | ret = smp_execute_task(dev, bio_data(req->bio), req->data_len, |
1911 | out: | 1920 | bio_data(rsp->bio), rsp->data_len); |
1912 | kfree(ex->smp_req); | 1921 | |
1913 | ex->smp_req = NULL; | 1922 | return ret; |
1914 | ex->smp_req_size = 0; | ||
1915 | ex->smp_portal_pid = -1; | ||
1916 | up(&ex->smp_sema); | ||
1917 | return res; | ||
1918 | } | 1923 | } |
1919 | #endif | ||