From 1acce1942a32296f7c25ba82776c97e9c04c8e1e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 22 Aug 2006 12:39:19 -0500 Subject: [SCSI] libsas: Add SATA support to STP piece for SATA on SAS expanders This patch adds support for SATA over SAS expanders to the previous two SATA support in libsas patches. There were a couple of nasty non trivial things to sort out before this one could be made to work. Firstly, I'd like to thank Doug Gilbert for diagnosing a problem with the LSI expanders where the REPORT_SATA_PHY command was returning the D2H FIS in the wrong order (Although, here, I think I have to blame the SAS standards which specifies the FIS "shall be returned in little endian format" and later on "which means resp[24] shall be FIS type" The latter, of course, implying big endian format). Just to make sure, I put a check for the D2H FIS type being in the wrong position and reverse the FIS data if it is. The second is a problem outlined in Annex G of the SAS standard (again, a technical point with D2H FIS ... necessitating a phy reset on certain conditions). With the patch, I can now see my SATA-1 disk in a cascaded expander configuration. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 81 ++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 13 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 23e90c5f8f35..0c4e3a977915 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -220,6 +220,36 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, #define DISCOVER_REQ_SIZE 16 #define DISCOVER_RESP_SIZE 56 +static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, + u8 *disc_resp, int single) +{ + int i, res; + + disc_req[9] = single; + for (i = 1 ; i < 3; i++) { + struct discover_resp *dr; + + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + return res; + /* This is detecting a failure to transmit inital + * dev to host FIS as described in section G.5 of + * sas-2 r 04b */ + dr = &((struct smp_resp *)disc_resp)->disc; + if (!(dr->attached_dev_type == 0 && + dr->attached_sata_dev)) + break; + /* In order to generate the dev to host FIS, we + * send a link reset to the expander port */ + sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET); + /* Wait for the reset to trigger the negotiation */ + msleep(500); + } + sas_set_ex_phy(dev, single, disc_resp); + return 0; +} + static int sas_ex_phy_discover(struct domain_device *dev, int single) { struct expander_device *ex = &dev->ex_dev; @@ -240,23 +270,15 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single) disc_req[1] = SMP_DISCOVER; if (0 <= single && single < ex->num_phys) { - disc_req[9] = single; - res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (res) - goto out_err; - sas_set_ex_phy(dev, single, disc_resp); + res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); } else { int i; for (i = 0; i < ex->num_phys; i++) { - disc_req[9] = i; - res = smp_execute_task(dev, disc_req, - DISCOVER_REQ_SIZE, disc_resp, - DISCOVER_RESP_SIZE); + res = sas_ex_phy_discover_helper(dev, disc_req, + disc_resp, i); if (res) goto out_err; - sas_set_ex_phy(dev, i, disc_resp); } } out_err: @@ -529,6 +551,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); + u8 *resp = (u8 *)rps_resp; if (!rps_req) return -ENOMEM; @@ -539,8 +562,28 @@ static int sas_get_report_phy_sata(struct domain_device *dev, res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, rps_resp, RPS_RESP_SIZE); + /* 0x34 is the FIS type for the D2H fis. There's a potential + * standards cockup here. sas-2 explicitly specifies the FIS + * should be encoded so that FIS type is in resp[24]. + * However, some expanders endian reverse this. Undo the + * reversal here */ + if (!res && resp[27] == 0x34 && resp[24] != 0x34) { + int i; + + for (i = 0; i < 5; i++) { + int j = 24 + (i*4); + u8 a, b; + a = resp[j + 0]; + b = resp[j + 1]; + resp[j + 0] = resp[j + 3]; + resp[j + 1] = resp[j + 2]; + resp[j + 2] = b; + resp[j + 3] = a; + } + } + kfree(rps_req); - return 0; + return res; } static void sas_ex_get_linkrate(struct domain_device *parent, @@ -625,14 +668,26 @@ static struct domain_device *sas_ex_discover_end_dev( } memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, sizeof(struct dev_to_host_fis)); + + rphy = sas_end_device_alloc(phy->port); + /* FIXME: error handling */ + BUG_ON(!rphy); + sas_init_dev(child); + + child->rphy = rphy; + + spin_lock(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock(&parent->port->dev_list_lock); + res = sas_discover_sata(child); if (res) { SAS_DPRINTK("sas_discover_sata() for device %16llx at " "%016llx:0x%x returned 0x%x\n", SAS_ADDR(child->sas_addr), SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_free; + goto out_list_del; } } else if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; -- cgit v1.2.2 From 38e2f035587b0674b3168931c8402f4d719fdd76 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 7 Sep 2006 15:52:09 -0500 Subject: [SCSI] libsas: fix up sas_smp_phy_control() The prototype of this has changed for the link speed setting patch. Need to update the SATA use of this. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 0c4e3a977915..07464873ab89 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -242,7 +242,7 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, break; /* In order to generate the dev to host FIS, we * send a link reset to the expander port */ - sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET); + sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); /* Wait for the reset to trigger the negotiation */ msleep(500); } -- cgit v1.2.2 From 528fd55200ec135548e71aee43650bca92a041aa Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Oct 2006 10:57:05 -0500 Subject: [SCSI] libsas: better error handling in sas_ex_discover_end_dev() This replaces a few BUG_ON() statements with the correct failure error handling. There are still many more to do. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 07464873ab89..d05fc23b4d5b 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -670,8 +670,8 @@ static struct domain_device *sas_ex_discover_end_dev( sizeof(struct dev_to_host_fis)); rphy = sas_end_device_alloc(phy->port); - /* FIXME: error handling */ - BUG_ON(!rphy); + if (unlikely(!rphy)) + goto out_free; sas_init_dev(child); -- cgit v1.2.2 From 9d720d82dc295521d70939c3f5edd54050730f09 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Jul 2007 13:15:51 -0500 Subject: [SCSI] libsas: fix lockdep issue with ATA lockdep noticed that with ATA support the port->dev_list_lock was entangled at irq context, so it now needs to become IRQ safe Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index d05fc23b4d5b..969fd3eb494a 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -677,9 +677,9 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_sata(child); if (res) { @@ -701,9 +701,9 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; sas_fill_in_rphy(child, rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_end_dev(child); if (res) { @@ -816,9 +816,9 @@ static struct domain_device *sas_ex_discover_expander( sas_fill_in_rphy(child, rphy); sas_rphy_add(rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_expander(child); if (res) { -- cgit v1.2.2 From ba1fc175cc6c0af7e78241e50160344f0f198282 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 9 Jul 2007 12:52:08 +0900 Subject: [SCSI] libsas: add SAS management protocol handler This patch adds support for SAS Management Protocol (SMP) passthrough support via bsg. aic94xx can use this. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 969fd3eb494a..a81195354b9c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -23,6 +23,7 @@ */ #include +#include #include "sas_internal.h" @@ -1972,3 +1973,50 @@ out: return res; } #endif + +int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + struct domain_device *dev; + int ret, type = rphy->identify.device_type; + struct request *rsp = req->next_rq; + + if (!rsp) { + printk("%s: space for a smp response is missing\n", + __FUNCTION__); + return -EINVAL; + } + + /* seems aic94xx doesn't support */ + if (!rphy) { + printk("%s: can we send a smp request to a host?\n", + __FUNCTION__); + return -EINVAL; + } + + if (type != SAS_EDGE_EXPANDER_DEVICE && + type != SAS_FANOUT_EXPANDER_DEVICE) { + printk("%s: can we send a smp request to a device?\n", + __FUNCTION__); + return -EINVAL; + } + + dev = sas_find_dev_by_rphy(rphy); + if (!dev) { + printk("%s: fail to find a domain_device?\n", __FUNCTION__); + return -EINVAL; + } + + /* do we need to support multiple segments? */ + if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { + printk("%s: multiple segments req %u %u, rsp %u %u\n", + __FUNCTION__, req->bio->bi_vcnt, req->data_len, + rsp->bio->bi_vcnt, rsp->data_len); + return -EINVAL; + } + + ret = smp_execute_task(dev, bio_data(req->bio), req->data_len, + bio_data(rsp->bio), rsp->data_len); + + return ret; +} -- cgit v1.2.2 From 08547354c17a50a54906b7936d6ecb05ea39bedd Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 7 Jul 2007 18:11:35 +0900 Subject: [SCSI] libsas: kill unused smp_portal code Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 106 ------------------------------------- 1 file changed, 106 deletions(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index a81195354b9c..eca83e8d8c0d 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -37,14 +37,6 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, u8 *sas_addr, int include); static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); -#if 0 -/* FIXME: smp needs to migrate into the sas class */ -static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -static ssize_t smp_portal_write(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -#endif - /* ---------- SMP task management ---------- */ static void smp_task_timedout(unsigned long _task) @@ -1415,30 +1407,6 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) return 0; } -#if 0 -#define SMP_BIN_ATTR_NAME "smp_portal" - -static void sas_ex_smp_hook(struct domain_device *dev) -{ - struct expander_device *ex_dev = &dev->ex_dev; - struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr; - - memset(bin_attr, 0, sizeof(*bin_attr)); - - bin_attr->attr.name = SMP_BIN_ATTR_NAME; - bin_attr->attr.mode = 0600; - - bin_attr->size = 0; - bin_attr->private = NULL; - bin_attr->read = smp_portal_read; - bin_attr->write= smp_portal_write; - bin_attr->mmap = NULL; - - ex_dev->smp_portal_pid = -1; - init_MUTEX(&ex_dev->smp_sema); -} -#endif - /** * sas_discover_expander -- expander discovery * @ex: pointer to expander domain device @@ -1900,80 +1868,6 @@ out: return res; } -#if 0 -/* ---------- SMP portal ---------- */ - -static ssize_t smp_portal_write(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) -{ - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - - if (offs != 0) - return -EFBIG; - else if (size == 0) - return 0; - - down_interruptible(&ex->smp_sema); - if (ex->smp_req) - kfree(ex->smp_req); - ex->smp_req = kzalloc(size, GFP_USER); - if (!ex->smp_req) { - up(&ex->smp_sema); - return -ENOMEM; - } - memcpy(ex->smp_req, buf, size); - ex->smp_req_size = size; - ex->smp_portal_pid = current->pid; - up(&ex->smp_sema); - - return size; -} - -static ssize_t smp_portal_read(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) -{ - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - u8 *smp_resp; - int res = -EINVAL; - - /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact - * it should be 0. - */ - - down_interruptible(&ex->smp_sema); - if (!ex->smp_req || ex->smp_portal_pid != current->pid) - goto out; - - res = 0; - if (size == 0) - goto out; - - res = -ENOMEM; - smp_resp = alloc_smp_resp(size); - if (!smp_resp) - goto out; - res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size, - smp_resp, size); - if (!res) { - memcpy(buf, smp_resp, size); - res = size; - } - - kfree(smp_resp); -out: - kfree(ex->smp_req); - ex->smp_req = NULL; - ex->smp_req_size = 0; - ex->smp_portal_pid = -1; - up(&ex->smp_sema); - return res; -} -#endif - int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, struct request *req) { -- cgit v1.2.2 From b91421749a1840148d8c81637c03c0ace3f35269 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 22 Jul 2007 13:15:55 -0500 Subject: [SCSI] libsas: make ATA functions selectable by a config option Not everyone wants libsas automatically to pull in libata. This patch makes the behaviour configurable, so you can build libsas with or without ATA support. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/libsas/sas_expander.c') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index eca83e8d8c0d..b500f0c1449c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -535,6 +535,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) } +#ifdef CONFIG_SCSI_SAS_ATA + #define RPS_REQ_SIZE 16 #define RPS_RESP_SIZE 60 @@ -578,6 +580,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, kfree(rps_req); return res; } +#endif static void sas_ex_get_linkrate(struct domain_device *parent, struct domain_device *child, @@ -645,6 +648,7 @@ static struct domain_device *sas_ex_discover_end_dev( } sas_ex_get_linkrate(parent, child, phy); +#ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { child->dev_type = SATA_DEV; if (phy->attached_tproto & SAS_PROTO_STP) @@ -682,7 +686,9 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_ADDR(parent->sas_addr), phy_id, res); goto out_list_del; } - } else if (phy->attached_tproto & SAS_PROTO_SSP) { + } else +#endif + if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ @@ -710,6 +716,7 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", phy->attached_tproto, SAS_ADDR(parent->sas_addr), phy_id); + goto out_free; } list_add_tail(&child->siblings, &parent_ex->children); -- cgit v1.2.2