diff options
Diffstat (limited to 'drivers/scsi/libsas')
| -rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 172 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 183 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_dump.c | 1 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_event.c | 40 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 260 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_init.c | 140 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 17 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 42 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_port.c | 69 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 199 |
10 files changed, 808 insertions, 315 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index bc0cecc6ad6..766098af4eb 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -139,12 +139,12 @@ static void sas_ata_task_done(struct sas_task *task) if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || ((stat->stat == SAM_STAT_CHECK_CONDITION && dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { - ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE); if (!link->sactive) { - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + qc->err_mask |= ac_err_mask(dev->sata_dev.fis[2]); } else { - link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.tf.command); + 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; } @@ -161,8 +161,8 @@ static void sas_ata_task_done(struct sas_task *task) qc->flags |= ATA_QCFLAG_FAILED; } - dev->sata_dev.tf.feature = 0x04; /* status err */ - dev->sata_dev.tf.command = ATA_ERR; + dev->sata_dev.fis[3] = 0x04; /* status err */ + dev->sata_dev.fis[2] = ATA_ERR; } } @@ -211,7 +211,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) qc->tf.nsect = 0; } - ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis); + ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8 *)&task->ata_task.fis); task->uldd_task = qc; if (ata_is_atapi(qc->tf.protocol)) { memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); @@ -269,7 +269,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) { struct domain_device *dev = qc->ap->private_data; - memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf)); + ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf); return true; } @@ -285,14 +285,14 @@ 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; if (phy->attached_sata_dev) - dev->tproto |= SATA_DEV; + dev->tproto |= SAS_SATA_DEV; - if (phy->attached_dev_type == SATA_PENDING) - dev->dev_type = SATA_PENDING; + if (phy->attached_dev_type == SAS_SATA_PENDING) + dev->dev_type = SAS_SATA_PENDING; else { int res; - dev->dev_type = SATA_DEV; + dev->dev_type = SAS_SATA_DEV; res = sas_get_report_phy_sata(dev->parent, phy->phy_id, &dev->sata_dev.rps_resp); if (res) { @@ -314,7 +314,7 @@ static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy) int res; /* we weren't pending, so successfully end the reset sequence now */ - if (dev->dev_type != SATA_PENDING) + if (dev->dev_type != SAS_SATA_PENDING) return 1; /* hmmm, if this succeeds do we need to repost the domain_device to the @@ -348,9 +348,9 @@ static int smp_ata_check_ready(struct ata_link *link) return 0; switch (ex_phy->attached_dev_type) { - case SATA_PENDING: + case SAS_SATA_PENDING: return 0; - case SAS_END_DEV: + case SAS_END_DEVICE: if (ex_phy->attached_sata_dev) return sas_ata_clear_pending(dev, ex_phy); default: @@ -523,6 +523,31 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) i->dft->lldd_ata_set_dmamode(dev); } +static void sas_ata_sched_eh(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + struct sas_ha_struct *ha = dev->port->ha; + unsigned long flags; + + spin_lock_irqsave(&ha->lock, flags); + if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state)) + ha->eh_active++; + ata_std_sched_eh(ap); + spin_unlock_irqrestore(&ha->lock, flags); +} + +void sas_ata_end_eh(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + struct sas_ha_struct *ha = dev->port->ha; + unsigned long flags; + + spin_lock_irqsave(&ha->lock, flags); + if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state)) + ha->eh_active--; + spin_unlock_irqrestore(&ha->lock, flags); +} + static struct ata_port_operations sas_sata_ops = { .prereset = ata_std_prereset, .hardreset = sas_ata_hard_reset, @@ -536,6 +561,8 @@ static struct ata_port_operations sas_sata_ops = { .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 = { @@ -546,16 +573,14 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev) +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 ata_port *ap; + int rc; - ata_host_init(&found_dev->sata_dev.ata_host, - ha->dev, - sata_port_info.flags, - &sas_sata_ops); + ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops); ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, &sata_port_info, shost); @@ -567,8 +592,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - /* publish initialized ata port */ - smp_wmb(); + rc = ata_sas_port_init(ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; @@ -587,7 +615,6 @@ void sas_ata_task_abort(struct sas_task *task) spin_lock_irqsave(q->queue_lock, flags); blk_abort_request(qc->scsicmd->request); spin_unlock_irqrestore(q->queue_lock, flags); - scsi_schedule_eh(qc->scsicmd->device->host); return; } @@ -604,7 +631,7 @@ static void sas_get_ata_command_set(struct domain_device *dev) struct dev_to_host_fis *fis = (struct dev_to_host_fis *) dev->frame_rcvd; - if (dev->dev_type == SATA_PENDING) + if (dev->dev_type == SAS_SATA_PENDING) return; if ((fis->sector_count == 1 && /* ATA */ @@ -648,18 +675,13 @@ static void sas_get_ata_command_set(struct domain_device *dev) void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; - int err; mutex_lock(&port->ha->disco_mutex); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + list_for_each_entry(dev, &port->disco_list, disco_list_node) { if (!dev_is_sata(dev)) continue; - err = sas_ata_init_host_and_port(dev); - if (err) - sas_fail_probe(dev, __func__, err); - else - ata_sas_async_port_init(dev->sata_dev.ap); + ata_sas_async_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -675,6 +697,67 @@ void sas_probe_sata(struct asd_sas_port *port) if (ata_dev_disabled(sas_to_ata_dev(dev))) sas_fail_probe(dev, __func__, -ENODEV); } + +} + +static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) +{ + struct domain_device *dev, *n; + + list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) { + if (!dev_is_sata(dev)) + continue; + + sas_ata_wait_eh(dev); + + /* if libata failed to power manage the device, tear it down */ + if (ata_dev_disabled(sas_to_ata_dev(dev))) + sas_fail_probe(dev, func, -ENODEV); + } +} + +void sas_suspend_sata(struct asd_sas_port *port) +{ + struct domain_device *dev; + + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + struct sata_device *sata; + + if (!dev_is_sata(dev)) + continue; + + sata = &dev->sata_dev; + if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND) + continue; + + ata_sas_port_suspend(sata->ap); + } + mutex_unlock(&port->ha->disco_mutex); + + sas_ata_flush_pm_eh(port, __func__); +} + +void sas_resume_sata(struct asd_sas_port *port) +{ + struct domain_device *dev; + + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + struct sata_device *sata; + + if (!dev_is_sata(dev)) + continue; + + sata = &dev->sata_dev; + if (sata->ap->pm_mesg.event == PM_EVENT_ON) + continue; + + ata_sas_port_resume(sata->ap); + } + mutex_unlock(&port->ha->disco_mutex); + + sas_ata_flush_pm_eh(port, __func__); } /** @@ -689,7 +772,7 @@ int sas_discover_sata(struct domain_device *dev) { int res; - if (dev->dev_type == SATA_PM) + if (dev->dev_type == SAS_SATA_PM) return -ENODEV; sas_get_ata_command_set(dev); @@ -709,31 +792,15 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) struct ata_port *ap = dev->sata_dev.ap; struct sas_ha_struct *ha = dev->port->ha; - /* hold a reference over eh since we may be racing with final - * remove once all commands are completed - */ - kref_get(&dev->kref); sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); ata_scsi_port_error_handler(ha->core.shost, ap); sas_put_device(dev); } -static bool sas_ata_dev_eh_valid(struct domain_device *dev) -{ - struct ata_port *ap; - - if (!dev_is_sata(dev)) - return false; - ap = dev->sata_dev.ap; - /* consume fully initialized ata ports */ - smp_rmb(); - return !!ap; -} - void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); - LIST_HEAD(async); + ASYNC_DOMAIN_EXCLUSIVE(async); int i; /* it's ok to defer revalidation events during ata eh, these @@ -753,8 +820,15 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) spin_lock(&port->dev_list_lock); list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (!sas_ata_dev_eh_valid(dev)) + if (!dev_is_sata(dev)) continue; + + /* hold a reference over eh since we may be + * racing with final remove once all commands + * are completed + */ + kref_get(&dev->kref); + async_schedule_domain(async_sas_ata_eh, dev, &async); } spin_unlock(&port->dev_list_lock); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 36467967560..62b58d38ce2 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -24,6 +24,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/async.h> #include <scsi/scsi_host.h> #include <scsi/scsi_eh.h> #include "sas_internal.h" @@ -38,19 +39,14 @@ void sas_init_dev(struct domain_device *dev) { switch (dev->dev_type) { - case SAS_END_DEV: + case SAS_END_DEVICE: + INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node); break; - case EDGE_DEV: - case FANOUT_DEV: + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: INIT_LIST_HEAD(&dev->ex_dev.children); mutex_init(&dev->ex_dev.cmd_mutex); break; - case SATA_DEV: - case SATA_PM: - case SATA_PM_PORT: - case SATA_PENDING: - INIT_LIST_HEAD(&dev->sata_dev.children); - break; default: break; } @@ -72,6 +68,7 @@ static int sas_get_port_device(struct asd_sas_port *port) struct asd_sas_phy *phy; struct sas_rphy *rphy; struct domain_device *dev; + int rc = -ENODEV; dev = sas_alloc_device(); if (!dev) @@ -96,9 +93,9 @@ static int sas_get_port_device(struct asd_sas_port *port) if (fis->interrupt_reason == 1 && fis->lbal == 1 && fis->byte_count_low==0x69 && fis->byte_count_high == 0x96 && (fis->device & ~0x10) == 0) - dev->dev_type = SATA_PM; + dev->dev_type = SAS_SATA_PM; else - dev->dev_type = SATA_DEV; + dev->dev_type = SAS_SATA_DEV; dev->tproto = SAS_PROTOCOL_SATA; } else { struct sas_identify_frame *id = @@ -110,16 +107,23 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_init_dev(dev); + dev->port = port; switch (dev->dev_type) { - case SAS_END_DEV: - case SATA_DEV: + case SAS_SATA_DEV: + rc = sas_ata_init(dev); + if (rc) { + rphy = NULL; + break; + } + /* fall through */ + case SAS_END_DEVICE: rphy = sas_end_device_alloc(port->port); break; - case EDGE_DEV: + case SAS_EDGE_EXPANDER_DEVICE: rphy = sas_expander_alloc(port->port, SAS_EDGE_EXPANDER_DEVICE); break; - case FANOUT_DEV: + case SAS_FANOUT_EXPANDER_DEVICE: rphy = sas_expander_alloc(port->port, SAS_FANOUT_EXPANDER_DEVICE); break; @@ -131,19 +135,14 @@ static int sas_get_port_device(struct asd_sas_port *port) if (!rphy) { sas_put_device(dev); - return -ENODEV; + return rc; } - spin_lock_irq(&port->phy_list_lock); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - sas_phy_set_target(phy, dev); - spin_unlock_irq(&port->phy_list_lock); rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE); sas_fill_in_rphy(dev, rphy); sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); port->port_dev = dev; - dev->port = port; dev->linkrate = port->linkrate; dev->min_linkrate = port->linkrate; dev->max_linkrate = port->linkrate; @@ -155,8 +154,9 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_device_set_phy(dev, port->port); dev->rphy = rphy; + get_device(&dev->rphy->dev); - if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV) + if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEVICE) list_add_tail(&dev->disco_list_node, &port->disco_list); else { spin_lock_irq(&port->dev_list_lock); @@ -164,6 +164,11 @@ static int sas_get_port_device(struct asd_sas_port *port) spin_unlock_irq(&port->dev_list_lock); } + spin_lock_irq(&port->phy_list_lock); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + sas_phy_set_target(phy, dev); + spin_unlock_irq(&port->phy_list_lock); + return 0; } @@ -176,16 +181,18 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) struct Scsi_Host *shost = sas_ha->core.shost; struct sas_internal *i = to_sas_internal(shost->transportt); - if (i->dft->lldd_dev_found) { - res = i->dft->lldd_dev_found(dev); - if (res) { - printk("sas: driver on pcidev %s cannot handle " - "device %llx, error:%d\n", - dev_name(sas_ha->dev), - SAS_ADDR(dev->sas_addr), res); - } - kref_get(&dev->kref); + if (!i->dft->lldd_dev_found) + return 0; + + res = i->dft->lldd_dev_found(dev); + if (res) { + printk("sas: driver on pcidev %s cannot handle " + "device %llx, error:%d\n", + dev_name(sas_ha->dev), + SAS_ADDR(dev->sas_addr), res); } + set_bit(SAS_DEV_FOUND, &dev->state); + kref_get(&dev->kref); return res; } @@ -196,7 +203,10 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) struct Scsi_Host *shost = sas_ha->core.shost; struct sas_internal *i = to_sas_internal(shost->transportt); - if (i->dft->lldd_dev_gone) { + if (!i->dft->lldd_dev_gone) + return; + + if (test_and_clear_bit(SAS_DEV_FOUND, &dev->state)) { i->dft->lldd_dev_gone(dev); sas_put_device(dev); } @@ -205,8 +215,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) static void sas_probe_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_PROBE, &port->disc.pending); @@ -231,6 +240,47 @@ static void sas_probe_devices(struct work_struct *work) } } +static void sas_suspend_devices(struct work_struct *work) +{ + struct asd_sas_phy *phy; + 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 sas_internal *si = to_sas_internal(shost->transportt); + + clear_bit(DISCE_SUSPEND, &port->disc.pending); + + sas_suspend_sata(port); + + /* lldd is free to forget the domain_device across the + * suspension, we force the issue here to keep the reference + * counts aligned + */ + list_for_each_entry(dev, &port->dev_list, dev_list_node) + sas_notify_lldd_dev_gone(dev); + + /* we are suspending, so we know events are disabled and + * phy_list is not being mutated + */ + list_for_each_entry(phy, &port->phy_list, port_phy_el) { + if (si->dft->lldd_port_formed) + si->dft->lldd_port_deformed(phy); + phy->suspended = 1; + port->suspended = 1; + } +} + +static void sas_resume_devices(struct work_struct *work) +{ + struct sas_discovery_event *ev = to_sas_discovery_event(work); + struct asd_sas_port *port = ev->port; + + clear_bit(DISCE_RESUME, &port->disc.pending); + + sas_resume_sata(port); +} + /** * sas_discover_end_dev -- discover an end device (SSP, etc) * @end: pointer to domain device of interest @@ -255,6 +305,9 @@ void sas_free_device(struct kref *kref) { struct domain_device *dev = container_of(kref, typeof(*dev), kref); + put_device(&dev->rphy->dev); + dev->rphy = NULL; + if (dev->parent) sas_put_device(dev->parent); @@ -262,7 +315,7 @@ void sas_free_device(struct kref *kref) dev->phy = NULL; /* remove the phys and ports, everything else should be gone */ - if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) + if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) kfree(dev->ex_dev.ex_phy); if (dev_is_sata(dev) && dev->sata_dev.ap) { @@ -275,6 +328,8 @@ void sas_free_device(struct kref *kref) static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { + struct sas_ha_struct *ha = port->ha; + sas_notify_lldd_dev_gone(dev); if (!dev->parent) dev->port->port_dev = NULL; @@ -283,16 +338,25 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); + if (dev_is_sata(dev)) + sas_ata_end_eh(dev->sata_dev.ap); spin_unlock_irq(&port->dev_list_lock); + spin_lock_irq(&ha->lock); + if (dev->dev_type == SAS_END_DEVICE && + !list_empty(&dev->ssp_dev.eh_list_node)) { + list_del_init(&dev->ssp_dev.eh_list_node); + ha->eh_active--; + } + spin_unlock_irq(&ha->lock); + sas_put_device(dev); } static void sas_destruct_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DESTRUCT, &port->disc.pending); @@ -302,7 +366,6 @@ static void sas_destruct_devices(struct work_struct *work) sas_remove_children(&dev->rphy->dev); sas_rphy_delete(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); } } @@ -314,11 +377,11 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) /* this rphy never saw sas_rphy_add */ list_del_init(&dev->disco_list_node); sas_rphy_free(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); + return; } - if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { sas_rphy_unlink(dev->rphy); list_move_tail(&dev->disco_list_node, &port->destroy_list); sas_discover_event(dev->port, DISCE_DESTRUCT); @@ -377,8 +440,7 @@ static void sas_discover_domain(struct work_struct *work) { struct domain_device *dev; int error = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); @@ -395,15 +457,15 @@ static void sas_discover_domain(struct work_struct *work) task_pid_nr(current)); switch (dev->dev_type) { - case SAS_END_DEV: + case SAS_END_DEVICE: error = sas_discover_end_dev(dev); break; - case EDGE_DEV: - case FANOUT_DEV: + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: error = sas_discover_root_expander(dev); break; - case SATA_DEV: - case SATA_PM: + case SAS_SATA_DEV: + case SAS_SATA_PM: #ifdef CONFIG_SCSI_SAS_ATA error = sas_discover_sata(dev); break; @@ -419,8 +481,6 @@ static void sas_discover_domain(struct work_struct *work) if (error) { sas_rphy_free(dev->rphy); - dev->rphy = NULL; - list_del_init(&dev->disco_list_node); spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); @@ -437,8 +497,7 @@ static void sas_discover_domain(struct work_struct *work) static void sas_revalidate_domain(struct work_struct *work) { int res = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; struct sas_ha_struct *ha = port->ha; @@ -466,22 +525,26 @@ static void sas_revalidate_domain(struct work_struct *work) /* ---------- Events ---------- */ -static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) +static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) { - /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ - scsi_queue_work(ha->core.shost, work); + /* chained work is not subject to SA_HA_DRAINING or + * SAS_HA_REGISTERED, because it is either submitted in the + * workqueue, or known to be submitted from a context that is + * not racing against draining + */ + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_chain_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *sw, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { unsigned long flags; - spin_lock_irqsave(&ha->state_lock, flags); - sas_chain_work(ha, work); - spin_unlock_irqrestore(&ha->state_lock, flags); + spin_lock_irqsave(&ha->lock, flags); + sas_chain_work(ha, sw); + spin_unlock_irqrestore(&ha->lock, flags); } } @@ -514,12 +577,14 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, [DISCE_PROBE] = sas_probe_devices, + [DISCE_SUSPEND] = sas_suspend_devices, + [DISCE_RESUME] = sas_resume_devices, [DISCE_DESTRUCT] = sas_destruct_devices, }; disc->pending = 0; for (i = 0; i < DISC_NUM_EVENTS; i++) { - INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); + INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]); disc->disc_work[i].port = port; } } diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index fc460933575..cd6f99c1ae7 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -41,6 +41,7 @@ static const char *sas_phye_str[] = { [1] = "PHYE_OOB_DONE", [2] = "PHYE_OOB_ERROR", [3] = "PHYE_SPINUP_HOLD", + [4] = "PHYE_RESUME_TIMEOUT", }; void sas_dprint_porte(int phyid, enum port_event pe) diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 16639bbae62..aadbd5314c5 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -27,27 +27,29 @@ #include "sas_internal.h" #include "sas_dump.h" -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { if (!test_bit(SAS_HA_REGISTERED, &ha->state)) return; - if (test_bit(SAS_HA_DRAINING, &ha->state)) - list_add(&work->entry, &ha->defer_q); - else - scsi_queue_work(ha->core.shost, work); + if (test_bit(SAS_HA_DRAINING, &ha->state)) { + /* add it to the defer list, if not already pending */ + if (list_empty(&sw->drain_node)) + list_add(&sw->drain_node, &ha->defer_q); + } else + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_queue_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *work, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { unsigned long flags; - spin_lock_irqsave(&ha->state_lock, flags); + spin_lock_irqsave(&ha->lock, flags); sas_queue_work(ha, work); - spin_unlock_irqrestore(&ha->state_lock, flags); + spin_unlock_irqrestore(&ha->lock, flags); } } @@ -55,22 +57,22 @@ static void sas_queue_event(int event, unsigned long *pending, void __sas_drain_work(struct sas_ha_struct *ha) { struct workqueue_struct *wq = ha->core.shost->work_q; - struct work_struct *w, *_w; + struct sas_work *sw, *_sw; set_bit(SAS_HA_DRAINING, &ha->state); /* flush submitters */ - spin_lock_irq(&ha->state_lock); - spin_unlock_irq(&ha->state_lock); + spin_lock_irq(&ha->lock); + spin_unlock_irq(&ha->lock); drain_workqueue(wq); - spin_lock_irq(&ha->state_lock); + spin_lock_irq(&ha->lock); clear_bit(SAS_HA_DRAINING, &ha->state); - list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { - list_del_init(&w->entry); - sas_queue_work(ha, w); + list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { + list_del_init(&sw->drain_node); + sas_queue_work(ha, sw); } - spin_unlock_irq(&ha->state_lock); + spin_unlock_irq(&ha->lock); } int sas_drain_work(struct sas_ha_struct *ha) @@ -132,7 +134,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) &phy->port_events[event].work, ha); } -static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) +void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) { struct sas_ha_struct *ha = phy->ha; @@ -151,13 +153,13 @@ int sas_init_events(struct sas_ha_struct *sas_ha) int i; for (i = 0; i < HA_NUM_EVENTS; i++) { - INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); + INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); sas_ha->ha_events[i].ha = sas_ha; } sas_ha->notify_ha_event = notify_ha_event; sas_ha->notify_port_event = notify_port_event; - sas_ha->notify_phy_event = notify_phy_event; + sas_ha->notify_phy_event = sas_notify_phy_event; return 0; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 05acd9e35fc..0cac7d8fd0f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -51,14 +51,14 @@ static void smp_task_timedout(unsigned long _task) task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); - complete(&task->completion); + complete(&task->slow_task->completion); } static void smp_task_done(struct sas_task *task) { - if (!del_timer(&task->timer)) + if (!del_timer(&task->slow_task->timer)) return; - complete(&task->completion); + complete(&task->slow_task->completion); } /* Give it some long enough timeout. In seconds. */ @@ -79,7 +79,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, break; } - task = sas_alloc_task(GFP_KERNEL); + task = sas_alloc_slow_task(GFP_KERNEL); if (!task) { res = -ENOMEM; break; @@ -91,20 +91,20 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task->task_done = smp_task_done; - task->timer.data = (unsigned long) task; - task->timer.function = smp_task_timedout; - task->timer.expires = jiffies + SMP_TIMEOUT*HZ; - add_timer(&task->timer); + task->slow_task->timer.data = (unsigned long) task; + task->slow_task->timer.function = smp_task_timedout; + task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; + add_timer(&task->slow_task->timer); res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); if (res) { - del_timer(&task->timer); + del_timer(&task->slow_task->timer); SAS_DPRINTK("executing SMP task failed:%d\n", res); break; } - wait_for_completion(&task->completion); + wait_for_completion(&task->slow_task->completion); res = -ECOMM; if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { SAS_DPRINTK("smp task timed out or aborted\n"); @@ -183,25 +183,26 @@ static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) } } -static enum sas_dev_type to_dev_type(struct discover_resp *dr) +static enum sas_device_type to_dev_type(struct discover_resp *dr) { /* This is detecting a failure to transmit initial dev to host * FIS as described in section J.5 of sas-2 r16 */ - if (dr->attached_dev_type == NO_DEVICE && dr->attached_sata_dev && + if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev && dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) - return SATA_PENDING; + return SAS_SATA_PENDING; else return dr->attached_dev_type; } static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) { - enum sas_dev_type dev_type; + enum sas_device_type dev_type; enum sas_linkrate linkrate; u8 sas_addr[SAS_ADDR_SIZE]; struct smp_resp *resp = rsp; struct discover_resp *dr = &resp->disc; + struct sas_ha_struct *ha = dev->port->ha; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; struct sas_rphy *rphy = dev->rphy; @@ -209,6 +210,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) char *type; if (new_phy) { + if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) + return; phy->phy = sas_phy_alloc(&rphy->dev, phy_id); /* FIXME: error_handling */ @@ -232,7 +235,20 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) linkrate = phy->linkrate; memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + /* Handle vacant phy - rest of dr data is not valid so skip it */ + if (phy->phy_state == PHY_VACANT) { + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + phy->attached_dev_type = SAS_PHY_UNUSED; + if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { + phy->phy_id = phy_id; + goto skip; + } else + goto out; + } + phy->attached_dev_type = to_dev_type(dr); + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + goto out; phy->phy_id = phy_id; phy->linkrate = dr->linkrate; phy->attached_sata_host = dr->attached_sata_host; @@ -240,7 +256,14 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->attached_sata_ps = dr->attached_sata_ps; phy->attached_iproto = dr->iproto << 1; phy->attached_tproto = dr->tproto << 1; - memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); + /* 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) + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + else + memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); phy->attached_phy_id = dr->attached_phy_id; phy->phy_change_count = dr->change_count; phy->routing_attr = dr->routing_attr; @@ -260,20 +283,22 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->phy->maximum_linkrate = dr->pmax_linkrate; phy->phy->negotiated_linkrate = phy->linkrate; + skip: if (new_phy) if (sas_phy_add(phy->phy)) { sas_phy_free(phy->phy); return; } + out: switch (phy->attached_dev_type) { - case SATA_PENDING: + case SAS_SATA_PENDING: type = "stp pending"; break; - case NO_DEVICE: + case SAS_PHY_UNUSED: type = "no device"; break; - case SAS_END_DEV: + case SAS_END_DEVICE: if (phy->attached_iproto) { if (phy->attached_tproto) type = "host+target"; @@ -286,8 +311,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) type = "ssp"; } break; - case EDGE_DEV: - case FANOUT_DEV: + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: type = "smp"; break; default: @@ -304,7 +329,15 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) else return; - SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + /* if the attached device type changed and ata_eh is active, + * make sure we run revalidation when eh completes (see: + * sas_enable_revalidation) + */ + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); + + SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", SAS_ADDR(dev->sas_addr), phy->phy_id, sas_route_char(dev, phy), phy->linkrate, SAS_ADDR(phy->attached_sas_addr), type); @@ -367,7 +400,7 @@ int sas_ex_phy_discover(struct domain_device *dev, int single) if (!disc_req) return -ENOMEM; - disc_resp = alloc_smp_req(DISCOVER_RESP_SIZE); + disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); if (!disc_resp) { kfree(disc_req); return -ENOMEM; @@ -776,13 +809,16 @@ static struct domain_device *sas_ex_discover_end_dev( 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 (unlikely(!rphy)) + if (!rphy) goto out_free; - sas_init_dev(child); - child->rphy = rphy; + get_device(&rphy->dev); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -797,7 +833,7 @@ static struct domain_device *sas_ex_discover_end_dev( } else #endif if (phy->attached_tproto & SAS_PROTOCOL_SSP) { - child->dev_type = SAS_END_DEV; + child->dev_type = SAS_END_DEVICE; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ if (unlikely(!rphy)) @@ -806,6 +842,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_init_dev(child); child->rphy = rphy; + get_device(&rphy->dev); sas_fill_in_rphy(child, rphy); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -830,8 +867,6 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); - child->rphy = NULL; - list_del(&child->disco_list_node); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); @@ -845,7 +880,7 @@ static struct domain_device *sas_ex_discover_end_dev( } /* See if this phy is part of a wide port */ -static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id) +static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id) { struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; int i; @@ -861,11 +896,11 @@ static int sas_ex_join_wide_port(struct domain_device *parent, int phy_id) sas_port_add_phy(ephy->port, phy->phy); phy->port = ephy->port; phy->phy_state = PHY_DEVICE_DISCOVERED; - return 0; + return true; } } - return -ENODEV; + return false; } static struct domain_device *sas_ex_discover_expander( @@ -897,11 +932,11 @@ static struct domain_device *sas_ex_discover_expander( switch (phy->attached_dev_type) { - case EDGE_DEV: + case SAS_EDGE_EXPANDER_DEVICE: rphy = sas_expander_alloc(phy->port, SAS_EDGE_EXPANDER_DEVICE); break; - case FANOUT_DEV: + case SAS_FANOUT_EXPANDER_DEVICE: rphy = sas_expander_alloc(phy->port, SAS_FANOUT_EXPANDER_DEVICE); break; @@ -911,6 +946,7 @@ static struct domain_device *sas_ex_discover_expander( } port = parent->port; child->rphy = rphy; + get_device(&rphy->dev); edev = rphy_to_expander_device(rphy); child->dev_type = phy->attached_dev_type; kref_get(&parent->kref); @@ -934,6 +970,7 @@ static struct domain_device *sas_ex_discover_expander( res = sas_discover_expander(child); if (res) { + sas_rphy_delete(rphy); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); @@ -976,7 +1013,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr)) sas_ex_disable_port(dev, ex_phy->attached_sas_addr); - if (ex_phy->attached_dev_type == NO_DEVICE) { + if (ex_phy->attached_dev_type == SAS_PHY_UNUSED) { if (ex_phy->routing_attr == DIRECT_ROUTING) { memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); sas_configure_routing(dev, ex_phy->attached_sas_addr); @@ -985,10 +1022,10 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) } else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN) return 0; - if (ex_phy->attached_dev_type != SAS_END_DEV && - ex_phy->attached_dev_type != FANOUT_DEV && - ex_phy->attached_dev_type != EDGE_DEV && - ex_phy->attached_dev_type != SATA_PENDING) { + if (ex_phy->attached_dev_type != SAS_END_DEVICE && + ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE && + ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE && + ex_phy->attached_dev_type != SAS_SATA_PENDING) { SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " "phy 0x%x\n", ex_phy->attached_dev_type, SAS_ADDR(dev->sas_addr), @@ -1005,19 +1042,18 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) return res; } - res = sas_ex_join_wide_port(dev, phy_id); - if (!res) { + if (sas_ex_join_wide_port(dev, phy_id)) { SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n", phy_id, SAS_ADDR(ex_phy->attached_sas_addr)); return res; } switch (ex_phy->attached_dev_type) { - case SAS_END_DEV: - case SATA_PENDING: + case SAS_END_DEVICE: + case SAS_SATA_PENDING: child = sas_ex_discover_end_dev(dev, phy_id); break; - case FANOUT_DEV: + case SAS_FANOUT_EXPANDER_DEVICE: if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) { SAS_DPRINTK("second fanout expander %016llx phy 0x%x " "attached to ex %016llx phy 0x%x\n", @@ -1031,7 +1067,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) memcpy(dev->port->disc.fanout_sas_addr, ex_phy->attached_sas_addr, SAS_ADDR_SIZE); /* fallthrough */ - case EDGE_DEV: + case SAS_EDGE_EXPANDER_DEVICE: child = sas_ex_discover_expander(dev, phy_id); break; default: @@ -1052,8 +1088,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) == SAS_ADDR(child->sas_addr)) { ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED; - res = sas_ex_join_wide_port(dev, i); - if (!res) + if (sas_ex_join_wide_port(dev, i)) SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n", i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr)); @@ -1076,8 +1111,8 @@ static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr) phy->phy_state == PHY_NOT_PRESENT) continue; - if ((phy->attached_dev_type == EDGE_DEV || - phy->attached_dev_type == FANOUT_DEV) && + if ((phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE || + phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE) && phy->routing_attr == SUBTRACTIVE_ROUTING) { memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE); @@ -1095,8 +1130,8 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev) u8 sub_addr[8] = {0, }; list_for_each_entry(child, &ex->children, siblings) { - if (child->dev_type != EDGE_DEV && - child->dev_type != FANOUT_DEV) + if (child->dev_type != SAS_EDGE_EXPANDER_DEVICE && + child->dev_type != SAS_FANOUT_EXPANDER_DEVICE) continue; if (sub_addr[0] == 0) { sas_find_sub_addr(child, sub_addr); @@ -1173,7 +1208,7 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev) int i; u8 *sub_sas_addr = NULL; - if (dev->dev_type != EDGE_DEV) + if (dev->dev_type != SAS_EDGE_EXPANDER_DEVICE) return 0; for (i = 0; i < ex->num_phys; i++) { @@ -1183,8 +1218,8 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev) phy->phy_state == PHY_NOT_PRESENT) continue; - if ((phy->attached_dev_type == FANOUT_DEV || - phy->attached_dev_type == EDGE_DEV) && + if ((phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE || + phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE) && phy->routing_attr == SUBTRACTIVE_ROUTING) { if (!sub_sas_addr) @@ -1210,8 +1245,8 @@ static void sas_print_parent_topology_bug(struct domain_device *child, struct ex_phy *child_phy) { static const char *ex_type[] = { - [EDGE_DEV] = "edge", - [FANOUT_DEV] = "fanout", + [SAS_EDGE_EXPANDER_DEVICE] = "edge", + [SAS_FANOUT_EXPANDER_DEVICE] = "fanout", }; struct domain_device *parent = child->parent; @@ -1286,8 +1321,8 @@ static int sas_check_parent_topology(struct domain_device *child) if (!child->parent) return 0; - if (child->parent->dev_type != EDGE_DEV && - child->parent->dev_type != FANOUT_DEV) + if (child->parent->dev_type != SAS_EDGE_EXPANDER_DEVICE && + child->parent->dev_type != SAS_FANOUT_EXPANDER_DEVICE) return 0; parent_ex = &child->parent->ex_dev; @@ -1306,8 +1341,8 @@ static int sas_check_parent_topology(struct domain_device *child) child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; switch (child->parent->dev_type) { - case EDGE_DEV: - if (child->dev_type == FANOUT_DEV) { + 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); @@ -1331,7 +1366,7 @@ static int sas_check_parent_topology(struct domain_device *child) } } break; - case FANOUT_DEV: + 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); @@ -1584,8 +1619,8 @@ static int sas_ex_level_discovery(struct asd_sas_port *port, const int level) struct domain_device *dev; list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (dev->dev_type == EDGE_DEV || - dev->dev_type == FANOUT_DEV) { + if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || + dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); @@ -1685,7 +1720,7 @@ static int sas_get_phy_change_count(struct domain_device *dev, } static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, - u8 *sas_addr, enum sas_dev_type *type) + u8 *sas_addr, enum sas_device_type *type) { int res; struct smp_resp *disc_resp; @@ -1718,9 +1753,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, int phy_change_count = 0; res = sas_get_phy_change_count(dev, i, &phy_change_count); - if (res) - goto out; - else if (phy_change_count != ex->ex_phy[i].phy_change_count) { + switch (res) { + case SMP_RESP_PHY_VACANT: + case SMP_RESP_NO_PHY: + continue; + case SMP_RESP_FUNC_ACC: + break; + default: + return res; + } + + if (phy_change_count != ex->ex_phy[i].phy_change_count) { if (update) ex->ex_phy[i].phy_change_count = phy_change_count; @@ -1728,8 +1771,7 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, return 0; } } -out: - return res; + return 0; } static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) @@ -1770,7 +1812,7 @@ out: * @dev:domain device to be detect. * @src_dev: the device which originated BROADCAST(CHANGE). * - * Add self-configuration expander suport. Suppose two expander cascading, + * Add self-configuration expander support. Suppose two expander cascading, * when the first level expander is self-configuring, hotplug the disks in * second level expander, BROADCAST(CHANGE) will not only be originated * in the second level expander, but also be originated in the first level @@ -1807,7 +1849,7 @@ static int sas_find_bcast_dev(struct domain_device *dev, SAS_DPRINTK("Expander phys DID NOT change\n"); } list_for_each_entry(ch, &ex->children, siblings) { - if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV) { + if (ch->dev_type == SAS_EDGE_EXPANDER_DEVICE || ch->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { res = sas_find_bcast_dev(ch, src_dev); if (*src_dev) return res; @@ -1824,8 +1866,8 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi list_for_each_entry_safe(child, n, &ex->children, siblings) { set_bit(SAS_DEV_GONE, &child->state); - if (child->dev_type == EDGE_DEV || - child->dev_type == FANOUT_DEV) + if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || + child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) sas_unregister_ex_tree(port, child); else sas_unregister_dev(port, child); @@ -1845,8 +1887,8 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, if (SAS_ADDR(child->sas_addr) == SAS_ADDR(phy->attached_sas_addr)) { set_bit(SAS_DEV_GONE, &child->state); - if (child->dev_type == EDGE_DEV || - child->dev_type == FANOUT_DEV) + if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || + child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); @@ -1874,8 +1916,8 @@ static int sas_discover_bfs_by_root_level(struct domain_device *root, int res = 0; list_for_each_entry(child, &ex_root->children, siblings) { - if (child->dev_type == EDGE_DEV || - child->dev_type == FANOUT_DEV) { + if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || + child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { struct sas_expander_device *ex = rphy_to_expander_device(child->rphy); @@ -1911,55 +1953,42 @@ static int sas_discover_new(struct domain_device *dev, int phy_id) { struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id]; struct domain_device *child; - bool found = false; - int res, i; + int res; SAS_DPRINTK("ex %016llx phy%d new device attached\n", SAS_ADDR(dev->sas_addr), phy_id); res = sas_ex_phy_discover(dev, phy_id); if (res) - goto out; - /* to support the wide port inserted */ - for (i = 0; i < dev->ex_dev.num_phys; i++) { - struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i]; - if (i == phy_id) - continue; - if (SAS_ADDR(ex_phy_temp->attached_sas_addr) == - SAS_ADDR(ex_phy->attached_sas_addr)) { - found = true; - break; - } - } - if (found) { - sas_ex_join_wide_port(dev, phy_id); + return res; + + if (sas_ex_join_wide_port(dev, phy_id)) return 0; - } + res = sas_ex_discover_devices(dev, phy_id); - if (!res) - goto out; + if (res) + return res; list_for_each_entry(child, &dev->ex_dev.children, siblings) { if (SAS_ADDR(child->sas_addr) == SAS_ADDR(ex_phy->attached_sas_addr)) { - if (child->dev_type == EDGE_DEV || - child->dev_type == FANOUT_DEV) + if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || + child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) res = sas_discover_bfs_by_root(child); break; } } -out: return res; } -static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old) +static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) { if (old == new) return true; /* treat device directed resets as flutter, if we went - * SAS_END_DEV to SATA_PENDING the link needs recovery + * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery */ - if ((old == SATA_PENDING && new == SAS_END_DEV) || - (old == SAS_END_DEV && new == SATA_PENDING)) + if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) || + (old == SAS_END_DEVICE && new == SAS_SATA_PENDING)) return true; return false; @@ -1969,10 +1998,11 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) { struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; - enum sas_dev_type type = NO_DEVICE; + enum sas_device_type type = SAS_PHY_UNUSED; u8 sas_addr[8]; int res; + memset(sas_addr, 0, 8); res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); switch (res) { case SMP_RESP_NO_PHY: @@ -1985,9 +2015,13 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) return res; case SMP_RESP_FUNC_ACC: break; + case -ECOMM: + break; + default: + return res; } - if (SAS_ADDR(sas_addr) == 0) { + if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); return res; @@ -1998,7 +2032,7 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) sas_ex_phy_discover(dev, phy_id); - if (ata_dev && phy->attached_dev_type == SATA_PENDING) + if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) action = ", needs recovery"; SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", SAS_ADDR(dev->sas_addr), phy_id, action); @@ -2077,9 +2111,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) struct domain_device *dev = NULL; res = sas_find_bcast_dev(port_dev, &dev); - if (res) - goto out; - if (dev) { + while (res == 0 && dev) { struct expander_device *ex = &dev->ex_dev; int i = 0, phy_id; @@ -2091,8 +2123,10 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) res = sas_rediscover(dev, phy_id); i = phy_id + 1; } while (i < ex->num_phys); + + dev = NULL; + res = sas_find_bcast_dev(port_dev, &dev); } -out: return res; } @@ -2129,10 +2163,10 @@ int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, } /* 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, blk_rq_bytes(req), - rsp->bio->bi_vcnt, blk_rq_bytes(rsp)); + if (bio_multiple_segments(req->bio) || + bio_multiple_segments(rsp->bio)) { + printk("%s: multiple segments req %u, rsp %u\n", + __func__, blk_rq_bytes(req), blk_rq_bytes(rsp)); return -EINVAL; } diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 120bff64be3..dbc8a793fd8 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -48,18 +48,37 @@ struct sas_task *sas_alloc_task(gfp_t flags) 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); +struct sas_task *sas_alloc_slow_task(gfp_t flags) +{ + struct sas_task *task = sas_alloc_task(flags); + struct sas_task_slow *slow = kmalloc(sizeof(*slow), flags); + + if (!task || !slow) { + if (task) + kmem_cache_free(sas_task_cache, task); + kfree(slow); + return NULL; + } + + task->slow_task = slow; + init_timer(&slow->timer); + init_completion(&slow->completion); + + return task; +} +EXPORT_SYMBOL_GPL(sas_alloc_slow_task); + void sas_free_task(struct sas_task *task) { if (task) { BUG_ON(!list_empty(&task->list)); + kfree(task->slow_task); kmem_cache_free(sas_task_cache, task); } } @@ -94,8 +113,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) void sas_hae_reset(struct work_struct *work) { - struct sas_ha_event *ev = - container_of(work, struct sas_ha_event, work); + struct sas_ha_event *ev = to_sas_ha_event(work); struct sas_ha_struct *ha = ev->ha; clear_bit(HAE_RESET, &ha->pending); @@ -115,9 +133,11 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) sas_ha->lldd_queue_size = 128; /* Sanity */ set_bit(SAS_HA_REGISTERED, &sas_ha->state); - spin_lock_init(&sas_ha->state_lock); + spin_lock_init(&sas_ha->lock); mutex_init(&sas_ha->drain_mutex); + init_waitqueue_head(&sas_ha->eh_wait_q); INIT_LIST_HEAD(&sas_ha->defer_q); + INIT_LIST_HEAD(&sas_ha->eh_dev_q); error = sas_register_phys(sas_ha); if (error) { @@ -158,18 +178,22 @@ Undo_phys: return error; } -int sas_unregister_ha(struct sas_ha_struct *sas_ha) +static void sas_disable_events(struct sas_ha_struct *sas_ha) { /* Set the state to unregistered to avoid further unchained * events to be queued, and flush any in-progress drainers */ mutex_lock(&sas_ha->drain_mutex); - spin_lock_irq(&sas_ha->state_lock); + spin_lock_irq(&sas_ha->lock); clear_bit(SAS_HA_REGISTERED, &sas_ha->state); - spin_unlock_irq(&sas_ha->state_lock); + spin_unlock_irq(&sas_ha->lock); __sas_drain_work(sas_ha); mutex_unlock(&sas_ha->drain_mutex); +} +int sas_unregister_ha(struct sas_ha_struct *sas_ha) +{ + sas_disable_events(sas_ha); sas_unregister_ports(sas_ha); /* flush unregistration work */ @@ -361,6 +385,90 @@ int sas_set_phy_speed(struct sas_phy *phy, return ret; } +void sas_prep_resume_ha(struct sas_ha_struct *ha) +{ + int i; + + set_bit(SAS_HA_REGISTERED, &ha->state); + + /* clear out any stale link events/data from the suspension path */ + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + phy->port_events_pending = 0; + phy->phy_events_pending = 0; + phy->frame_rcvd_size = 0; + } +} +EXPORT_SYMBOL(sas_prep_resume_ha); + +static int phys_suspended(struct sas_ha_struct *ha) +{ + int i, rc = 0; + + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + + if (phy->suspended) + rc++; + } + + return rc; +} + +void sas_resume_ha(struct sas_ha_struct *ha) +{ + const unsigned long tmo = msecs_to_jiffies(25000); + int i; + + /* deform ports on phys that did not resume + * at this point we may be racing the phy coming back (as posted + * by the lldd). So we post the event and once we are in the + * libsas context check that the phy remains suspended before + * tearing it down. + */ + i = phys_suspended(ha); + if (i) + dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", + i, i > 1 ? "s" : ""); + wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_phy *phy = ha->sas_phy[i]; + + if (phy->suspended) { + dev_warn(&phy->phy->dev, "resume timeout\n"); + sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT); + } + } + + /* 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); + sas_drain_work(ha); +} +EXPORT_SYMBOL(sas_resume_ha); + +void sas_suspend_ha(struct sas_ha_struct *ha) +{ + int i; + + sas_disable_events(ha); + scsi_block_requests(ha->core.shost); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + + sas_discover_event(port, DISCE_SUSPEND); + } + + /* flush suspend events while unregistered */ + mutex_lock(&ha->drain_mutex); + __sas_drain_work(ha); + mutex_unlock(&ha->drain_mutex); +} +EXPORT_SYMBOL(sas_suspend_ha); + static void sas_phy_release(struct sas_phy *phy) { kfree(phy->hostdata); @@ -369,14 +477,14 @@ static void sas_phy_release(struct sas_phy *phy) static void phy_reset_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); + struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work); d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static void phy_enable_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), enable_work); + struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work); d->enable_result = sas_phy_enable(d->phy, d->enable); } @@ -389,8 +497,8 @@ static int sas_phy_setup(struct sas_phy *phy) return -ENOMEM; mutex_init(&d->event_lock); - INIT_WORK(&d->reset_work, phy_reset_work); - INIT_WORK(&d->enable_work, phy_enable_work); + INIT_SAS_WORK(&d->reset_work, phy_reset_work); + INIT_SAS_WORK(&d->enable_work, phy_enable_work); d->phy = phy; phy->hostdata = d; @@ -412,9 +520,9 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) d->reset_result = 0; d->hard_reset = hard_reset; - spin_lock_irq(&ha->state_lock); + spin_lock_irq(&ha->lock); sas_queue_work(ha, &d->reset_work); - spin_unlock_irq(&ha->state_lock); + spin_unlock_irq(&ha->lock); rc = sas_drain_work(ha); if (rc == 0) @@ -439,9 +547,9 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) d->enable_result = 0; d->enable = enable; - spin_lock_irq(&ha->state_lock); + spin_lock_irq(&ha->lock); sas_queue_work(ha, &d->enable_work); - spin_unlock_irq(&ha->state_lock); + spin_unlock_irq(&ha->lock); rc = sas_drain_work(ha); if (rc == 0) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index f05c6387994..7e7ba83f0a2 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -45,10 +45,10 @@ struct sas_phy_data { struct mutex event_lock; int hard_reset; int reset_result; - struct work_struct reset_work; + struct sas_work reset_work; int enable; int enable_result; - struct work_struct enable_work; + struct sas_work enable_work; }; void sas_scsi_recover_host(struct Scsi_Host *shost); @@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work); -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work); +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); @@ -89,6 +89,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); +void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event); void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); @@ -130,16 +131,16 @@ static inline void sas_fill_in_rphy(struct domain_device *dev, rphy->identify.initiator_port_protocols = dev->iproto; rphy->identify.target_port_protocols = dev->tproto; switch (dev->dev_type) { - case SATA_DEV: + case SAS_SATA_DEV: /* FIXME: need sata device type */ - case SAS_END_DEV: - case SATA_PENDING: + case SAS_END_DEVICE: + case SAS_SATA_PENDING: rphy->identify.device_type = SAS_END_DEVICE; break; - case EDGE_DEV: + case SAS_EDGE_EXPANDER_DEVICE: rphy->identify.device_type = SAS_EDGE_EXPANDER_DEVICE; break; - case FANOUT_DEV: + case SAS_FANOUT_EXPANDER_DEVICE: rphy->identify.device_type = SAS_FANOUT_EXPANDER_DEVICE; break; default: diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index dcfd4a9105c..cdee446c29e 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -32,8 +32,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); @@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) static void sas_phye_oob_done(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); @@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work) static void sas_phye_oob_error(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; @@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work) static void sas_phye_spinup_hold(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = @@ -98,6 +94,25 @@ static void sas_phye_spinup_hold(struct work_struct *work) i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); } +static void sas_phye_resume_timeout(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + struct asd_sas_phy *phy = ev->phy; + + clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); + + /* phew, lldd got the phy back in the nick of time */ + if (!phy->suspended) { + dev_info(&phy->phy->dev, "resume timeout cancelled\n"); + return; + } + + phy->error = 0; + phy->suspended = 0; + sas_deform_port(phy, 1); +} + + /* ---------- Phy class registration ---------- */ int sas_register_phys(struct sas_ha_struct *sas_ha) @@ -109,6 +124,8 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) [PHYE_OOB_DONE] = sas_phye_oob_done, [PHYE_OOB_ERROR] = sas_phye_oob_error, [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, + [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, + }; static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { @@ -127,14 +144,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) phy->error = 0; INIT_LIST_HEAD(&phy->port_phy_el); for (k = 0; k < PORT_NUM_EVENTS; k++) { - INIT_WORK(&phy->port_events[k].work, - sas_port_event_fns[k]); + INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]); phy->port_events[k].phy = phy; } for (k = 0; k < PHY_NUM_EVENTS; k++) { - INIT_WORK(&phy->phy_events[k].work, - sas_phy_event_fns[k]); + INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); phy->phy_events[k].phy = phy; } @@ -144,8 +159,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->core.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 eb19c016d50..d3c5297c6c8 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -39,6 +39,49 @@ static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy return true; } +static void sas_resume_port(struct asd_sas_phy *phy) +{ + struct domain_device *dev; + 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); + + if (si->dft->lldd_port_formed) + si->dft->lldd_port_formed(phy); + + if (port->suspended) + port->suspended = 0; + else { + /* we only need to handle "link returned" actions once */ + return; + } + + /* if the port came back: + * 1/ presume every device came back + * 2/ force the next revalidation to check all expander phys + */ + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + int i, rc; + + rc = sas_notify_lldd_dev_found(dev); + if (rc) { + sas_unregister_dev(port, dev); + continue; + } + + if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + dev->ex_dev.ex_change_count = -1; + for (i = 0; i < dev->ex_dev.num_phys; i++) { + struct ex_phy *phy = &dev->ex_dev.ex_phy[i]; + + phy->phy_change_count = -1; + } + } + } + + sas_discover_event(port, DISCE_RESUME); +} + /** * sas_form_port -- add this phy to a port * @phy: the phy of interest @@ -58,7 +101,14 @@ static void sas_form_port(struct asd_sas_phy *phy) if (port) { if (!phy_is_wideport_member(port, phy)) sas_deform_port(phy, 0); - else { + else if (phy->suspended) { + phy->suspended = 0; + sas_resume_port(phy); + + /* phy came back, try to cancel the timeout */ + wake_up(&sas_ha->eh_wait_q); + return; + } else { SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", __func__, phy->id, phy->port->id, phy->port->num_phys); @@ -123,7 +173,7 @@ static void sas_form_port(struct asd_sas_phy *phy) spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); if (!port->port) { - port->port = sas_port_alloc(phy->phy->dev.parent, phy->id); + port->port = sas_port_alloc(phy->phy->dev.parent, port->id); BUG_ON(!port->port); sas_port_add(port->port); } @@ -208,8 +258,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) void sas_porte_bytes_dmaed(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); @@ -219,8 +268,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) void sas_porte_broadcast_rcvd(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; unsigned long flags; u32 prim; @@ -237,8 +285,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) void sas_porte_link_reset_err(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); @@ -248,8 +295,7 @@ void sas_porte_link_reset_err(struct work_struct *work) void sas_porte_timer_event(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); @@ -259,8 +305,7 @@ void sas_porte_timer_event(struct work_struct *work) void sas_porte_hard_reset(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f0b9b7bf188..25d0f127424 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -167,7 +167,7 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, int_to_scsilun(cmd->device->lun, &lun); memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); task->ssp_task.task_attr = TASK_ATTR_SIMPLE; - memcpy(task->ssp_task.cdb, cmd->cmnd, 16); + task->ssp_task.cmd = cmd; task->scatter = scsi_sglist(cmd); task->num_scatter = scsi_sg_count(cmd); @@ -460,14 +460,109 @@ 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; + } +} +EXPORT_SYMBOL(sas_wait_eh); + +static int sas_queue_reset(struct domain_device *dev, int reset_type, int lun, int wait) +{ + struct sas_ha_struct *ha = dev->port->ha; + int scheduled = 0, tries = 100; + + /* 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; + } + + while (!scheduled && tries--) { + spin_lock_irq(&ha->lock); + if (!test_bit(SAS_DEV_EH_PENDING, &dev->state) && + !test_bit(reset_type, &dev->state)) { + scheduled = 1; + ha->eh_active++; + list_add_tail(&dev->ssp_dev.eh_list_node, &ha->eh_dev_q); + 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); + } + spin_unlock_irq(&ha->lock); + + if (wait) + sas_wait_eh(dev); + + if (scheduled) + return SUCCESS; + } + + SAS_DPRINTK("%s reset of %s failed\n", + reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus", + dev_name(&dev->rphy->dev)); + + return FAILED; +} + +int sas_eh_abort_handler(struct scsi_cmnd *cmd) +{ + int res; + struct sas_task *task = TO_SAS_TASK(cmd); + struct Scsi_Host *host = cmd->device->host; + struct sas_internal *i = to_sas_internal(host->transportt); + + if (current != host->ehandler) + return FAILED; + + if (!i->dft->lldd_abort_task) + return FAILED; + + res = i->dft->lldd_abort_task(task); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) + return SUCCESS; + + return FAILED; +} +EXPORT_SYMBOL_GPL(sas_eh_abort_handler); + /* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) { - struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); - struct scsi_lun lun; int res; + struct scsi_lun lun; + struct Scsi_Host *host = cmd->device->host; + struct domain_device *dev = cmd_to_domain_dev(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); int_to_scsilun(cmd->device->lun, &lun); @@ -481,21 +576,22 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) return FAILED; } -/* Attempt to send a phy (bus) reset */ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { - struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_phy *phy = sas_get_local_phy(dev); int res; + struct Scsi_Host *host = cmd->device->host; + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_internal *i = to_sas_internal(host->transportt); - res = sas_phy_reset(phy, 1); - if (res) - SAS_DPRINTK("Bus reset of %s failed 0x%x\n", - kobject_name(&phy->dev.kobj), - res); - sas_put_local_phy(phy); + if (current != host->ehandler) + return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); - if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) + if (!i->dft->lldd_I_T_nexus_reset) + return FAILED; + + res = i->dft->lldd_I_T_nexus_reset(dev); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE || + res == -ENODEV) return SUCCESS; return FAILED; @@ -667,16 +763,53 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * goto out; } +static void sas_eh_handle_resets(struct Scsi_Host *shost) +{ + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_internal *i = to_sas_internal(shost->transportt); + + /* handle directed resets to sas devices */ + spin_lock_irq(&ha->lock); + while (!list_empty(&ha->eh_dev_q)) { + struct domain_device *dev; + struct ssp_device *ssp; + + ssp = list_entry(ha->eh_dev_q.next, typeof(*ssp), eh_list_node); + list_del_init(&ssp->eh_list_node); + dev = container_of(ssp, typeof(*dev), ssp_dev); + kref_get(&dev->kref); + WARN_ONCE(dev_is_sata(dev), "ssp reset to ata device?\n"); + + spin_unlock_irq(&ha->lock); + + if (test_and_clear_bit(SAS_DEV_LU_RESET, &dev->state)) + i->dft->lldd_lu_reset(dev, ssp->reset_lun.scsi_lun); + + if (test_and_clear_bit(SAS_DEV_RESET, &dev->state)) + i->dft->lldd_I_T_nexus_reset(dev); + + sas_put_device(dev); + spin_lock_irq(&ha->lock); + clear_bit(SAS_DEV_EH_PENDING, &dev->state); + ha->eh_active--; + } + spin_unlock_irq(&ha->lock); +} + + void sas_scsi_recover_host(struct Scsi_Host *shost) { struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); - unsigned long flags; LIST_HEAD(eh_work_q); + int tries = 0; + bool retry; - spin_lock_irqsave(shost->host_lock, flags); +retry: + tries++; + retry = true; + spin_lock_irq(shost->host_lock); list_splice_init(&shost->eh_cmd_q, &eh_work_q); - shost->host_eh_scheduled = 0; - spin_unlock_irqrestore(shost->host_lock, flags); + spin_unlock_irq(shost->host_lock); SAS_DPRINTK("Enter %s busy: %d failed: %d\n", __func__, shost->host_busy, shost->host_failed); @@ -705,18 +838,31 @@ out: if (ha->lldd_max_execute_num > 1) wake_up_process(ha->core.queue_thread); + sas_eh_handle_resets(shost); + /* now link into libata eh --- if we have any ata devices */ sas_ata_strategy_handler(shost); scsi_eh_flush_done_q(&ha->eh_done_q); - SAS_DPRINTK("--- Exit %s: busy: %d failed: %d\n", - __func__, shost->host_busy, shost->host_failed); + /* check if any new eh work was scheduled during the last run */ + spin_lock_irq(&ha->lock); + if (ha->eh_active == 0) { + shost->host_eh_scheduled = 0; + retry = false; + } + spin_unlock_irq(&ha->lock); + + if (retry) + goto retry; + + SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", + __func__, shost->host_busy, shost->host_failed, tries); } enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { - scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd); + scmd_dbg(cmd, "command %p timed out\n", cmd); return BLK_EH_NOT_HANDLED; } @@ -988,9 +1134,13 @@ void sas_task_abort(struct sas_task *task) /* Escape for libsas internal commands */ if (!sc) { - if (!del_timer(&task->timer)) + struct sas_task_slow *slow = task->slow_task; + + if (!slow) + return; + if (!del_timer(&slow->timer)) return; - task->timer.function(task->timer.data); + slow->timer.function(slow->timer.data); return; } @@ -1003,7 +1153,6 @@ void sas_task_abort(struct sas_task *task) spin_lock_irqsave(q->queue_lock, flags); blk_abort_request(sc->request); spin_unlock_irqrestore(q->queue_lock, flags); - scsi_schedule_eh(sc->device->host); } } |
