diff options
Diffstat (limited to 'drivers/scsi/libsas')
| -rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 245 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 49 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 351 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 4 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_init.c | 24 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 124 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 14 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_port.c | 21 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 79 |
9 files changed, 556 insertions, 355 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5e80225b5308..bcecb4911da9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -125,7 +125,7 @@ static void sas_ata_task_done(struct sas_task *task) } else { link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]); if (unlikely(link->eh_info.err_mask)) - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; } } else { ac = sas_to_ata_err(stat); @@ -136,7 +136,7 @@ static void sas_ata_task_done(struct sas_task *task) qc->err_mask = ac; } else { link->eh_info.err_mask |= AC_ERR_DEV; - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; } dev->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */ @@ -162,7 +162,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct domain_device *dev = ap->private_data; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *host = sas_ha->core.shost; + struct Scsi_Host *host = sas_ha->shost; struct sas_internal *i = to_sas_internal(host->transportt); /* TODO: we should try to remove that unlock */ @@ -201,12 +201,14 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->data_dir = qc->dma_dir; } task->scatter = qc->sg; - task->ata_task.retry_count = 1; qc->lldd_task = task; task->ata_task.use_ncq = ata_is_ncq(qc->tf.protocol); task->ata_task.dma_xfer = ata_is_dma(qc->tf.protocol); + if (qc->flags & ATA_QCFLAG_RESULT_TF) + task->ata_task.return_fis_on_success = 1; + if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, task); @@ -226,22 +228,31 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) return ret; } -static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) +static void sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) { struct domain_device *dev = qc->ap->private_data; ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf); - return true; } static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) { - return to_sas_internal(dev->port->ha->core.shost->transportt); + return to_sas_internal(dev->port->ha->shost->transportt); } -static int sas_get_ata_command_set(struct domain_device *dev); +static int sas_get_ata_command_set(struct domain_device *dev) +{ + struct ata_taskfile tf; + + if (dev->dev_type == SAS_SATA_PENDING) + return ATA_DEV_UNKNOWN; + + ata_tf_from_fis(dev->frame_rcvd, &tf); + + return ata_dev_classify(&tf); +} -int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) +static int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) { if (phy->attached_tproto & SAS_PROTOCOL_STP) dev->tproto = phy->attached_tproto; @@ -476,7 +487,7 @@ static void sas_ata_internal_abort(struct sas_task *task) static void sas_ata_post_internal(struct ata_queued_cmd *qc) { - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { @@ -548,34 +559,22 @@ static int sas_ata_prereset(struct ata_link *link, unsigned long deadline) } static struct ata_port_operations sas_sata_ops = { - .prereset = sas_ata_prereset, - .hardreset = sas_ata_hard_reset, + .reset.prereset = sas_ata_prereset, + .reset.hardreset = sas_ata_hard_reset, .error_handler = ata_std_error_handler, .post_internal_cmd = sas_ata_post_internal, .qc_defer = ata_std_qc_defer, - .qc_prep = ata_noop_qc_prep, .qc_issue = sas_ata_qc_issue, .qc_fill_rtf = sas_ata_qc_fill_rtf, - .port_start = ata_sas_port_start, - .port_stop = ata_sas_port_stop, .set_dmamode = sas_ata_set_dmamode, .sched_eh = sas_ata_sched_eh, .end_eh = sas_ata_end_eh, }; -static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | - ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &sas_sata_ops -}; - int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; - struct Scsi_Host *shost = ha->core.shost; + struct Scsi_Host *shost = ha->shost; struct ata_host *ata_host; struct ata_port *ap; int rc; @@ -588,31 +587,35 @@ int sas_ata_init(struct domain_device *found_dev) ata_host_init(ata_host, ha->dev, &sas_sata_ops); - ap = ata_sas_port_alloc(ata_host, &sata_port_info, shost); + ap = ata_port_alloc(ata_host); if (!ap) { - pr_err("ata_sas_port_alloc failed.\n"); + pr_err("ata_port_alloc failed.\n"); rc = -ENODEV; goto free_host; } + ap->port_no = 0; + ap->pio_mask = ATA_PIO4; + ap->mwdma_mask = ATA_MWDMA2; + ap->udma_mask = ATA_UDMA6; + ap->flags |= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | + ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX; + ap->ops = &sas_sata_ops; ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - rc = ata_sas_port_init(ap); - if (rc) - goto destroy_port; - rc = ata_sas_tport_add(ata_host->dev, ap); + rc = ata_tport_add(ata_host->dev, ap); if (rc) - goto destroy_port; + goto free_port; found_dev->sata_dev.ata_host = ata_host; found_dev->sata_dev.ap = ap; return 0; -destroy_port: - ata_sas_port_destroy(ap); +free_port: + ata_port_free(ap); free_host: ata_host_put(ata_host); return rc; @@ -631,26 +634,12 @@ void sas_ata_task_abort(struct sas_task *task) /* Internal command, fake a timeout and complete. */ qc->flags &= ~ATA_QCFLAG_ACTIVE; - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; qc->err_mask |= AC_ERR_TIMEOUT; waiting = qc->private_data; complete(waiting); } -static int sas_get_ata_command_set(struct domain_device *dev) -{ - struct dev_to_host_fis *fis = - (struct dev_to_host_fis *) dev->frame_rcvd; - struct ata_taskfile tf; - - if (dev->dev_type == SAS_SATA_PENDING) - return ATA_DEV_UNKNOWN; - - ata_tf_from_fis((const u8 *)fis, &tf); - - return ata_dev_classify(&tf); -} - void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; @@ -660,7 +649,7 @@ void sas_probe_sata(struct asd_sas_port *port) if (!dev_is_sata(dev)) continue; - ata_sas_async_probe(dev->sata_dev.ap); + ata_port_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -679,6 +668,68 @@ void sas_probe_sata(struct asd_sas_port *port) } +int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + struct sas_rphy *rphy; + int ret; + + if (child->linkrate > parent->min_linkrate) { + struct sas_phy *cphy = child->phy; + enum sas_linkrate min_prate = cphy->minimum_linkrate, + parent_min_lrate = parent->min_linkrate, + min_linkrate = (min_prate > parent_min_lrate) ? + parent_min_lrate : 0; + struct sas_phy_linkrates rates = { + .maximum_linkrate = parent->min_linkrate, + .minimum_linkrate = min_linkrate, + }; + + pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n", + SAS_ADDR(child->sas_addr), phy_id); + ret = sas_smp_phy_control(parent, phy_id, + PHY_FUNC_LINK_RESET, &rates); + if (ret) { + pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n", + SAS_ADDR(child->sas_addr), phy_id, ret); + return ret; + } + pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n", + SAS_ADDR(child->sas_addr), phy_id); + child->linkrate = child->min_linkrate; + } + ret = sas_get_ata_info(child, phy); + if (ret) + return ret; + + sas_init_dev(child); + ret = sas_ata_init(child); + if (ret) + return ret; + + rphy = sas_end_device_alloc(phy->port); + if (!rphy) + return -ENOMEM; + + rphy->identify.phy_identifier = phy_id; + child->rphy = rphy; + get_device(&rphy->dev); + + list_add_tail(&child->disco_list_node, &parent->port->disco_list); + + ret = sas_discover_sata(child); + if (ret) { + pr_notice("sas_discover_sata() for device %16llx at %016llx:%02d returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, ret); + sas_rphy_free(child->rphy); + list_del(&child->disco_list_node); + return ret; + } + + return 0; +} + static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) { struct domain_device *dev, *n; @@ -765,7 +816,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) struct sas_ha_struct *ha = dev->port->ha; sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); - ata_scsi_port_error_handler(ha->core.shost, ap); + ata_scsi_port_error_handler(ha->shost, ap); sas_put_device(dev); } @@ -876,13 +927,7 @@ EXPORT_SYMBOL_GPL(sas_ata_schedule_reset); void sas_ata_wait_eh(struct domain_device *dev) { - struct ata_port *ap; - - if (!dev_is_sata(dev)) - return; - - ap = dev->sata_dev.ap; - ata_port_wait_eh(ap); + ata_port_wait_eh(dev->sata_dev.ap); } void sas_ata_device_link_abort(struct domain_device *device, bool force_reset) @@ -910,3 +955,87 @@ int sas_execute_ata_cmd(struct domain_device *device, u8 *fis, int force_phy_id) force_phy_id, &tmf_task); } EXPORT_SYMBOL_GPL(sas_execute_ata_cmd); + +static ssize_t sas_ncq_prio_supported_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool supported; + int rc; + + rc = ata_ncq_prio_supported(ddev->sata_dev.ap, sdev, &supported); + if (rc) + return rc; + + return sysfs_emit(buf, "%d\n", supported); +} + +static struct device_attribute dev_attr_sas_ncq_prio_supported = + __ATTR(ncq_prio_supported, S_IRUGO, sas_ncq_prio_supported_show, NULL); + +static ssize_t sas_ncq_prio_enable_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool enabled; + int rc; + + rc = ata_ncq_prio_enabled(ddev->sata_dev.ap, sdev, &enabled); + if (rc) + return rc; + + return sysfs_emit(buf, "%d\n", enabled); +} + +static ssize_t sas_ncq_prio_enable_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool enable; + int rc; + + rc = kstrtobool(buf, &enable); + if (rc) + return rc; + + rc = ata_ncq_prio_enable(ddev->sata_dev.ap, sdev, enable); + if (rc) + return rc; + + return len; +} + +static struct device_attribute dev_attr_sas_ncq_prio_enable = + __ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR, + sas_ncq_prio_enable_show, sas_ncq_prio_enable_store); + +static struct attribute *sas_ata_sdev_attrs[] = { + &dev_attr_sas_ncq_prio_supported.attr, + &dev_attr_sas_ncq_prio_enable.attr, + NULL +}; + +static umode_t sas_ata_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct scsi_device *sdev = to_scsi_device(dev); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + + if (!dev_is_sata(ddev)) + return 0; + + return attr->mode; +} + +const struct attribute_group sas_ata_sdev_attr_group = { + .attrs = sas_ata_sdev_attrs, + .is_visible = sas_ata_attr_is_visible, +}; +EXPORT_SYMBOL_GPL(sas_ata_sdev_attr_group); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index d5bc1314c341..b07062db50b2 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -170,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) { int res = 0; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_found) @@ -192,7 +192,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) void sas_notify_lldd_dev_gone(struct domain_device *dev) { struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_gone) @@ -234,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work) struct domain_device *dev; struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; - struct Scsi_Host *shost = port->ha->core.shost; + struct Scsi_Host *shost = port->ha->shost; struct sas_internal *si = to_sas_internal(shost->transportt); clear_bit(DISCE_SUSPEND, &port->disc.pending); @@ -275,7 +275,7 @@ static void sas_resume_devices(struct work_struct *work) * * See comment in sas_discover_sata(). */ -int sas_discover_end_dev(struct domain_device *dev) +static int sas_discover_end_dev(struct domain_device *dev) { return sas_notify_lldd_dev_found(dev); } @@ -300,8 +300,8 @@ void sas_free_device(struct kref *kref) kfree(dev->ex_dev.ex_phy); if (dev_is_sata(dev) && dev->sata_dev.ap) { - ata_sas_tport_delete(dev->sata_dev.ap); - ata_sas_port_destroy(dev->sata_dev.ap); + ata_tport_delete(dev->sata_dev.ap); + ata_port_free(dev->sata_dev.ap); ata_host_put(dev->sata_dev.ata_host); dev->sata_dev.ata_host = NULL; dev->sata_dev.ap = NULL; @@ -360,6 +360,33 @@ static void sas_destruct_ports(struct asd_sas_port *port) } } +static bool sas_abort_cmd(struct request *req, void *data) +{ + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); + struct domain_device *dev = data; + + if (dev == cmd_to_domain_dev(cmd)) + blk_abort_request(req); + return true; +} + +static void sas_abort_device_scsi_cmds(struct domain_device *dev) +{ + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *shost = sas_ha->shost; + + if (dev_is_expander(dev->dev_type)) + return; + + /* + * For removed device with active IOs, the user space applications have + * to spend very long time waiting for the timeout. This is not + * necessary because a removed device will not return the IOs. + * Abort the inflight IOs here so that EH can be quickly kicked in. + */ + blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev); +} + void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (!test_bit(SAS_DEV_DESTROY, &dev->state) && @@ -372,12 +399,14 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) } if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + if (test_bit(SAS_DEV_GONE, &dev->state)) + sas_abort_device_scsi_cmds(dev); sas_rphy_unlink(dev->rphy); list_move_tail(&dev->disco_list_node, &port->destroy_list); } } -void sas_unregister_domain_devices(struct asd_sas_port *port, int gone) +void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone) { struct domain_device *dev, *n; @@ -455,14 +484,8 @@ static void sas_discover_domain(struct work_struct *work) break; case SAS_SATA_DEV: case SAS_SATA_PM: -#ifdef CONFIG_SCSI_SAS_ATA error = sas_discover_sata(dev); break; -#else - pr_notice("ATA device seen but CONFIG_SCSI_SAS_ATA=N so cannot attach\n"); - fallthrough; -#endif - /* Fall through - only for the #else condition above. */ default: error = -ENXIO; pr_err("unhandled device %d\n", dev->dev_type); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index a04cad620e93..d953225f6cc2 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -11,7 +11,7 @@ #include <linux/scatterlist.h> #include <linux/blkdev.h> #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "sas_internal.h" @@ -26,6 +26,28 @@ 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); +static void sas_port_add_ex_phy(struct sas_port *port, struct ex_phy *ex_phy) +{ + sas_port_add_phy(port, ex_phy->phy); + ex_phy->port = port; + ex_phy->phy_state = PHY_DEVICE_DISCOVERED; +} + +static void sas_ex_add_parent_port(struct domain_device *dev, int phy_id) +{ + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; + + if (!ex->parent_port) { + ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); + /* FIXME: error handling */ + BUG_ON(!ex->parent_port); + BUG_ON(sas_port_add(ex->parent_port)); + sas_port_mark_backlink(ex->parent_port); + } + sas_port_add_ex_phy(ex->parent_port, ex_phy); +} + /* ---------- SMP task management ---------- */ /* Give it some long enough timeout. In seconds. */ @@ -37,7 +59,7 @@ static int smp_execute_task_sg(struct domain_device *dev, int res, retry; struct sas_task *task = NULL; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); struct sas_ha_struct *ha = dev->port->ha; pm_runtime_get_sync(ha->dev); @@ -67,7 +89,7 @@ static int smp_execute_task_sg(struct domain_device *dev, res = i->dft->lldd_execute_task(task, GFP_KERNEL); if (res) { - del_timer_sync(&task->slow_task->timer); + timer_delete_sync(&task->slow_task->timer); pr_notice("executing SMP task failed:%d\n", res); break; } @@ -135,7 +157,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, static inline void *alloc_smp_req(int size) { - u8 *p = kzalloc(size, GFP_KERNEL); + u8 *p = kzalloc(ALIGN(size, ARCH_DMA_MINALIGN), GFP_KERNEL); if (p) p[0] = SMP_REQUEST; return p; @@ -239,8 +261,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, /* help some expanders that fail to zero sas_address in the 'no * device' case */ - if (phy->attached_dev_type == SAS_PHY_UNUSED || - phy->linkrate < SAS_LINK_RATE_1_5_GBPS) + if (phy->attached_dev_type == SAS_PHY_UNUSED) memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); else memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); @@ -751,13 +772,46 @@ static void sas_ex_get_linkrate(struct domain_device *parent, child->pathways = min(child->pathways, parent->pathways); } +static int sas_ex_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + struct sas_rphy *rphy; + int res; + + child->dev_type = SAS_END_DEVICE; + rphy = sas_end_device_alloc(phy->port); + if (!rphy) + return -ENOMEM; + + child->tproto = phy->attached_tproto; + sas_init_dev(child); + + child->rphy = rphy; + get_device(&rphy->dev); + rphy->identify.phy_identifier = phy_id; + sas_fill_in_rphy(child, rphy); + + list_add_tail(&child->disco_list_node, &parent->port->disco_list); + + res = sas_notify_lldd_dev_found(child); + if (res) { + pr_notice("notify lldd for device %016llx at %016llx:%02d returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, res); + sas_rphy_free(child->rphy); + list_del(&child->disco_list_node); + return res; + } + + return 0; +} + static struct domain_device *sas_ex_discover_end_dev( struct domain_device *parent, int phy_id) { struct expander_device *parent_ex = &parent->ex_dev; struct ex_phy *phy = &parent_ex->ex_phy[phy_id]; struct domain_device *child = NULL; - struct sas_rphy *rphy; int res; if (phy->attached_sata_host || phy->attached_sata_ps) @@ -785,99 +839,23 @@ static struct domain_device *sas_ex_discover_end_dev( sas_ex_get_linkrate(parent, child, phy); sas_device_set_phy(child, phy->port); -#ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { - if (child->linkrate > parent->min_linkrate) { - struct sas_phy *cphy = child->phy; - enum sas_linkrate min_prate = cphy->minimum_linkrate, - parent_min_lrate = parent->min_linkrate, - min_linkrate = (min_prate > parent_min_lrate) ? - parent_min_lrate : 0; - struct sas_phy_linkrates rates = { - .maximum_linkrate = parent->min_linkrate, - .minimum_linkrate = min_linkrate, - }; - int ret; - - pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n", - SAS_ADDR(child->sas_addr), phy_id); - ret = sas_smp_phy_control(parent, phy_id, - PHY_FUNC_LINK_RESET, &rates); - if (ret) { - pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n", - SAS_ADDR(child->sas_addr), phy_id, ret); - goto out_free; - } - pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n", - SAS_ADDR(child->sas_addr), phy_id); - child->linkrate = child->min_linkrate; - } - res = sas_get_ata_info(child, phy); - if (res) - goto out_free; - - sas_init_dev(child); - res = sas_ata_init(child); - if (res) - goto out_free; - rphy = sas_end_device_alloc(phy->port); - if (!rphy) - goto out_free; - rphy->identify.phy_identifier = phy_id; - - child->rphy = rphy; - get_device(&rphy->dev); - - list_add_tail(&child->disco_list_node, &parent->port->disco_list); - - res = sas_discover_sata(child); - if (res) { - pr_notice("sas_discover_sata() for device %16llx at %016llx:%02d returned 0x%x\n", - SAS_ADDR(child->sas_addr), - SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_list_del; - } - } else -#endif - if (phy->attached_tproto & SAS_PROTOCOL_SSP) { - child->dev_type = SAS_END_DEVICE; - rphy = sas_end_device_alloc(phy->port); - /* FIXME: error handling */ - if (unlikely(!rphy)) - goto out_free; - child->tproto = phy->attached_tproto; - sas_init_dev(child); - - child->rphy = rphy; - get_device(&rphy->dev); - rphy->identify.phy_identifier = phy_id; - sas_fill_in_rphy(child, rphy); - - list_add_tail(&child->disco_list_node, &parent->port->disco_list); - - res = sas_discover_end_dev(child); - if (res) { - pr_notice("sas_discover_end_dev() for device %016llx at %016llx:%02d returned 0x%x\n", - SAS_ADDR(child->sas_addr), - SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_list_del; - } + res = sas_ata_add_dev(parent, phy, child, phy_id); + } else if (phy->attached_tproto & SAS_PROTOCOL_SSP) { + res = sas_ex_add_dev(parent, phy, child, phy_id); } else { pr_notice("target proto 0x%x at %016llx:0x%x not handled\n", phy->attached_tproto, SAS_ADDR(parent->sas_addr), phy_id); - goto out_free; + res = -ENODEV; } + if (res) + goto out_free; + list_add_tail(&child->siblings, &parent_ex->children); return child; - out_list_del: - sas_rphy_free(child->rphy); - list_del(&child->disco_list_node); - 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: @@ -900,9 +878,7 @@ static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id) if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr, SAS_ADDR_SIZE) && ephy->port) { - sas_port_add_phy(ephy->port, phy->phy); - phy->port = ephy->port; - phy->phy_state = PHY_DEVICE_DISCOVERED; + sas_port_add_ex_phy(ephy->port, phy); return true; } } @@ -1006,11 +982,11 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) /* Parent and domain coherency */ if (!dev->parent && sas_phy_match_port_addr(dev->port, ex_phy)) { - sas_add_parent_port(dev, phy_id); + sas_ex_add_parent_port(dev, phy_id); return 0; } if (dev->parent && sas_phy_match_dev_addr(dev->parent, ex_phy)) { - sas_add_parent_port(dev, phy_id); + sas_ex_add_parent_port(dev, phy_id); if (ex_phy->routing_attr == TABLE_ROUTING) sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1); return 0; @@ -1241,37 +1217,37 @@ static void sas_print_parent_topology_bug(struct domain_device *child, sas_route_char(child, child_phy)); } +static bool sas_eeds_valid(struct domain_device *parent, + struct domain_device *child) +{ + struct sas_discovery *disc = &parent->port->disc; + + return (SAS_ADDR(disc->eeds_a) == SAS_ADDR(parent->sas_addr) || + SAS_ADDR(disc->eeds_a) == SAS_ADDR(child->sas_addr)) && + (SAS_ADDR(disc->eeds_b) == SAS_ADDR(parent->sas_addr) || + SAS_ADDR(disc->eeds_b) == SAS_ADDR(child->sas_addr)); +} + static int sas_check_eeds(struct domain_device *child, - struct ex_phy *parent_phy, - struct ex_phy *child_phy) + struct ex_phy *parent_phy, + struct ex_phy *child_phy) { int res = 0; struct domain_device *parent = child->parent; + struct sas_discovery *disc = &parent->port->disc; - if (SAS_ADDR(parent->port->disc.fanout_sas_addr) != 0) { + if (SAS_ADDR(disc->fanout_sas_addr) != 0) { res = -ENODEV; pr_warn("edge ex %016llx phy S:%02d <--> edge ex %016llx phy S:%02d, while there is a fanout ex %016llx\n", SAS_ADDR(parent->sas_addr), parent_phy->phy_id, SAS_ADDR(child->sas_addr), child_phy->phy_id, - SAS_ADDR(parent->port->disc.fanout_sas_addr)); - } else if (SAS_ADDR(parent->port->disc.eeds_a) == 0) { - memcpy(parent->port->disc.eeds_a, parent->sas_addr, - SAS_ADDR_SIZE); - memcpy(parent->port->disc.eeds_b, child->sas_addr, - SAS_ADDR_SIZE); - } else if (((SAS_ADDR(parent->port->disc.eeds_a) == - SAS_ADDR(parent->sas_addr)) || - (SAS_ADDR(parent->port->disc.eeds_a) == - SAS_ADDR(child->sas_addr))) - && - ((SAS_ADDR(parent->port->disc.eeds_b) == - SAS_ADDR(parent->sas_addr)) || - (SAS_ADDR(parent->port->disc.eeds_b) == - SAS_ADDR(child->sas_addr)))) - ; - else { + SAS_ADDR(disc->fanout_sas_addr)); + } else if (SAS_ADDR(disc->eeds_a) == 0) { + memcpy(disc->eeds_a, parent->sas_addr, SAS_ADDR_SIZE); + memcpy(disc->eeds_b, child->sas_addr, SAS_ADDR_SIZE); + } else if (!sas_eeds_valid(parent, child)) { res = -ENODEV; pr_warn("edge ex %016llx phy%02d <--> edge ex %016llx phy%02d link forms a third EEDS!\n", SAS_ADDR(parent->sas_addr), @@ -1283,26 +1259,67 @@ static int sas_check_eeds(struct domain_device *child, return res; } -/* Here we spill over 80 columns. It is intentional. - */ -static int sas_check_parent_topology(struct domain_device *child) +static int sas_check_edge_expander_topo(struct domain_device *child, + struct ex_phy *parent_phy) +{ + struct expander_device *child_ex = &child->ex_dev; + struct expander_device *parent_ex = &child->parent->ex_dev; + struct ex_phy *child_phy; + + child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; + + if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || + child_phy->routing_attr != TABLE_ROUTING) + goto error; + } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) + return sas_check_eeds(child, parent_phy, child_phy); + else if (child_phy->routing_attr != TABLE_ROUTING) + goto error; + } 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)) + goto error; + } + + return 0; +error: + sas_print_parent_topology_bug(child, parent_phy, child_phy); + return -ENODEV; +} + +static int sas_check_fanout_expander_topo(struct domain_device *child, + struct ex_phy *parent_phy) { struct expander_device *child_ex = &child->ex_dev; + struct ex_phy *child_phy; + + child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; + + if (parent_phy->routing_attr == TABLE_ROUTING && + child_phy->routing_attr == SUBTRACTIVE_ROUTING) + return 0; + + sas_print_parent_topology_bug(child, parent_phy, child_phy); + + return -ENODEV; +} + +static int sas_check_parent_topology(struct domain_device *child) +{ struct expander_device *parent_ex; int i; int res = 0; - if (!child->parent) - return 0; - - if (!dev_is_expander(child->parent->dev_type)) + if (!dev_parent_is_expander(child)) return 0; parent_ex = &child->parent->ex_dev; for (i = 0; i < parent_ex->num_phys; i++) { struct ex_phy *parent_phy = &parent_ex->ex_phy[i]; - struct ex_phy *child_phy; if (parent_phy->phy_state == PHY_VACANT || parent_phy->phy_state == PHY_NOT_PRESENT) @@ -1311,40 +1328,14 @@ static int sas_check_parent_topology(struct domain_device *child) if (!sas_phy_match_dev_addr(child, parent_phy)) continue; - child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; - switch (child->parent->dev_type) { case SAS_EDGE_EXPANDER_DEVICE: - if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { - if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || - child_phy->routing_attr != TABLE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; - } - } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { - if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) { - res = sas_check_eeds(child, parent_phy, child_phy); - } else if (child_phy->routing_attr != TABLE_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; - } - } + if (sas_check_edge_expander_topo(child, parent_phy)) + res = -ENODEV; break; case SAS_FANOUT_EXPANDER_DEVICE: - if (parent_phy->routing_attr != TABLE_ROUTING || - child_phy->routing_attr != SUBTRACTIVE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); + if (sas_check_fanout_expander_topo(child, parent_phy)) res = -ENODEV; - } break; default: break; @@ -1646,6 +1637,16 @@ out_err: /* ---------- Domain revalidation ---------- */ +static void sas_get_sas_addr_and_dev_type(struct smp_disc_resp *disc_resp, + u8 *sas_addr, + enum sas_device_type *type) +{ + memcpy(sas_addr, disc_resp->disc.attached_sas_addr, SAS_ADDR_SIZE); + *type = to_dev_type(&disc_resp->disc); + if (*type == SAS_PHY_UNUSED) + memset(sas_addr, 0, SAS_ADDR_SIZE); +} + static int sas_get_phy_discover(struct domain_device *dev, int phy_id, struct smp_disc_resp *disc_resp) { @@ -1699,13 +1700,8 @@ int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, return -ENOMEM; res = sas_get_phy_discover(dev, phy_id, disc_resp); - if (res == 0) { - memcpy(sas_addr, disc_resp->disc.attached_sas_addr, - SAS_ADDR_SIZE); - *type = to_dev_type(&disc_resp->disc); - if (*type == 0) - memset(sas_addr, 0, SAS_ADDR_SIZE); - } + if (res == 0) + sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, type); kfree(disc_resp); return res; } @@ -1869,9 +1865,12 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, if (phy->port) { sas_port_delete_phy(phy->port, phy->phy); sas_device_set_phy(found, phy->port); - if (phy->port->num_phys == 0) + if (phy->port->num_phys == 0) { list_add_tail(&phy->port->del_list, &parent->port->sas_port_del_list); + if (ex_dev->parent_port == phy->port) + ex_dev->parent_port = NULL; + } phy->port = NULL; } } @@ -1965,6 +1964,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; enum sas_device_type type = SAS_PHY_UNUSED; + struct smp_disc_resp *disc_resp; u8 sas_addr[SAS_ADDR_SIZE]; char msg[80] = ""; int res; @@ -1976,33 +1976,41 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, SAS_ADDR(dev->sas_addr), phy_id, msg); memset(sas_addr, 0, SAS_ADDR_SIZE); - res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); + disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); + if (!disc_resp) + return -ENOMEM; + + res = sas_get_phy_discover(dev, phy_id, disc_resp); switch (res) { case SMP_RESP_NO_PHY: phy->phy_state = PHY_NOT_PRESENT; sas_unregister_devs_sas_addr(dev, phy_id, last); - return res; + goto out_free_resp; case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; sas_unregister_devs_sas_addr(dev, phy_id, last); - return res; + goto out_free_resp; case SMP_RESP_FUNC_ACC: break; case -ECOMM: break; default: - return res; + goto out_free_resp; } + if (res == 0) + sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, &type); + if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); /* - * Even though the PHY is empty, for convenience we discover - * the PHY to update the PHY info, like negotiated linkrate. + * Even though the PHY is empty, for convenience we update + * the PHY info, like negotiated linkrate. */ - sas_ex_phy_discover(dev, phy_id); - return res; + if (res == 0) + sas_set_ex_phy(dev, phy_id, disc_resp); + goto out_free_resp; } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && dev_type_flutter(type, phy->attached_dev_type)) { struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); @@ -2014,7 +2022,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, action = ", needs recovery"; pr_debug("ex %016llx phy%02d broadcast flutter%s\n", SAS_ADDR(dev->sas_addr), phy_id, action); - return res; + goto out_free_resp; } /* we always have to delete the old device when we went here */ @@ -2023,7 +2031,10 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, SAS_ADDR(phy->attached_sas_addr)); sas_unregister_devs_sas_addr(dev, phy_id, last); - return sas_discover_new(dev, phy_id); + res = sas_discover_new(dev, phy_id); +out_free_resp: + kfree(disc_resp); + return res; } /** diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 32cdc969b736..2ecb8535634c 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -114,7 +114,7 @@ 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); + struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt); int written; if (i->dft->lldd_write_gpio == NULL) { @@ -182,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, enum sas_linkrate max, u8 *resp_data) { struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct sas_phy_linkrates rates; struct asd_sas_phy *asd_phy; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index f2c05ebeb72f..8566bb1208a0 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -122,12 +122,12 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) error = -ENOMEM; snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev)); - sas_ha->event_q = create_singlethread_workqueue(name); + sas_ha->event_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name); if (!sas_ha->event_q) goto Undo_ports; snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev)); - sas_ha->disco_q = create_singlethread_workqueue(name); + sas_ha->disco_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name); if (!sas_ha->disco_q) goto Undo_event_q; @@ -183,7 +183,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) 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); + to_sas_internal(sas_ha->shost->transportt); return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); } @@ -232,7 +232,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) 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); + to_sas_internal(sas_ha->shost->transportt); if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) return 0; @@ -266,7 +266,7 @@ int sas_phy_enable(struct sas_phy *phy, int enable) 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); + to_sas_internal(sas_ha->shost->transportt); if (enable) ret = transport_sas_phy_reset(phy, 0); @@ -303,7 +303,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) 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); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); } else { @@ -315,8 +315,8 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) } EXPORT_SYMBOL_GPL(sas_phy_reset); -int sas_set_phy_speed(struct sas_phy *phy, - struct sas_phy_linkrates *rates) +static int sas_set_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) { int ret; @@ -339,7 +339,7 @@ int sas_set_phy_speed(struct sas_phy *phy, 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); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, rates); @@ -438,7 +438,7 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) /* all phys are back up or timed out, turn on i/o so we can * flush out disks that did not return */ - scsi_unblock_requests(ha->core.shost); + scsi_unblock_requests(ha->shost); if (drain) sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); @@ -468,7 +468,7 @@ void sas_suspend_ha(struct sas_ha_struct *ha) int i; sas_disable_events(ha); - scsi_block_requests(ha->core.shost); + scsi_block_requests(ha->shost); for (i = 0; i < ha->num_phys; i++) { struct asd_sas_port *port = ha->sas_port[i]; @@ -641,7 +641,7 @@ struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, struct asd_sas_event *event; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); event = kmem_cache_zalloc(sas_event_cache, gfp_flags); if (!event) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 6f593fa69b58..6706f2be8d27 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -39,15 +39,21 @@ struct sas_phy_data { struct sas_work enable_work; }; -void sas_scsi_recover_host(struct Scsi_Host *shost); +void sas_hash_addr(u8 *hashed, const u8 *sas_addr); + +int sas_discover_root_expander(struct domain_device *dev); -int sas_show_class(enum sas_class class, char *buf); -int sas_show_proto(enum sas_protocol proto, char *buf); -int sas_show_linkrate(enum sas_linkrate linkrate, char *buf); -int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); +int sas_ex_revalidate_domain(struct domain_device *dev); +void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone); +void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port); +void sas_discover_event(struct asd_sas_port *port, enum discover_event ev); + +void sas_init_dev(struct domain_device *dev); +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev); + +void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_register_phys(struct sas_ha_struct *sas_ha); -void sas_unregister_phys(struct sas_ha_struct *sas_ha); struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags); void sas_free_event(struct asd_sas_event *event); @@ -64,7 +70,7 @@ void sas_enable_revalidation(struct sas_ha_struct *ha); void sas_queue_deferred_work(struct sas_ha_struct *ha); void __sas_drain_work(struct sas_ha_struct *ha); -void sas_deform_port(struct asd_sas_phy *phy, int gone); +void sas_deform_port(struct asd_sas_phy *phy, bool gone); void sas_porte_bytes_dmaed(struct work_struct *work); void sas_porte_broadcast_rcvd(struct work_struct *work); @@ -91,7 +97,6 @@ int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, u8 *sas_addr, enum sas_device_type *type); int sas_try_ata_reset(struct asd_sas_phy *phy); -void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); void sas_destruct_devices(struct asd_sas_port *port); @@ -140,6 +145,20 @@ static inline void sas_fail_probe(struct domain_device *dev, const char *func, i func, dev->parent ? "exp-attached" : "direct-attached", SAS_ADDR(dev->sas_addr), err); + + /* + * If the device probe failed, the expander phy attached address + * needs to be reset so that the phy will not be treated as flutter + * in the next revalidation + */ + if (dev->parent && !dev_is_expander(dev->dev_type)) { + struct sas_phy *phy = dev->phy; + struct domain_device *parent = dev->parent; + struct ex_phy *ex_phy = &parent->ex_dev.ex_phy[phy->number]; + + memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + } + sas_unregister_dev(dev->port, dev); } @@ -184,21 +203,6 @@ static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_devic } } -static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) -{ - struct expander_device *ex = &dev->ex_dev; - struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; - - if (!ex->parent_port) { - ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); - /* FIXME: error handling */ - BUG_ON(!ex->parent_port); - BUG_ON(sas_port_add(ex->parent_port)); - sas_port_mark_backlink(ex->parent_port); - } - sas_port_add_phy(ex->parent_port, ex_phy->phy); -} - static inline struct domain_device *sas_alloc_device(void) { struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -218,4 +222,78 @@ static inline void sas_put_device(struct domain_device *dev) kref_put(&dev->kref, sas_free_device); } +#ifdef CONFIG_SCSI_SAS_ATA + +int sas_ata_init(struct domain_device *dev); +void sas_ata_task_abort(struct sas_task *task); +int sas_discover_sata(struct domain_device *dev); +int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id); +void sas_ata_strategy_handler(struct Scsi_Host *shost); +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q); +void sas_ata_end_eh(struct ata_port *ap); +void sas_ata_wait_eh(struct domain_device *dev); +void sas_probe_sata(struct asd_sas_port *port); +void sas_suspend_sata(struct asd_sas_port *port); +void sas_resume_sata(struct asd_sas_port *port); + +#else + +static inline int sas_ata_init(struct domain_device *dev) +{ + return 0; +} + +static inline void sas_ata_task_abort(struct sas_task *task) +{ +} + +static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ +} + +static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q) +{ +} + +static inline void sas_ata_end_eh(struct ata_port *ap) +{ +} + +static inline void sas_ata_wait_eh(struct domain_device *dev) +{ +} + +static inline void sas_probe_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_suspend_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_resume_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_ata_disabled_notice(void) +{ + pr_notice_once("ATA device seen but CONFIG_SCSI_SAS_ATA=N\n"); +} + +static inline int sas_discover_sata(struct domain_device *dev) +{ + sas_ata_disabled_notice(); + return -ENXIO; +} + +static inline int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + sas_ata_disabled_notice(); + return -ENODEV; +} + +#endif + #endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index a0d592d11dfb..635835c28ecd 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -20,7 +20,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; phy->error = 0; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } static void sas_phye_oob_done(struct work_struct *work) @@ -38,9 +38,9 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); - sas_deform_port(phy, 1); + sas_deform_port(phy, true); if (!port && phy->enabled && i->dft->lldd_control_phy) { phy->error++; @@ -66,7 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); @@ -85,7 +85,7 @@ static void sas_phye_resume_timeout(struct work_struct *work) phy->error = 0; phy->suspended = 0; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } @@ -95,7 +95,7 @@ static void sas_phye_shutdown(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (phy->enabled) { int ret; @@ -131,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) spin_lock_init(&phy->sas_prim_lock); phy->frame_rcvd_size = 0; - phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); + phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i); if (!phy->phy) return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 11599c0e3fc3..de7556070048 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -28,7 +28,7 @@ static void sas_resume_port(struct asd_sas_phy *phy) struct domain_device *dev, *n; struct asd_sas_port *port = phy->port; struct sas_ha_struct *sas_ha = phy->ha; - struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt); if (si->dft->lldd_port_formed) si->dft->lldd_port_formed(phy); @@ -83,7 +83,6 @@ static void sas_form_port_add_phy(struct asd_sas_port *port, memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE); if (*(u64 *)port->attached_sas_addr == 0) { - port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); port->iproto = phy->iproto; @@ -109,12 +108,12 @@ static void sas_form_port(struct asd_sas_phy *phy) struct asd_sas_port *port = phy->port; struct domain_device *port_dev = NULL; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); unsigned long flags; if (port) { if (!phy_is_wideport_member(port, phy)) - sas_deform_port(phy, 0); + sas_deform_port(phy, false); else if (phy->suspended) { phy->suspended = 0; sas_resume_port(phy); @@ -207,12 +206,12 @@ static void sas_form_port(struct asd_sas_phy *phy) * This is called when the physical link to the other phy has been * lost (on this phy), in Event thread context. We cannot delay here. */ -void sas_deform_port(struct asd_sas_phy *phy, int gone) +void sas_deform_port(struct asd_sas_phy *phy, bool gone) { struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct domain_device *dev; unsigned long flags; @@ -249,7 +248,6 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) INIT_LIST_HEAD(&port->phy_list); memset(port->sas_addr, 0, SAS_ADDR_SIZE); memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE); - port->class = 0; port->iproto = 0; port->tproto = 0; port->oob_mode = 0; @@ -303,7 +301,7 @@ void sas_porte_link_reset_err(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } void sas_porte_timer_event(struct work_struct *work) @@ -311,7 +309,7 @@ void sas_porte_timer_event(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } void sas_porte_hard_reset(struct work_struct *work) @@ -319,7 +317,7 @@ void sas_porte_hard_reset(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } /* ---------- SAS port registration ---------- */ @@ -360,8 +358,7 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha) for (i = 0; i < sas_ha->num_phys; i++) if (sas_ha->sas_phy[i]->port) - sas_deform_port(sas_ha->sas_phy[i], 0); - + sas_deform_port(sas_ha->sas_phy[i], false); } const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index a36fa1c128a8..ffa5b49aaf08 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -142,7 +142,6 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, task->dev = dev; task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ - task->ssp_task.retry_count = 1; int_to_scsilun(cmd->device->lun, &lun); memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); task->ssp_task.task_attr = TASK_ATTR_SIMPLE; @@ -279,7 +278,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) unsigned long flags; int i, res; struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); + to_sas_internal(task->dev->port->ha->shost->transportt); for (i = 0; i < 5; i++) { pr_notice("%s: aborting task 0x%p\n", __func__, task); @@ -327,7 +326,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) int res = TMF_RESP_FUNC_FAILED; struct scsi_lun lun; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); int_to_scsilun(cmd->device->lun, &lun); @@ -355,7 +354,7 @@ static int sas_recover_I_T(struct domain_device *dev) { int res = TMF_RESP_FUNC_FAILED; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); pr_notice("I_T nexus reset for dev %016llx\n", SAS_ADDR(dev->sas_addr)); @@ -387,37 +386,7 @@ struct sas_phy *sas_get_local_phy(struct domain_device *dev) } EXPORT_SYMBOL_GPL(sas_get_local_phy); -static void sas_wait_eh(struct domain_device *dev) -{ - struct sas_ha_struct *ha = dev->port->ha; - DEFINE_WAIT(wait); - - if (dev_is_sata(dev)) { - ata_port_wait_eh(dev->sata_dev.ap); - return; - } - retry: - spin_lock_irq(&ha->lock); - - while (test_bit(SAS_DEV_EH_PENDING, &dev->state)) { - prepare_to_wait(&ha->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&ha->lock); - schedule(); - spin_lock_irq(&ha->lock); - } - finish_wait(&ha->eh_wait_q, &wait); - - spin_unlock_irq(&ha->lock); - - /* make sure SCSI EH is complete */ - if (scsi_host_in_recovery(ha->core.shost)) { - msleep(10); - goto retry; - } -} - -static int sas_queue_reset(struct domain_device *dev, int reset_type, - u64 lun, int wait) +static int sas_queue_reset(struct domain_device *dev, int reset_type, u64 lun) { struct sas_ha_struct *ha = dev->port->ha; int scheduled = 0, tries = 100; @@ -425,8 +394,6 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, /* ata: promote lun reset to bus reset */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); - if (wait) - sas_ata_wait_eh(dev); return SUCCESS; } @@ -440,13 +407,10 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, set_bit(SAS_DEV_EH_PENDING, &dev->state); set_bit(reset_type, &dev->state); int_to_scsilun(lun, &dev->ssp_dev.reset_lun); - scsi_schedule_eh(ha->core.shost); + scsi_schedule_eh(ha->shost); } spin_unlock_irq(&ha->lock); - if (wait) - sas_wait_eh(dev); - if (scheduled) return SUCCESS; } @@ -499,7 +463,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun, 0); + return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun); int_to_scsilun(cmd->device->lun, &lun); @@ -522,7 +486,7 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); + return sas_queue_reset(dev, SAS_DEV_RESET, 0); if (!i->dft->lldd_I_T_nexus_reset) return FAILED; @@ -840,14 +804,14 @@ EXPORT_SYMBOL_GPL(sas_target_alloc); #define SAS_DEF_QD 256 -int sas_slave_configure(struct scsi_device *scsi_dev) +int sas_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim) { struct domain_device *dev = sdev_to_domain_dev(scsi_dev); BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); if (dev_is_sata(dev)) { - ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap); + ata_sas_sdev_configure(scsi_dev, lim, dev->sata_dev.ap); return 0; } @@ -865,15 +829,14 @@ int sas_slave_configure(struct scsi_device *scsi_dev) return 0; } -EXPORT_SYMBOL_GPL(sas_slave_configure); +EXPORT_SYMBOL_GPL(sas_sdev_configure); int sas_change_queue_depth(struct scsi_device *sdev, int depth) { struct domain_device *dev = sdev_to_domain_dev(sdev); if (dev_is_sata(dev)) - return ata_change_queue_depth(dev->sata_dev.ap, - sas_to_ata_dev(dev), sdev, depth); + return ata_change_queue_depth(dev->sata_dev.ap, sdev, depth); if (!sdev->tagged_supported) depth = 1; @@ -882,7 +845,7 @@ int sas_change_queue_depth(struct scsi_device *sdev, int depth) EXPORT_SYMBOL_GPL(sas_change_queue_depth); int sas_bios_param(struct scsi_device *scsi_dev, - struct block_device *bdev, + struct gendisk *unused, sector_t capacity, int *hsc) { hsc[0] = 255; @@ -896,13 +859,13 @@ EXPORT_SYMBOL_GPL(sas_bios_param); void sas_task_internal_done(struct sas_task *task) { - del_timer(&task->slow_task->timer); + timer_delete(&task->slow_task->timer); complete(&task->slow_task->completion); } void sas_task_internal_timedout(struct timer_list *t) { - struct sas_task_slow *slow = from_timer(slow, t, timer); + struct sas_task_slow *slow = timer_container_of(slow, t, timer); struct sas_task *task = slow->task; bool is_completed = true; unsigned long flags; @@ -926,7 +889,7 @@ static int sas_execute_internal_abort(struct domain_device *device, unsigned int qid, void *data) { struct sas_ha_struct *ha = device->port->ha; - struct sas_internal *i = to_sas_internal(ha->core.shost->transportt); + struct sas_internal *i = to_sas_internal(ha->shost->transportt); struct sas_task *task = NULL; int res, retry; @@ -948,7 +911,7 @@ static int sas_execute_internal_abort(struct domain_device *device, res = i->dft->lldd_execute_task(task, GFP_KERNEL); if (res) { - del_timer_sync(&task->slow_task->timer); + timer_delete_sync(&task->slow_task->timer); pr_err("Executing internal abort failed %016llx (%d)\n", SAS_ADDR(device->sas_addr), res); break; @@ -1016,7 +979,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter, { struct sas_task *task; struct sas_internal *i = - to_sas_internal(device->port->ha->core.shost->transportt); + to_sas_internal(device->port->ha->shost->transportt); int res, retry; for (retry = 0; retry < TASK_RETRY; retry++) { @@ -1047,7 +1010,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter, res = i->dft->lldd_execute_task(task, GFP_KERNEL); if (res) { - del_timer_sync(&task->slow_task->timer); + timer_delete_sync(&task->slow_task->timer); pr_err("executing TMF task failed %016llx (%d)\n", SAS_ADDR(device->sas_addr), res); break; @@ -1217,7 +1180,7 @@ void sas_task_abort(struct sas_task *task) if (!slow) return; - if (!del_timer(&slow->timer)) + if (!timer_delete(&slow->timer)) return; slow->timer.function(&slow->timer); return; @@ -1230,14 +1193,14 @@ void sas_task_abort(struct sas_task *task) } EXPORT_SYMBOL_GPL(sas_task_abort); -int sas_slave_alloc(struct scsi_device *sdev) +int sas_sdev_init(struct scsi_device *sdev) { if (dev_is_sata(sdev_to_domain_dev(sdev)) && sdev->lun) return -ENXIO; return 0; } -EXPORT_SYMBOL_GPL(sas_slave_alloc); +EXPORT_SYMBOL_GPL(sas_sdev_init); void sas_target_destroy(struct scsi_target *starget) { |
