X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fscsi%2Flibsas%2Fsas_expander.c;h=3da02e4367884b4f8fcc1015811b55b0bf88a968;hb=cadbd4a5e36dde7e6c49b587b2c419103c0b7218;hp=e34442e405e8a8aa96e05f0f195b20958a9a2ddb;hpb=ec4883b015c3212f6f6d04fb2ff45f528492f598;p=pandora-kernel.git diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e34442e405e8..3da02e436788 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" @@ -36,12 +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 *, char *, loff_t, size_t); -static ssize_t smp_portal_write(struct kobject *, char *, loff_t, size_t); -#endif - /* ---------- SMP task management ---------- */ static void smp_task_timedout(unsigned long _task) @@ -101,7 +96,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, } wait_for_completion(&task->completion); - res = -ETASK; + res = -ECOMM; if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { SAS_DPRINTK("smp task timed out or aborted\n"); i->dft->lldd_abort_task(task); @@ -114,9 +109,19 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task->task_status.stat == SAM_GOOD) { res = 0; break; + } if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { + /* no error, but return the number of bytes of + * underrun */ + res = task->task_status.residual; + break; + } if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { + res = -EMSGSIZE; + break; } else { SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " - "status 0x%x\n", __FUNCTION__, + "status 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), task->task_status.resp, task->task_status.stat); @@ -218,6 +223,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, NULL); + /* 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; @@ -238,23 +273,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: @@ -490,14 +517,21 @@ static int sas_dev_present_in_domain(struct asd_sas_port *port, int sas_smp_get_phy_events(struct sas_phy *phy) { int res; + u8 *req; + u8 *resp; struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); struct domain_device *dev = sas_find_dev_by_rphy(rphy); - u8 *req = alloc_smp_req(RPEL_REQ_SIZE); - u8 *resp = kzalloc(RPEL_RESP_SIZE, GFP_KERNEL); - if (!resp) + req = alloc_smp_req(RPEL_REQ_SIZE); + if (!req) return -ENOMEM; + resp = alloc_smp_resp(RPEL_RESP_SIZE); + if (!resp) { + kfree(req); + return -ENOMEM; + } + req[1] = SMP_REPORT_PHY_ERR_LOG; req[9] = phy->number; @@ -518,6 +552,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 @@ -527,6 +563,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; @@ -537,9 +574,30 @@ 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; } +#endif static void sas_ex_get_linkrate(struct domain_device *parent, struct domain_device *child, @@ -607,9 +665,10 @@ static struct domain_device *sas_ex_discover_end_dev( } sas_ex_get_linkrate(parent, child, phy); - if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { +#ifdef CONFIG_SCSI_SAS_ATA + if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { child->dev_type = SATA_DEV; - if (phy->attached_tproto & SAS_PROTO_STP) + if (phy->attached_tproto & SAS_PROTOCOL_STP) child->tproto = phy->attached_tproto; if (phy->attached_sata_dev) child->tproto |= SATA_DEV; @@ -623,16 +682,30 @@ 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); + if (unlikely(!rphy)) + goto out_free; + sas_init_dev(child); + + child->rphy = rphy; + + spin_lock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock_irq(&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) { + } else +#endif + if (phy->attached_tproto & SAS_PROTOCOL_SSP) { child->dev_type = SAS_END_DEV; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ @@ -644,9 +717,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) { @@ -660,6 +733,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); @@ -759,9 +833,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) { @@ -1205,7 +1279,7 @@ static int sas_configure_present(struct domain_device *dev, int phy_id, goto out; } else if (res != SMP_RESP_FUNC_ACC) { SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x " - "result 0x%x\n", __FUNCTION__, + "result 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), phy_id, i, res); goto out; } @@ -1357,31 +1431,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.owner = THIS_MODULE; - 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 @@ -1843,74 +1892,57 @@ out: return res; } -#if 0 -/* ---------- SMP portal ---------- */ - -static ssize_t smp_portal_write(struct kobject *kobj, char *buf, loff_t offs, - size_t size) +int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) { - 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; + struct domain_device *dev; + int ret, type; + struct request *rsp = req->next_rq; - 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; + if (!rsp) { + printk("%s: space for a smp response is missing\n", + __func__); + return -EINVAL; } - memcpy(ex->smp_req, buf, size); - ex->smp_req_size = size; - ex->smp_portal_pid = current->pid; - up(&ex->smp_sema); - return size; -} + /* no rphy means no smp target support (ie aic94xx host) */ + if (!rphy) + return sas_smp_host_handler(shost, req, rsp); -static ssize_t smp_portal_read(struct kobject *kobj, 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; + type = rphy->identify.device_type; - /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact - * it should be 0. - */ + if (type != SAS_EDGE_EXPANDER_DEVICE && + type != SAS_FANOUT_EXPANDER_DEVICE) { + printk("%s: can we send a smp request to a device?\n", + __func__); + return -EINVAL; + } - down_interruptible(&ex->smp_sema); - if (!ex->smp_req || ex->smp_portal_pid != current->pid) - goto out; + dev = sas_find_dev_by_rphy(rphy); + if (!dev) { + printk("%s: fail to find a domain_device?\n", __func__); + return -EINVAL; + } - res = 0; - if (size == 0) - goto out; + /* 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", + __func__, req->bio->bi_vcnt, req->data_len, + rsp->bio->bi_vcnt, rsp->data_len); + return -EINVAL; + } - 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; + ret = smp_execute_task(dev, bio_data(req->bio), req->data_len, + bio_data(rsp->bio), rsp->data_len); + if (ret > 0) { + /* positive number is the untransferred residual */ + rsp->data_len = ret; + req->data_len = 0; + ret = 0; + } else if (ret == 0) { + rsp->data_len = 0; + req->data_len = 0; } - 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; + return ret; } -#endif