diff options
Diffstat (limited to 'drivers/scsi/scsi_transport_sas.c')
| -rw-r--r-- | drivers/scsi/scsi_transport_sas.c | 241 |
1 files changed, 191 insertions, 50 deletions
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 366609386be..c341f855fad 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -151,10 +151,22 @@ static struct { { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" }, { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" }, { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" }, + { SAS_LINK_RATE_12_0_GBPS, "12.0 Gbit" }, }; sas_bitfield_name_search(linkspeed, sas_linkspeed_names) sas_bitfield_name_set(linkspeed, sas_linkspeed_names) +static struct sas_end_device *sas_sdev_to_rdev(struct scsi_device *sdev) +{ + struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target); + struct sas_end_device *rdev; + + BUG_ON(rphy->identify.device_type != SAS_END_DEVICE); + + rdev = rphy_to_end_device(rphy); + return rdev; +} + static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, struct sas_rphy *rphy) { @@ -162,22 +174,16 @@ static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, int ret; int (*handler)(struct Scsi_Host *, struct sas_rphy *, struct request *); - while (!blk_queue_plugged(q)) { - req = elv_next_request(q); - if (!req) - break; - - blkdev_dequeue_request(req); - + while ((req = blk_fetch_request(q)) != NULL) { spin_unlock_irq(q->queue_lock); handler = to_sas_internal(shost->transportt)->f->smp_handler; ret = handler(shost, rphy, req); req->errors = ret; - spin_lock_irq(q->queue_lock); + blk_end_request_all(req, ret); - req->end_io(req, ret); + spin_lock_irq(q->queue_lock); } } @@ -207,7 +213,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) struct request_queue *q; int error; struct device *dev; - char namebuf[BUS_ID_SIZE]; + char namebuf[20]; const char *name; void (*release)(struct device *); @@ -219,7 +225,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) if (rphy) { q = blk_init_queue(sas_non_host_smp_request, NULL); dev = &rphy->dev; - name = dev->bus_id; + name = dev_name(dev); release = NULL; } else { q = blk_init_queue(sas_host_smp_request, NULL); @@ -360,6 +366,85 @@ void sas_remove_host(struct Scsi_Host *shost) } EXPORT_SYMBOL(sas_remove_host); +/** + * sas_tlr_supported - checking TLR bit in vpd 0x90 + * @sdev: scsi device struct + * + * Check Transport Layer Retries are supported or not. + * If vpd page 0x90 is present, TRL is supported. + * + */ +unsigned int +sas_tlr_supported(struct scsi_device *sdev) +{ + const int vpd_len = 32; + struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); + char *buffer = kzalloc(vpd_len, GFP_KERNEL); + int ret = 0; + + if (scsi_get_vpd_page(sdev, 0x90, buffer, vpd_len)) + goto out; + + /* + * Magic numbers: the VPD Protocol page (0x90) + * has a 4 byte header and then one entry per device port + * the TLR bit is at offset 8 on each port entry + * if we take the first port, that's at total offset 12 + */ + ret = buffer[12] & 0x01; + + out: + kfree(buffer); + rdev->tlr_supported = ret; + return ret; + +} +EXPORT_SYMBOL_GPL(sas_tlr_supported); + +/** + * sas_disable_tlr - setting TLR flags + * @sdev: scsi device struct + * + * Seting tlr_enabled flag to 0. + * + */ +void +sas_disable_tlr(struct scsi_device *sdev) +{ + struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); + + rdev->tlr_enabled = 0; +} +EXPORT_SYMBOL_GPL(sas_disable_tlr); + +/** + * sas_enable_tlr - setting TLR flags + * @sdev: scsi device struct + * + * Seting tlr_enabled flag 1. + * + */ +void sas_enable_tlr(struct scsi_device *sdev) +{ + unsigned int tlr_supported = 0; + tlr_supported = sas_tlr_supported(sdev); + + if (tlr_supported) { + struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); + + rdev->tlr_enabled = 1; + } + + return; +} +EXPORT_SYMBOL_GPL(sas_enable_tlr); + +unsigned int sas_is_tlr_enabled(struct scsi_device *sdev) +{ + struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); + return rdev->tlr_enabled; +} +EXPORT_SYMBOL_GPL(sas_is_tlr_enabled); /* * SAS Phy attributes @@ -531,6 +616,7 @@ do_sas_phy_reset(struct device *dev, size_t count, int hard_reset) error = i->f->phy_reset(phy, hard_reset); if (error) return error; + phy->enabled = 1; return count; }; @@ -568,9 +654,21 @@ sas_phy_linkerror_attr(running_disparity_error_count); sas_phy_linkerror_attr(loss_of_dword_sync_count); sas_phy_linkerror_attr(phy_reset_problem_count); +static int sas_phy_setup(struct transport_container *tc, struct device *dev, + struct device *cdev) +{ + struct sas_phy *phy = dev_to_phy(dev); + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_internal *i = to_sas_internal(shost->transportt); + + if (i->f->phy_setup) + i->f->phy_setup(phy); + + return 0; +} static DECLARE_TRANSPORT_CLASS(sas_phy_class, - "sas_phy", NULL, NULL, NULL); + "sas_phy", sas_phy_setup, NULL, NULL); static int sas_phy_match(struct attribute_container *cont, struct device *dev) { @@ -594,7 +692,11 @@ static int sas_phy_match(struct attribute_container *cont, struct device *dev) static void sas_phy_release(struct device *dev) { struct sas_phy *phy = dev_to_phy(dev); + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_internal *i = to_sas_internal(shost->transportt); + if (i->f->phy_release) + i->f->phy_release(phy); put_device(dev->parent); kfree(phy); } @@ -629,10 +731,10 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) INIT_LIST_HEAD(&phy->port_siblings); if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(phy->dev.bus_id, "phy-%d:%d:%d", shost->host_no, + dev_set_name(&phy->dev, "phy-%d:%d:%d", shost->host_no, rphy->scsi_target_id, number); } else - sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); + dev_set_name(&phy->dev, "phy-%d:%d", shost->host_no, number); transport_setup_device(&phy->dev); @@ -668,7 +770,7 @@ EXPORT_SYMBOL(sas_phy_add); * * Note: * This function must only be called on a PHY that has not - * sucessfully been added using sas_phy_add(). + * successfully been added using sas_phy_add(). */ void sas_phy_free(struct sas_phy *phy) { @@ -770,7 +872,7 @@ static void sas_port_create_link(struct sas_port *port, int res; res = sysfs_create_link(&port->dev.kobj, &phy->dev.kobj, - phy->dev.bus_id); + dev_name(&phy->dev)); if (res) goto err; res = sysfs_create_link(&phy->dev.kobj, &port->dev.kobj, "port"); @@ -785,7 +887,7 @@ err: static void sas_port_delete_link(struct sas_port *port, struct sas_phy *phy) { - sysfs_remove_link(&port->dev.kobj, phy->dev.bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(&phy->dev)); sysfs_remove_link(&phy->dev.kobj, "port"); } @@ -821,11 +923,11 @@ struct sas_port *sas_port_alloc(struct device *parent, int port_id) if (scsi_is_sas_expander_device(parent)) { struct sas_rphy *rphy = dev_to_rphy(parent); - sprintf(port->dev.bus_id, "port-%d:%d:%d", shost->host_no, - rphy->scsi_target_id, port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d:%d", shost->host_no, + rphy->scsi_target_id, port->port_identifier); } else - sprintf(port->dev.bus_id, "port-%d:%d", shost->host_no, - port->port_identifier); + dev_set_name(&port->dev, "port-%d:%d", shost->host_no, + port->port_identifier); transport_setup_device(&port->dev); @@ -898,7 +1000,7 @@ EXPORT_SYMBOL(sas_port_add); * * Note: * This function must only be called on a PORT that has not - * sucessfully been added using sas_port_add(). + * successfully been added using sas_port_add(). */ void sas_port_free(struct sas_port *port) { @@ -935,7 +1037,7 @@ void sas_port_delete(struct sas_port *port) if (port->is_backlink) { struct device *parent = port->dev.parent; - sysfs_remove_link(&port->dev.kobj, parent->bus_id); + sysfs_remove_link(&port->dev.kobj, dev_name(parent)); port->is_backlink = 0; } @@ -960,6 +1062,29 @@ int scsi_is_sas_port(const struct device *dev) EXPORT_SYMBOL(scsi_is_sas_port); /** + * sas_port_get_phy - try to take a reference on a port member + * @port: port to check + */ +struct sas_phy *sas_port_get_phy(struct sas_port *port) +{ + struct sas_phy *phy; + + mutex_lock(&port->phy_list_mutex); + if (list_empty(&port->phy_list)) + phy = NULL; + else { + struct list_head *ent = port->phy_list.next; + + phy = list_entry(ent, typeof(*phy), port_siblings); + get_device(&phy->dev); + } + mutex_unlock(&port->phy_list_mutex); + + return phy; +} +EXPORT_SYMBOL(sas_port_get_phy); + +/** * sas_port_add_phy - add another phy to a port to form a wide port * @port: port to add the phy to * @phy: phy to add @@ -984,7 +1109,8 @@ void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy) /* If this trips, you added a phy that was already * part of a different port */ if (unlikely(tmp != phy)) { - dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", phy->dev.bus_id); + dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", + dev_name(&phy->dev)); BUG(); } } else { @@ -1023,7 +1149,7 @@ void sas_port_mark_backlink(struct sas_port *port) return; port->is_backlink = 1; res = sysfs_create_link(&port->dev.kobj, &parent->kobj, - parent->bus_id); + dev_name(parent)); if (res) goto err; return; @@ -1147,15 +1273,10 @@ sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); int sas_read_port_mode_page(struct scsi_device *sdev) { char *buffer = kzalloc(BUF_SIZE, GFP_KERNEL), *msdata; - struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target); - struct sas_end_device *rdev; + struct sas_end_device *rdev = sas_sdev_to_rdev(sdev); struct scsi_mode_data mode_data; int res, error; - BUG_ON(rphy->identify.device_type != SAS_END_DEVICE); - - rdev = rphy_to_end_device(rphy); - if (!buffer) return -ENOMEM; @@ -1208,6 +1329,10 @@ sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout, "%d\n", int); sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout, "%d\n", int); +sas_end_dev_simple_attr(tlr_supported, tlr_supported, + "%d\n", int); +sas_end_dev_simple_attr(tlr_enabled, tlr_enabled, + "%d\n", int); static DECLARE_TRANSPORT_CLASS(sas_expander_class, "sas_expander", NULL, NULL, NULL); @@ -1367,11 +1492,12 @@ struct sas_rphy *sas_end_device_alloc(struct sas_port *parent) rdev->rphy.dev.release = sas_end_device_release; if (scsi_is_sas_expander_device(parent->dev.parent)) { struct sas_rphy *rphy = dev_to_rphy(parent->dev.parent); - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d:%d", - shost->host_no, rphy->scsi_target_id, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d:%d", + shost->host_no, rphy->scsi_target_id, + parent->port_identifier); } else - sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d", - shost->host_no, parent->port_identifier); + dev_set_name(&rdev->rphy.dev, "end_device-%d:%d", + shost->host_no, parent->port_identifier); rdev->rphy.identify.device_type = SAS_END_DEVICE; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1411,8 +1537,8 @@ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, mutex_lock(&sas_host->lock); rdev->rphy.scsi_target_id = sas_host->next_expander_id++; mutex_unlock(&sas_host->lock); - sprintf(rdev->rphy.dev.bus_id, "expander-%d:%d", - shost->host_no, rdev->rphy.scsi_target_id); + dev_set_name(&rdev->rphy.dev, "expander-%d:%d", + shost->host_no, rdev->rphy.scsi_target_id); rdev->rphy.identify.device_type = type; sas_rphy_initialize(&rdev->rphy); transport_setup_device(&rdev->rphy.dev); @@ -1445,7 +1571,7 @@ int sas_rphy_add(struct sas_rphy *rphy) transport_add_device(&rphy->dev); transport_configure_device(&rphy->dev); if (sas_bsg_initialize(shost, rphy)) - printk("fail to a bsg device %s\n", rphy->dev.bus_id); + printk("fail to a bsg device %s\n", dev_name(&rphy->dev)); mutex_lock(&sas_host->lock); @@ -1460,8 +1586,14 @@ int sas_rphy_add(struct sas_rphy *rphy) if (identify->device_type == SAS_END_DEVICE && rphy->scsi_target_id != -1) { - scsi_scan_target(&rphy->dev, 0, - rphy->scsi_target_id, SCAN_WILD_CARD, 0); + int lun; + + if (identify->target_port_protocols & SAS_PROTOCOL_SSP) + lun = SCAN_WILD_CARD; + else + lun = 0; + + scsi_scan_target(&rphy->dev, 0, rphy->scsi_target_id, lun, 0); } return 0; @@ -1476,7 +1608,7 @@ EXPORT_SYMBOL(sas_rphy_add); * * Note: * This function must only be called on a remote - * PHY that has not sucessfully been added using + * PHY that has not successfully been added using * sas_rphy_add() (or has been sas_rphy_remove()'d) */ void sas_rphy_free(struct sas_rphy *rphy) @@ -1489,8 +1621,6 @@ void sas_rphy_free(struct sas_rphy *rphy) list_del(&rphy->list); mutex_unlock(&sas_host->lock); - sas_bsg_remove(shost, rphy); - transport_destroy_device(dev); put_device(dev); @@ -1512,6 +1642,20 @@ sas_rphy_delete(struct sas_rphy *rphy) EXPORT_SYMBOL(sas_rphy_delete); /** + * sas_rphy_unlink - unlink SAS remote PHY + * @rphy: SAS remote phy to unlink from its parent port + * + * Removes port reference to an rphy + */ +void sas_rphy_unlink(struct sas_rphy *rphy) +{ + struct sas_port *parent = dev_to_sas_port(rphy->dev.parent); + + parent->rphy = NULL; +} +EXPORT_SYMBOL(sas_rphy_unlink); + +/** * sas_rphy_remove - remove SAS remote PHY * @rphy: SAS remote phy to remove * @@ -1521,7 +1665,6 @@ void sas_rphy_remove(struct sas_rphy *rphy) { struct device *dev = &rphy->dev; - struct sas_port *parent = dev_to_sas_port(dev->parent); switch (rphy->identify.device_type) { case SAS_END_DEVICE: @@ -1535,10 +1678,10 @@ sas_rphy_remove(struct sas_rphy *rphy) break; } + sas_rphy_unlink(rphy); + sas_bsg_remove(NULL, rphy); transport_remove_device(dev); device_del(dev); - - parent->rphy = NULL; } EXPORT_SYMBOL(sas_rphy_remove); @@ -1692,10 +1835,6 @@ sas_attach_transport(struct sas_function_template *ft) i->f = ft; count = 0; - SETUP_PORT_ATTRIBUTE(num_phys); - i->host_attrs[count] = NULL; - - count = 0; SETUP_PHY_ATTRIBUTE(initiator_port_protocols); SETUP_PHY_ATTRIBUTE(target_port_protocols); SETUP_PHY_ATTRIBUTE(device_type); @@ -1737,6 +1876,8 @@ sas_attach_transport(struct sas_function_template *ft) SETUP_END_DEV_ATTRIBUTE(end_dev_ready_led_meaning); SETUP_END_DEV_ATTRIBUTE(end_dev_I_T_nexus_loss_timeout); SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout); + SETUP_END_DEV_ATTRIBUTE(end_dev_tlr_supported); + SETUP_END_DEV_ATTRIBUTE(end_dev_tlr_enabled); i->end_dev_attrs[count] = NULL; count = 0; |
