diff options
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 13 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 37 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 103 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 43 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 145 |
5 files changed, 234 insertions, 107 deletions
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index f583193..54a5199 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -219,17 +219,20 @@ out_err2: /* ---------- Device registration and unregistration ---------- */ -static inline void sas_unregister_common_dev(struct domain_device *dev) +static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { sas_notify_lldd_dev_gone(dev); if (!dev->parent) dev->port->port_dev = NULL; else list_del_init(&dev->siblings); + + spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); + spin_unlock_irq(&port->dev_list_lock); } -void sas_unregister_dev(struct domain_device *dev) +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (dev->rphy) { sas_remove_children(&dev->rphy->dev); @@ -241,15 +244,15 @@ void sas_unregister_dev(struct domain_device *dev) kfree(dev->ex_dev.ex_phy); dev->ex_dev.ex_phy = NULL; } - sas_unregister_common_dev(dev); + sas_unregister_common_dev(port, dev); } void sas_unregister_domain_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node) - sas_unregister_dev(dev); + list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) + sas_unregister_dev(port, dev); port->port->rphy = NULL; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 16ad97d..1b831c5 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -199,6 +199,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->virtual = dr->virtual; phy->last_da_index = -1; + phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); + phy->phy->identify.device_type = phy->attached_dev_type; phy->phy->identify.initiator_port_protocols = phy->attached_iproto; phy->phy->identify.target_port_protocols = phy->attached_tproto; phy->phy->identify.phy_identifier = phy_id; @@ -329,6 +331,7 @@ static void ex_assign_report_general(struct domain_device *dev, dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.t2t_supp = rg->t2t_supp; dev->ex_dev.conf_route_table = rg->conf_route_table; dev->ex_dev.configuring = rg->configuring; memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); @@ -751,7 +754,10 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); child->rphy = NULL; + + spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); + spin_unlock_irq(&parent->port->dev_list_lock); out_free: sas_port_delete(phy->port); out_err: @@ -1133,15 +1139,17 @@ static void sas_print_parent_topology_bug(struct domain_device *child, }; struct domain_device *parent = child->parent; - sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx phy 0x%x " - "has %c:%c routing link!\n", + sas_printk("%s ex %016llx (T2T supp:%d) phy 0x%x <--> %s ex %016llx " + "(T2T supp:%d) phy 0x%x has %c:%c routing link!\n", ex_type[parent->dev_type], SAS_ADDR(parent->sas_addr), + parent->ex_dev.t2t_supp, parent_phy->phy_id, ex_type[child->dev_type], SAS_ADDR(child->sas_addr), + child->ex_dev.t2t_supp, child_phy->phy_id, ra_char[parent_phy->routing_attr], @@ -1238,10 +1246,15 @@ static int sas_check_parent_topology(struct domain_device *child) sas_print_parent_topology_bug(child, parent_phy, child_phy); res = -ENODEV; } - } else if (parent_phy->routing_attr == TABLE_ROUTING && - child_phy->routing_attr != SUBTRACTIVE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; + } else if (parent_phy->routing_attr == TABLE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING || + (child_phy->routing_attr == TABLE_ROUTING && + child_ex->t2t_supp && parent_ex->t2t_supp)) { + /* All good */; + } else { + sas_print_parent_topology_bug(child, parent_phy, child_phy); + res = -ENODEV; + } } break; case FANOUT_DEV: @@ -1729,7 +1742,7 @@ out: return res; } -static void sas_unregister_ex_tree(struct domain_device *dev) +static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev) { struct expander_device *ex = &dev->ex_dev; struct domain_device *child, *n; @@ -1738,11 +1751,11 @@ static void sas_unregister_ex_tree(struct domain_device *dev) child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(port, child); else - sas_unregister_dev(child); + sas_unregister_dev(port, child); } - sas_unregister_dev(dev); + sas_unregister_dev(port, dev); } static void sas_unregister_devs_sas_addr(struct domain_device *parent, @@ -1759,9 +1772,9 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, child->gone = 1; if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) - sas_unregister_ex_tree(child); + sas_unregister_ex_tree(parent->port, child); else - sas_unregister_dev(child); + sas_unregister_dev(parent->port, child); break; } } diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 04ad8dd..e1aa178 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -51,6 +51,91 @@ static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data, resp_data[15] = rphy->identify.target_port_protocols; } +/** + * to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od' + * @od: od bit to find + * @data: incoming bitstream (from frame) + * @index: requested data register index (from frame) + * @count: total number of registers in the bitstream (from frame) + * @bit: bit position of 'od' in the returned byte + * + * returns NULL if 'od' is not in 'data' + * + * From SFF-8485 v0.7: + * "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0) + * and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1). + * + * In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2) + * and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)." + * + * The general-purpose (raw-bitstream) RX registers have the same layout + * although 'od' is renamed 'id' for 'input data'. + * + * SFF-8489 defines the behavior of the LEDs in response to the 'od' values. + */ +static u8 *to_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count, u8 *bit) +{ + unsigned int reg; + u8 byte; + + /* gp registers start at index 1 */ + if (index == 0) + return NULL; + + index--; /* make index 0-based */ + if (od < index * 32) + return NULL; + + od -= index * 32; + reg = od >> 5; + + if (reg >= count) + return NULL; + + od &= (1 << 5) - 1; + byte = 3 - (od >> 3); + *bit = od & ((1 << 3) - 1); + + return &data[reg * 4 + byte]; +} + +int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count) +{ + u8 *byte; + u8 bit; + + byte = to_sas_gpio_gp_bit(od, data, index, count, &bit); + if (!byte) + return -1; + + return (*byte >> bit) & 1; +} +EXPORT_SYMBOL(try_test_sas_gpio_gp_bit); + +static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, + u8 reg_type, u8 reg_index, u8 reg_count, + u8 *req_data) +{ + struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); + int written; + + if (i->dft->lldd_write_gpio == NULL) { + resp_data[2] = SMP_RESP_FUNC_UNK; + return 0; + } + + written = i->dft->lldd_write_gpio(sas_ha, reg_type, reg_index, + reg_count, req_data); + + if (written < 0) { + resp_data[2] = SMP_RESP_FUNC_FAILED; + written = 0; + } else + resp_data[2] = SMP_RESP_FUNC_ACC; + + return written; +} + static void sas_report_phy_sata(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 phy_id) { @@ -230,9 +315,23 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, /* Can't implement; hosts have no routes */ break; - case SMP_WRITE_GPIO_REG: - /* FIXME: need GPIO support in the transport class */ + case SMP_WRITE_GPIO_REG: { + /* SFF-8485 v0.7 */ + const int base_frame_size = 11; + int to_write = req_data[4]; + + if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || + req->resid_len < base_frame_size + to_write * 4) { + resp_data[2] = SMP_RESP_INV_FRM_LEN; + break; + } + + to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], + req_data[3], to_write, &req_data[8]); + req->resid_len -= base_frame_size + to_write * 4; + rsp->resid_len -= 8; break; + } case SMP_CONF_ROUTE_INFO: /* Can't implement; hosts have no routes */ diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 2dc55343..d81c3b1 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -37,7 +37,32 @@ #include "../scsi_sas_internal.h" -struct kmem_cache *sas_task_cache; +static struct kmem_cache *sas_task_cache; + +struct sas_task *sas_alloc_task(gfp_t flags) +{ + struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags); + + if (task) { + INIT_LIST_HEAD(&task->list); + spin_lock_init(&task->task_state_lock); + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_timer(&task->timer); + init_completion(&task->completion); + } + + return task; +} +EXPORT_SYMBOL_GPL(sas_alloc_task); + +void sas_free_task(struct sas_task *task) +{ + if (task) { + BUG_ON(!list_empty(&task->list)); + kmem_cache_free(sas_task_cache, task); + } +} +EXPORT_SYMBOL_GPL(sas_free_task); /*------------ SAS addr hash -----------*/ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) @@ -152,10 +177,15 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) static int sas_get_linkerrors(struct sas_phy *phy) { - if (scsi_is_sas_phy_local(phy)) - /* FIXME: we have no local phy stats - * gathering at this time */ - return -EINVAL; + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); + } return sas_smp_get_phy_events(phy); } @@ -293,8 +323,7 @@ EXPORT_SYMBOL_GPL(sas_domain_release_transport); static int __init sas_class_init(void) { - sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task), - 0, SLAB_HWCACHE_ALIGN, NULL); + sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN); if (!sas_task_cache) return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index eeba76c..b2c4a77 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -182,79 +182,56 @@ int sas_queue_up(struct sas_task *task) return 0; } -/** - * sas_queuecommand -- Enqueue a command for processing - * @parameters: See SCSI Core documentation - * - * Note: XXX: Remove the host unlock/lock pair when SCSI Core can - * call us without holding an IRQ spinlock... - */ -static int sas_queuecommand_lck(struct scsi_cmnd *cmd, - void (*scsi_done)(struct scsi_cmnd *)) - __releases(host->host_lock) - __acquires(dev->sata_dev.ap->lock) - __releases(dev->sata_dev.ap->lock) - __acquires(host->host_lock) +int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { - int res = 0; - struct domain_device *dev = cmd_to_domain_dev(cmd); - struct Scsi_Host *host = cmd->device->host; struct sas_internal *i = to_sas_internal(host->transportt); + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_ha_struct *sas_ha = dev->port->ha; + struct sas_task *task; + int res = 0; - spin_unlock_irq(host->host_lock); + /* If the device fell off, no sense in issuing commands */ + if (dev->gone) { + cmd->result = DID_BAD_TARGET << 16; + goto out_done; + } - { - struct sas_ha_struct *sas_ha = dev->port->ha; - struct sas_task *task; - - /* If the device fell off, no sense in issuing commands */ - if (dev->gone) { - cmd->result = DID_BAD_TARGET << 16; - scsi_done(cmd); - goto out; - } + if (dev_is_sata(dev)) { + unsigned long flags; - if (dev_is_sata(dev)) { - unsigned long flags; + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + res = ata_sas_queuecmd(cmd, dev->sata_dev.ap); + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + return res; + } - spin_lock_irqsave(dev->sata_dev.ap->lock, flags); - res = ata_sas_queuecmd(cmd, dev->sata_dev.ap); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); - goto out; - } + task = sas_create_task(cmd, dev, GFP_ATOMIC); + if (!task) + return SCSI_MLQUEUE_HOST_BUSY; - res = -ENOMEM; - task = sas_create_task(cmd, dev, GFP_ATOMIC); - if (!task) - goto out; + /* Queue up, Direct Mode or Task Collector Mode. */ + if (sas_ha->lldd_max_execute_num < 2) + res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + else + res = sas_queue_up(task); - cmd->scsi_done = scsi_done; - /* Queue up, Direct Mode or Task Collector Mode. */ - if (sas_ha->lldd_max_execute_num < 2) - res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); - else - res = sas_queue_up(task); + if (res) + goto out_free_task; + return 0; - /* Examine */ - if (res) { - SAS_DPRINTK("lldd_execute_task returned: %d\n", res); - ASSIGN_SAS_TASK(cmd, NULL); - sas_free_task(task); - if (res == -SAS_QUEUE_FULL) { - cmd->result = DID_SOFT_ERROR << 16; /* retry */ - res = 0; - scsi_done(cmd); - } - goto out; - } - } -out: - spin_lock_irq(host->host_lock); - return res; +out_free_task: + SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + ASSIGN_SAS_TASK(cmd, NULL); + sas_free_task(task); + if (res == -SAS_QUEUE_FULL) + cmd->result = DID_SOFT_ERROR << 16; /* retry */ + else + cmd->result = DID_ERROR << 16; +out_done: + cmd->scsi_done(cmd); + return 0; } -DEF_SCSI_QCMD(sas_queuecommand) - static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) { struct sas_task *task = TO_SAS_TASK(cmd); @@ -784,8 +761,7 @@ int sas_target_alloc(struct scsi_target *starget) return 0; } -#define SAS_DEF_QD 32 -#define SAS_MAX_QD 64 +#define SAS_DEF_QD 256 int sas_slave_configure(struct scsi_device *scsi_dev) { @@ -825,34 +801,41 @@ void sas_slave_destroy(struct scsi_device *scsi_dev) struct domain_device *dev = sdev_to_domain_dev(scsi_dev); if (dev_is_sata(dev)) - dev->sata_dev.ap->link.device[0].class = ATA_DEV_NONE; + sas_to_ata_dev(dev)->class = ATA_DEV_NONE; } -int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth, - int reason) +int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { - int res = min(new_depth, SAS_MAX_QD); + struct domain_device *dev = sdev_to_domain_dev(sdev); - if (reason != SCSI_QDEPTH_DEFAULT) + if (dev_is_sata(dev)) + return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth, + reason); + + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + case SCSI_QDEPTH_RAMP_UP: + if (!sdev->tagged_supported) + depth = 1; + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + default: return -EOPNOTSUPP; - - if (scsi_dev->tagged_supported) - scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), - res); - else { - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - sas_printk("device %llx LUN %x queue depth changed to 1\n", - SAS_ADDR(dev->sas_addr), - scsi_dev->lun); - scsi_adjust_queue_depth(scsi_dev, 0, 1); - res = 1; } - return res; + return depth; } int sas_change_queue_type(struct scsi_device *scsi_dev, int qt) { + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + + if (dev_is_sata(dev)) + return -EINVAL; + if (!scsi_dev->tagged_supported) return 0; |