aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas/sas_expander.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-22 14:36:49 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-22 14:36:49 -0400
commite6f194d8f6f50da6837af637b2fd839c34185f7a (patch)
treef3c479a2bc24d49a150ff183e2614ee0f76cb366 /drivers/scsi/libsas/sas_expander.c
parent7578634990fb47cc30083fbd812689aa6deacfc0 (diff)
parentb91421749a1840148d8c81637c03c0ace3f35269 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (60 commits) [SCSI] libsas: make ATA functions selectable by a config option [SCSI] bsg: unexport sg v3 helper functions [SCSI] bsg: fix bsg_unregister_queue [SCSI] bsg: make class backlinks [SCSI] 3w-9xxx: add support for 9690SA [SCSI] bsg: fix bsg_register_queue error path [SCSI] ESP: Increase ESP_BUS_TIMEOUT to 275. [SCSI] libsas: fix scr_read/write users and update the libata documentation [SCSI] mpt fusion: update Kconfig help [SCSI] scsi_transport_sas: add destructor for bsg [SCSI] iscsi_tcp: buggered kmalloc() [SCSI] qla2xxx: Update version number to 8.02.00-k2. [SCSI] qla2xxx: Add ISP25XX support. [SCSI] qla2xxx: Use pci_try_set_mwi(). [SCSI] qla2xxx: Use PCI-X/PCI-Express read control interfaces. [SCSI] qla2xxx: Re-factor isp_operations to static structures. [SCSI] qla2xxx: Validate mid-layer 'underflow' during check-condition handling. [SCSI] qla2xxx: Correct setting of 'current' and 'supported' speeds during FDMI registration. [SCSI] qla2xxx: Generalize iIDMA support. [SCSI] qla2xxx: Generalize FW-Interface-2 support. ...
Diffstat (limited to 'drivers/scsi/libsas/sas_expander.c')
-rw-r--r--drivers/scsi/libsas/sas_expander.c230
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);
37static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); 38static 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 */
41static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *,
42 char *, loff_t, size_t);
43static 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
49static void smp_task_timedout(unsigned long _task) 42static 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
216static 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
223static int sas_ex_phy_discover(struct domain_device *dev, int single) 246static 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 }
262out_err: 277out_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
546static void sas_ex_get_linkrate(struct domain_device *parent, 585static 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
1365static 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 1878int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
1848/* ---------- SMP portal ---------- */ 1879 struct request *req)
1849
1850static 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",
1878static 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,
1911out: 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