diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 12:55:29 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 12:55:29 -0700 |
commit | 424a6f6ef990b7e9f56f6627bfc6c46b493faeb4 (patch) | |
tree | 0028356ed8003495fbbe1f716f359e3c8ebc35b6 /drivers/scsi/libsas | |
parent | 1ab142d499294b844ecc81e8004db4ce029b0b61 (diff) | |
parent | cd8df932d894f3128c884e3ae1b2b484540513db (diff) |
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
SCSI updates from James Bottomley:
"The update includes the usual assortment of driver updates (lpfc,
qla2xxx, qla4xxx, bfa, bnx2fc, bnx2i, isci, fcoe, hpsa) plus a huge
amount of infrastructure work in the SAS library and transport class
as well as an iSCSI update. There's also a new SCSI based virtio
driver."
* tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (177 commits)
[SCSI] qla4xxx: Update driver version to 5.02.00-k15
[SCSI] qla4xxx: trivial cleanup
[SCSI] qla4xxx: Fix sparse warning
[SCSI] qla4xxx: Add support for multiple session per host.
[SCSI] qla4xxx: Export CHAP index as sysfs attribute
[SCSI] scsi_transport: Export CHAP index as sysfs attribute
[SCSI] qla4xxx: Add support to display CHAP list and delete CHAP entry
[SCSI] iscsi_transport: Add support to display CHAP list and delete CHAP entry
[SCSI] pm8001: fix endian issue with code optimization.
[SCSI] pm8001: Fix possible racing condition.
[SCSI] pm8001: Fix bogus interrupt state flag issue.
[SCSI] ipr: update PCI ID definitions for new adapters
[SCSI] qla2xxx: handle default case in qla2x00_request_firmware()
[SCSI] isci: improvements in driver unloading routine
[SCSI] isci: improve phy event warnings
[SCSI] isci: debug, provide state-enum-to-string conversions
[SCSI] scsi_transport_sas: 'enable' phys on reset
[SCSI] libsas: don't recover end devices attached to disabled phys
[SCSI] libsas: fixup target_port_protocols for expanders that don't report sata
[SCSI] libsas: set attached device type and target protocols for local phys
...
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 828 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 246 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 96 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 342 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 11 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 214 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 97 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 12 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_port.c | 32 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 364 |
10 files changed, 1345 insertions, 897 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index db9238f2ecb..bc0cecc6ad6 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -23,6 +23,8 @@ #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/async.h> +#include <linux/export.h> #include <scsi/sas_ata.h> #include "sas_internal.h" @@ -93,22 +95,47 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) static void sas_ata_task_done(struct sas_task *task) { struct ata_queued_cmd *qc = task->uldd_task; - struct domain_device *dev; + struct domain_device *dev = task->dev; struct task_status_struct *stat = &task->task_status; struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; - struct sas_ha_struct *sas_ha; + struct sas_ha_struct *sas_ha = dev->port->ha; enum ata_completion_errors ac; unsigned long flags; struct ata_link *link; + struct ata_port *ap; + + spin_lock_irqsave(&dev->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &sas_ha->state)) + task = NULL; + else if (qc && qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); + spin_unlock_irqrestore(&dev->done_lock, flags); + + /* check if libsas-eh got to the task before us */ + if (unlikely(!task)) + return; if (!qc) goto qc_already_gone; - dev = qc->ap->private_data; - sas_ha = dev->port->ha; - link = &dev->sata_dev.ap->link; + ap = qc->ap; + link = &ap->link; + + spin_lock_irqsave(ap->lock, flags); + /* check if we lost the race with libata/sas_ata_post_internal() */ + if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) { + spin_unlock_irqrestore(ap->lock, flags); + if (qc->scsicmd) + goto qc_already_gone; + else { + /* if eh is not involved and the port is frozen then the + * ata internal abort process has taken responsibility + * for this sas_task + */ + return; + } + } - spin_lock_irqsave(dev->sata_dev.ap->lock, flags); 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))) { @@ -121,10 +148,6 @@ static void sas_ata_task_done(struct sas_task *task) if (unlikely(link->eh_info.err_mask)) qc->flags |= ATA_QCFLAG_FAILED; } - - dev->sata_dev.sstatus = resp->sstatus; - dev->sata_dev.serror = resp->serror; - dev->sata_dev.scontrol = resp->scontrol; } else { ac = sas_to_ata_err(stat); if (ac) { @@ -144,24 +167,8 @@ static void sas_ata_task_done(struct sas_task *task) } qc->lldd_task = NULL; - if (qc->scsicmd) - ASSIGN_SAS_TASK(qc->scsicmd, NULL); ata_qc_complete(qc); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); - - /* - * If the sas_task has an ata qc, a scsi_cmnd and the aborted - * flag is set, then we must have come in via the libsas EH - * functions. When we exit this function, we need to put the - * scsi_cmnd on the list of finished errors. The ata_qc_complete - * call cleans up the libata side of things but we're protected - * from the scsi_cmnd going away because the scsi_cmnd is owned - * by the EH, making libata's call to scsi_done a NOP. - */ - spin_lock_irqsave(&task->task_state_lock, flags); - if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED) - scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q); - spin_unlock_irqrestore(&task->task_state_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); qc_already_gone: list_del_init(&task->list); @@ -170,23 +177,30 @@ qc_already_gone: static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) { - int res; + unsigned long flags; struct sas_task *task; - struct domain_device *dev = qc->ap->private_data; + struct scatterlist *sg; + int ret = AC_ERR_SYSTEM; + unsigned int si, xfer = 0; + 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 sas_internal *i = to_sas_internal(host->transportt); - struct scatterlist *sg; - unsigned int xfer = 0; - unsigned int si; + + /* TODO: audit callers to ensure they are ready for qc_issue to + * unconditionally re-enable interrupts + */ + local_irq_save(flags); + spin_unlock(ap->lock); /* If the device fell off, no sense in issuing commands */ - if (dev->gone) - return AC_ERR_SYSTEM; + if (test_bit(SAS_DEV_GONE, &dev->state)) + goto out; task = sas_alloc_task(GFP_ATOMIC); if (!task) - return AC_ERR_SYSTEM; + goto out; task->dev = dev; task->task_proto = SAS_PROTOCOL_STP; task->task_done = sas_ata_task_done; @@ -231,21 +245,24 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) ASSIGN_SAS_TASK(qc->scsicmd, task); if (sas_ha->lldd_max_execute_num < 2) - res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + ret = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); else - res = sas_queue_up(task); + ret = sas_queue_up(task); /* Examine */ - if (res) { - SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + if (ret) { + SAS_DPRINTK("lldd_execute_task returned: %d\n", ret); if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, NULL); sas_free_task(task); - return AC_ERR_SYSTEM; + ret = AC_ERR_SYSTEM; } - return 0; + out: + spin_lock(ap->lock); + local_irq_restore(flags); + return ret; } static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) @@ -256,83 +273,222 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) return true; } -static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) +{ + return to_sas_internal(dev->port->ha->core.shost->transportt); +} + +static void sas_get_ata_command_set(struct domain_device *dev); + +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; + + if (phy->attached_dev_type == SATA_PENDING) + dev->dev_type = SATA_PENDING; + else { + int res; + + dev->dev_type = SATA_DEV; + res = sas_get_report_phy_sata(dev->parent, phy->phy_id, + &dev->sata_dev.rps_resp); + if (res) { + SAS_DPRINTK("report phy sata to %016llx:0x%x returned " + "0x%x\n", SAS_ADDR(dev->parent->sas_addr), + phy->phy_id, res); + return res; + } + memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis, + sizeof(struct dev_to_host_fis)); + /* TODO switch to ata_dev_classify() */ + sas_get_ata_command_set(dev); + } + return 0; +} + +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) + return 1; + + /* hmmm, if this succeeds do we need to repost the domain_device to the + * lldd so it can pick up new parameters? + */ + res = sas_get_ata_info(dev, phy); + if (res) + return 0; /* retry */ + else + return 1; +} + +static int smp_ata_check_ready(struct ata_link *link) +{ + int res; struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); - int res = TMF_RESP_FUNC_FAILED; - int ret = 0; + struct domain_device *ex_dev = dev->parent; + struct sas_phy *phy = sas_get_local_phy(dev); + struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number]; - if (i->dft->lldd_I_T_nexus_reset) - res = i->dft->lldd_I_T_nexus_reset(dev); + res = sas_ex_phy_discover(ex_dev, phy->number); + sas_put_local_phy(phy); - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); - ret = -EAGAIN; + /* break the wait early if the expander is unreachable, + * otherwise keep polling + */ + if (res == -ECOMM) + return res; + if (res != SMP_RESP_FUNC_ACC) + return 0; + + switch (ex_phy->attached_dev_type) { + case SATA_PENDING: + return 0; + case SAS_END_DEV: + if (ex_phy->attached_sata_dev) + return sas_ata_clear_pending(dev, ex_phy); + default: + return -ENODEV; } +} - switch (dev->sata_dev.command_set) { - case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __func__); - *class = ATA_DEV_ATA; - break; - case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); - *class = ATA_DEV_ATAPI; - break; - default: - SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __func__, - dev->sata_dev.command_set); - *class = ATA_DEV_UNKNOWN; - break; +static int local_ata_check_ready(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct domain_device *dev = ap->private_data; + struct sas_internal *i = dev_to_sas_internal(dev); + + if (i->dft->lldd_ata_check_ready) + return i->dft->lldd_ata_check_ready(dev); + else { + /* lldd's that don't implement 'ready' checking get the + * old default behavior of not coordinating reset + * recovery with libata + */ + return 1; } +} - ap->cbl = ATA_CBL_SATA; - return ret; +static int sas_ata_printk(const char *level, const struct domain_device *ddev, + const char *fmt, ...) +{ + struct ata_port *ap = ddev->sata_dev.ap; + struct device *dev = &ddev->rphy->dev; + struct va_format vaf; + va_list args; + int r; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + r = printk("%ssas: ata%u: %s: %pV", + level, ap->print_id, dev_name(dev), &vaf); + + va_end(args); + + return r; } -static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { + int ret = 0, res; + struct sas_phy *phy; struct ata_port *ap = link->ap; + int (*check_ready)(struct ata_link *link); struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); - int res = TMF_RESP_FUNC_FAILED; - int ret = 0; + struct sas_internal *i = dev_to_sas_internal(dev); - if (i->dft->lldd_ata_soft_reset) - res = i->dft->lldd_ata_soft_reset(dev); + res = i->dft->lldd_I_T_nexus_reset(dev); + if (res == -ENODEV) + return res; - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to soft reset\n", __func__); - ret = -EAGAIN; - } + if (res != TMF_RESP_FUNC_COMPLETE) + sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n"); + + phy = sas_get_local_phy(dev); + if (scsi_is_sas_phy_local(phy)) + check_ready = local_ata_check_ready; + else + check_ready = smp_ata_check_ready; + sas_put_local_phy(phy); + + ret = ata_wait_after_reset(link, deadline, check_ready); + if (ret && ret != -EAGAIN) + sas_ata_printk(KERN_ERR, dev, "reset failed (errno=%d)\n", ret); + /* XXX: if the class changes during the reset the upper layer + * should be informed, if the device has gone away we assume + * libsas will eventually delete it + */ switch (dev->sata_dev.command_set) { case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __func__); *class = ATA_DEV_ATA; break; case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __func__); *class = ATA_DEV_ATAPI; break; - default: - SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __func__, dev->sata_dev.command_set); - *class = ATA_DEV_UNKNOWN; - break; } ap->cbl = ATA_CBL_SATA; return ret; } +/* + * notify the lldd to forget the sas_task for this internal ata command + * that bypasses scsi-eh + */ +static void sas_ata_internal_abort(struct sas_task *task) +{ + struct sas_internal *si = dev_to_sas_internal(task->dev); + unsigned long flags; + int res; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED || + task->task_state_flags & SAS_TASK_STATE_DONE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("%s: Task %p already finished.\n", __func__, + task); + goto out; + } + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + res = si->dft->lldd_abort_task(task); + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_DONE || + res == TMF_RESP_FUNC_COMPLETE) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + goto out; + } + + /* XXX we are not prepared to deal with ->lldd_abort_task() + * failures. TODO: lldds need to unconditionally forget about + * aborted ata tasks, otherwise we (likely) leak the sas task + * here + */ + SAS_DPRINTK("%s: Task %p leaked.\n", __func__, task); + + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) + task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return; + out: + list_del_init(&task->list); + sas_free_task(task); +} + static void sas_ata_post_internal(struct ata_queued_cmd *qc) { if (qc->flags & ATA_QCFLAG_FAILED) @@ -340,30 +496,35 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) if (qc->err_mask) { /* - * Find the sas_task and kill it. By this point, - * libata has decided to kill the qc, so we needn't - * bother with sas_ata_task_done. But we still - * ought to abort the task. + * Find the sas_task and kill it. By this point, libata + * has decided to kill the qc and has frozen the port. + * In this state sas_ata_task_done() will no longer free + * the sas_task, so we need to notify the lldd (via + * ->lldd_abort_task) that the task is dead and free it + * ourselves. */ struct sas_task *task = qc->lldd_task; - unsigned long flags; qc->lldd_task = NULL; - if (task) { - /* Should this be a AT(API) device reset? */ - spin_lock_irqsave(&task->task_state_lock, flags); - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - task->uldd_task = NULL; - __sas_task_abort(task); - } + if (!task) + return; + task->uldd_task = NULL; + sas_ata_internal_abort(task); } } + +static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev) +{ + struct domain_device *dev = ap->private_data; + struct sas_internal *i = dev_to_sas_internal(dev); + + if (i->dft->lldd_ata_set_dmamode) + i->dft->lldd_ata_set_dmamode(dev); +} + static struct ata_port_operations sas_sata_ops = { .prereset = ata_std_prereset, - .softreset = sas_ata_soft_reset, .hardreset = sas_ata_hard_reset, .postreset = ata_std_postreset, .error_handler = ata_std_error_handler, @@ -374,6 +535,7 @@ static struct ata_port_operations sas_sata_ops = { .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, }; static struct ata_port_info sata_port_info = { @@ -384,11 +546,10 @@ 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, - struct scsi_target *starget) +int sas_ata_init_host_and_port(struct domain_device *found_dev) { - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_ha_struct *ha = found_dev->port->ha; + struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; ata_host_init(&found_dev->sata_dev.ata_host, @@ -406,6 +567,8 @@ 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(); found_dev->sata_dev.ap = ap; return 0; @@ -436,168 +599,14 @@ void sas_ata_task_abort(struct sas_task *task) complete(waiting); } -static void sas_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->completion); -} - -static void sas_disc_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -#define SAS_DEV_TIMEOUT 10 - -/** - * sas_execute_task -- Basic task processing for discovery - * @task: the task to be executed - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction. DMA_xxx - */ -static int sas_execute_task(struct sas_task *task, void *buffer, int size, - enum dma_data_direction dma_dir) -{ - int res = 0; - struct scatterlist *scatter = NULL; - struct task_status_struct *ts = &task->task_status; - int num_scatter = 0; - int retries = 0; - struct sas_internal *i = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - - if (dma_dir != DMA_NONE) { - scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); - if (!scatter) - goto out; - - sg_init_one(scatter, buffer, size); - num_scatter = 1; - } - - task->task_proto = task->dev->tproto; - task->scatter = scatter; - task->num_scatter = num_scatter; - task->total_xfer_len = size; - task->data_dir = dma_dir; - task->task_done = sas_disc_task_done; - if (dma_dir != DMA_NONE && - sas_protocol_ata(task->task_proto)) { - task->num_scatter = dma_map_sg(task->dev->port->ha->dev, - task->scatter, - task->num_scatter, - task->data_dir); - } - - for (retries = 0; retries < 5; retries++) { - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_completion(&task->completion); - - task->timer.data = (unsigned long) task; - task->timer.function = sas_task_timedout; - task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; - add_timer(&task->timer); - - res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); - if (res) { - del_timer(&task->timer); - SAS_DPRINTK("executing SAS discovery task failed:%d\n", - res); - goto ex_err; - } - wait_for_completion(&task->completion); - res = -ECOMM; - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { - int res2; - SAS_DPRINTK("task aborted, flags:0x%x\n", - task->task_state_flags); - res2 = i->dft->lldd_abort_task(task); - SAS_DPRINTK("came back from abort task\n"); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - if (res2 == TMF_RESP_FUNC_COMPLETE) - continue; /* Retry the task */ - else - goto ex_err; - } - } - if (task->task_status.stat == SAM_STAT_BUSY || - task->task_status.stat == SAM_STAT_TASK_SET_FULL || - task->task_status.stat == SAS_QUEUE_FULL) { - SAS_DPRINTK("task: q busy, sleeping...\n"); - schedule_timeout_interruptible(HZ); - } else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) { - struct scsi_sense_hdr shdr; - - if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, - &shdr)) { - SAS_DPRINTK("couldn't normalize sense\n"); - continue; - } - if ((shdr.sense_key == 6 && shdr.asc == 0x29) || - (shdr.sense_key == 2 && shdr.asc == 4 && - shdr.ascq == 1)) { - SAS_DPRINTK("device %016llx LUN: %016llx " - "powering up or not ready yet, " - "sleeping...\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN)); - - schedule_timeout_interruptible(5*HZ); - } else if (shdr.sense_key == 1) { - res = 0; - break; - } else if (shdr.sense_key == 5) { - break; - } else { - SAS_DPRINTK("dev %016llx LUN: %016llx " - "sense key:0x%x ASC:0x%x ASCQ:0x%x" - "\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN), - shdr.sense_key, - shdr.asc, shdr.ascq); - } - } else if (task->task_status.resp != SAS_TASK_COMPLETE || - task->task_status.stat != SAM_STAT_GOOD) { - SAS_DPRINTK("task finished with resp:0x%x, " - "stat:0x%x\n", - task->task_status.resp, - task->task_status.stat); - goto ex_err; - } else { - res = 0; - break; - } - } -ex_err: - if (dma_dir != DMA_NONE) { - if (sas_protocol_ata(task->task_proto)) - dma_unmap_sg(task->dev->port->ha->dev, - task->scatter, task->num_scatter, - task->data_dir); - kfree(scatter); - } -out: - return res; -} - -/* ---------- SATA ---------- */ - 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) + return; + if ((fis->sector_count == 1 && /* ATA */ fis->lbal == 1 && fis->lbam == 0 && @@ -636,224 +645,152 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } -/** - * sas_issue_ata_cmd -- Basic SATA command processing for discovery - * @dev: the device to send the command to - * @command: the command register - * @features: the features register - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @dma_dir: DMA direction. DMA_xxx - */ -static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, - u8 features, void *buffer, int size, - enum dma_data_direction dma_dir) -{ - int res = 0; - struct sas_task *task; - struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) - &dev->frame_rcvd[0]; - - res = -ENOMEM; - task = sas_alloc_task(GFP_KERNEL); - if (!task) - goto out; - - task->dev = dev; - - task->ata_task.fis.fis_type = 0x27; - task->ata_task.fis.command = command; - task->ata_task.fis.features = features; - task->ata_task.fis.device = d2h_fis->device; - task->ata_task.retry_count = 1; - - res = sas_execute_task(task, buffer, size, dma_dir); - - sas_free_task(task); -out: - return res; -} - -#define ATA_IDENTIFY_DEV 0xEC -#define ATA_IDENTIFY_PACKET_DEV 0xA1 -#define ATA_SET_FEATURES 0xEF -#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 - -/** - * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) - * @dev: STP/SATA device of interest (ATA/ATAPI) - * - * The LLDD has already been notified of this device, so that we can - * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY - * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its - * performance for this device. - */ -static int sas_discover_sata_dev(struct domain_device *dev) +void sas_probe_sata(struct asd_sas_port *port) { - int res; - __le16 *identify_x; - u8 command; + struct domain_device *dev, *n; + int err; - identify_x = kzalloc(512, GFP_KERNEL); - if (!identify_x) - return -ENOMEM; - - if (dev->sata_dev.command_set == ATA_COMMAND_SET) { - dev->sata_dev.identify_device = identify_x; - command = ATA_IDENTIFY_DEV; - } else { - dev->sata_dev.identify_packet_device = identify_x; - command = ATA_IDENTIFY_PACKET_DEV; - } + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - DMA_FROM_DEVICE); - if (res) - goto out_err; - - /* lives on the media? */ - if (le16_to_cpu(identify_x[0]) & 4) { - /* incomplete response */ - SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " - "dev %llx\n", SAS_ADDR(dev->sas_addr)); - if (!(identify_x[83] & cpu_to_le16(1<<6))) - goto cont1; - res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, - ATA_FEATURE_PUP_STBY_SPIN_UP, - NULL, 0, DMA_NONE); - if (res) - goto cont1; - - schedule_timeout_interruptible(5*HZ); /* More time? */ - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - DMA_FROM_DEVICE); - if (res) - goto out_err; + 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); } -cont1: - /* XXX Hint: register this SATA device with SATL. - When this returns, dev->sata_dev->lu is alive and - present. - sas_satl_register_dev(dev); - */ + mutex_unlock(&port->ha->disco_mutex); - sas_fill_in_rphy(dev, dev->rphy); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; - return 0; -out_err: - dev->sata_dev.identify_packet_device = NULL; - dev->sata_dev.identify_device = NULL; - kfree(identify_x); - return res; -} + sas_ata_wait_eh(dev); -static int sas_discover_sata_pm(struct domain_device *dev) -{ - return -ENODEV; + /* if libata could not bring the link up, don't surface + * the device + */ + if (ata_dev_disabled(sas_to_ata_dev(dev))) + sas_fail_probe(dev, __func__, -ENODEV); + } } /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest * - * First we notify the LLDD of this device, so we can send frames to - * it. Then depending on the type of device we call the appropriate - * discover functions. Once device discover is done, we notify the - * LLDD so that it can fine-tune its parameters for the device, by - * removing it and then adding it. That is, the second time around, - * the driver would have certain fields, that it is looking at, set. - * Finally we initialize the kobj so that the device can be added to - * the system at registration time. Devices directly attached to a HA - * port, have no parents. All other devices do, and should have their - * "parent" pointer set appropriately before calling this function. + * Devices directly attached to a HA port, have no parents. All other + * devices do, and should have their "parent" pointer set appropriately + * before calling this function. */ int sas_discover_sata(struct domain_device *dev) { int res; + if (dev->dev_type == SATA_PM) + return -ENODEV; + sas_get_ata_command_set(dev); + sas_fill_in_rphy(dev, dev->rphy); res = sas_notify_lldd_dev_found(dev); if (res) return res; - switch (dev->dev_type) { - case SATA_DEV: - res = sas_discover_sata_dev(dev); - break; - case SATA_PM: - res = sas_discover_sata_pm(dev); - break; - default: - break; - } - sas_notify_lldd_dev_gone(dev); - if (!res) { - sas_notify_lldd_dev_found(dev); - res = sas_rphy_add(dev->rphy); - } - - return res; + sas_discover_event(dev->port, DISCE_PROBE); + return 0; } -void sas_ata_strategy_handler(struct Scsi_Host *shost) +static void async_sas_ata_eh(void *data, async_cookie_t cookie) { - struct scsi_device *sdev; + struct domain_device *dev = data; + struct ata_port *ap = dev->sata_dev.ap; + struct sas_ha_struct *ha = dev->port->ha; - shost_for_each_device(sdev, shost) { - struct domain_device *ddev = sdev_to_domain_dev(sdev); - struct ata_port *ap = ddev->sata_dev.ap; + /* 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); +} - if (!dev_is_sata(ddev)) - continue; +static bool sas_ata_dev_eh_valid(struct domain_device *dev) +{ + struct ata_port *ap; - ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); - ata_scsi_port_error_handler(shost, ap); - } + if (!dev_is_sata(dev)) + return false; + ap = dev->sata_dev.ap; + /* consume fully initialized ata ports */ + smp_rmb(); + return !!ap; } -int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, - enum blk_eh_timer_return *rtn) +void sas_ata_strategy_handler(struct Scsi_Host *shost) { - struct domain_device *ddev = cmd_to_domain_dev(cmd); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + LIST_HEAD(async); + int i; + + /* it's ok to defer revalidation events during ata eh, these + * disks are in one of three states: + * 1/ present for initial domain discovery, and these + * resets will cause bcn flutters + * 2/ hot removed, we'll discover that after eh fails + * 3/ hot added after initial discovery, lost the race, and need + * to catch the next train. + */ + sas_disable_revalidation(sas_ha); - if (!dev_is_sata(ddev) || task) - return 0; + spin_lock_irq(&sas_ha->phy_port_lock); + for (i = 0; i < sas_ha->num_phys; i++) { + struct asd_sas_port *port = sas_ha->sas_port[i]; + struct domain_device *dev; - /* we're a sata device with no task, so this must be a libata - * eh timeout. Ideally should hook into libata timeout - * handling, but there's no point, it just wants to activate - * the eh thread */ - *rtn = BLK_EH_NOT_HANDLED; - return 1; + spin_lock(&port->dev_list_lock); + list_for_each_entry(dev, &port->dev_list, dev_list_node) { + if (!sas_ata_dev_eh_valid(dev)) + continue; + async_schedule_domain(async_sas_ata_eh, dev, &async); + } + spin_unlock(&port->dev_list_lock); + } + spin_unlock_irq(&sas_ha->phy_port_lock); + + async_synchronize_full_domain(&async); + + sas_enable_revalidation(sas_ha); } -int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q) +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) { - int rtn = 0; struct scsi_cmnd *cmd, *n; - struct ata_port *ap; + struct domain_device *eh_dev; do { LIST_HEAD(sata_q); - - ap = NULL; + eh_dev = NULL; list_for_each_entry_safe(cmd, n, work_q, eh_entry) { struct domain_device *ddev = cmd_to_domain_dev(cmd); if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd)) continue; - if (ap && ap != ddev->sata_dev.ap) + if (eh_dev && eh_dev != ddev) continue; - ap = ddev->sata_dev.ap; - rtn = 1; + eh_dev = ddev; list_move(&cmd->eh_entry, &sata_q); } if (!list_empty(&sata_q)) { - ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n"); + struct ata_port *ap = eh_dev->sata_dev.ap; + + sas_ata_printk(KERN_DEBUG, eh_dev, "cmd error handler\n"); ata_scsi_cmd_error_handler(shost, ap, &sata_q); /* * ata's error handler may leave the cmd on the list @@ -869,7 +806,36 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, while (!list_empty(&sata_q)) list_del_init(sata_q.next); } - } while (ap); + } while (eh_dev); +} + +void sas_ata_schedule_reset(struct domain_device *dev) +{ + struct ata_eh_info *ehi; + struct ata_port *ap; + unsigned long flags; + + if (!dev_is_sata(dev)) + return; + + ap = dev->sata_dev.ap; + ehi = &ap->link.eh_info; + + spin_lock_irqsave(ap->lock, flags); + ehi->err_mask |= AC_ERR_TIMEOUT; + ehi->action |= ATA_EH_RESET; + ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} +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; - return rtn; + ap = dev->sata_dev.ap; + ata_port_wait_eh(ap); } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 54a5199ceb5..36467967560 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -30,29 +30,30 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> +#include <scsi/sas_ata.h> #include "../scsi_sas_internal.h" /* ---------- Basic task processing for discovery purposes ---------- */ void sas_init_dev(struct domain_device *dev) { - INIT_LIST_HEAD(&dev->siblings); - INIT_LIST_HEAD(&dev->dev_list_node); - switch (dev->dev_type) { - case SAS_END_DEV: - break; - case EDGE_DEV: - case FANOUT_DEV: - INIT_LIST_HEAD(&dev->ex_dev.children); - break; - case SATA_DEV: - case SATA_PM: - case SATA_PM_PORT: - INIT_LIST_HEAD(&dev->sata_dev.children); - break; - default: - break; - } + switch (dev->dev_type) { + case SAS_END_DEV: + break; + case EDGE_DEV: + case FANOUT_DEV: + 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; + } } /* ---------- Domain device discovery ---------- */ @@ -68,19 +69,18 @@ void sas_init_dev(struct domain_device *dev) */ static int sas_get_port_device(struct asd_sas_port *port) { - unsigned long flags; struct asd_sas_phy *phy; struct sas_rphy *rphy; struct domain_device *dev; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = sas_alloc_device(); if (!dev) return -ENOMEM; - spin_lock_irqsave(&port->phy_list_lock, flags); + spin_lock_irq(&port->phy_list_lock); if (list_empty(&port->phy_list)) { - spin_unlock_irqrestore(&port->phy_list_lock, flags); - kfree(dev); + spin_unlock_irq(&port->phy_list_lock); + sas_put_device(dev); return -ENODEV; } phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el); @@ -88,7 +88,7 @@ static int sas_get_port_device(struct asd_sas_port *port) memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd), (size_t)phy->frame_rcvd_size)); spin_unlock(&phy->frame_rcvd_lock); - spin_unlock_irqrestore(&port->phy_list_lock, flags); + spin_unlock_irq(&port->phy_list_lock); if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) { struct dev_to_host_fis *fis = @@ -130,9 +130,14 @@ static int sas_get_port_device(struct asd_sas_port *port) } if (!rphy) { - kfree(dev); + sas_put_device(dev); return -ENODEV; } + + 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); @@ -147,11 +152,17 @@ static int sas_get_port_device(struct asd_sas_port *port) memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE); memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE); port->disc.max_level = 0; + sas_device_set_phy(dev, port->port); dev->rphy = rphy; - spin_lock_irq(&port->dev_list_lock); - list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock_irq(&port->dev_list_lock); + + if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV) + list_add_tail(&dev->disco_list_node, &port->disco_list); + else { + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + } return 0; } @@ -173,6 +184,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) dev_name(sas_ha->dev), SAS_ADDR(dev->sas_addr), res); } + kref_get(&dev->kref); } return res; } @@ -184,12 +196,40 @@ 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) { i->dft->lldd_dev_gone(dev); + sas_put_device(dev); + } } -/* ---------- Common/dispatchers ---------- */ +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 asd_sas_port *port = ev->port; + + clear_bit(DISCE_PROBE, &port->disc.pending); + /* devices must be domain members before link recovery and probe */ + list_for_each_entry(dev, &port->disco_list, disco_list_node) { + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + } + + sas_probe_sata(port); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + err = sas_rphy_add(dev->rphy); + if (err) + sas_fail_probe(dev, __func__, err); + else + list_del_init(&dev->disco_list_node); + } +} /** * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -203,22 +243,36 @@ int sas_discover_end_dev(struct domain_device *dev) res = sas_notify_lldd_dev_found(dev); if (res) - goto out_err2; - - res = sas_rphy_add(dev->rphy); - if (res) - goto out_err; + return res; + sas_discover_event(dev->port, DISCE_PROBE); return 0; - -out_err: - sas_notify_lldd_dev_gone(dev); -out_err2: - return res; } /* ---------- Device registration and unregistration ---------- */ +void sas_free_device(struct kref *kref) +{ + struct domain_device *dev = container_of(kref, typeof(*dev), kref); + + if (dev->parent) + sas_put_device(dev->parent); + + sas_port_put_phy(dev->phy); + dev->phy = NULL; + + /* remove the phys and ports, everything else should be gone */ + if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) + kfree(dev->ex_dev.ex_phy); + + if (dev_is_sata(dev) && dev->sata_dev.ap) { + ata_sas_port_destroy(dev->sata_dev.ap); + dev->sata_dev.ap = NULL; + } + + kfree(dev); +} + static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev) { sas_notify_lldd_dev_gone(dev); @@ -230,34 +284,84 @@ 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); spin_unlock_irq(&port->dev_list_lock); + + sas_put_device(dev); } -void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) +static void sas_destruct_devices(struct work_struct *work) { - if (dev->rphy) { + struct domain_device *dev, *n; + struct sas_discovery_event *ev = + container_of(work, struct sas_discovery_event, work); + struct asd_sas_port *port = ev->port; + + clear_bit(DISCE_DESTRUCT, &port->disc.pending); + + list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { + list_del_init(&dev->disco_list_node); + sas_remove_children(&dev->rphy->dev); sas_rphy_delete(dev->rphy); dev->rphy = NULL; + sas_unregister_common_dev(port, dev); } - if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) { - /* remove the phys and ports, everything else should be gone */ - kfree(dev->ex_dev.ex_phy); - dev->ex_dev.ex_phy = NULL; +} + +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) +{ + if (!test_bit(SAS_DEV_DESTROY, &dev->state) && + !list_empty(&dev->disco_list_node)) { + /* 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); + } + + if (dev->rphy && !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); } - sas_unregister_common_dev(port, dev); } -void sas_unregister_domain_devices(struct asd_sas_port *port) +void sas_unregister_domain_devices(struct asd_sas_port *port, int gone) { struct domain_device *dev, *n; - list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) + list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) { + if (gone) + set_bit(SAS_DEV_GONE, &dev->state); + sas_unregister_dev(port, dev); + } + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) sas_unregister_dev(port, dev); port->port->rphy = NULL; } +void sas_device_set_phy(struct domain_device *dev, struct sas_port *port) +{ + struct sas_ha_struct *ha; + struct sas_phy *new_phy; + + if (!dev) + return; + + ha = dev->port->ha; + new_phy = sas_port_get_phy(port); + + /* pin and record last seen phy */ + spin_lock_irq(&ha->phy_port_lock); + if (new_phy) { + sas_port_put_phy(dev->phy); + dev->phy = new_phy; + } + spin_unlock_irq(&ha->phy_port_lock); +} + /* ---------- Discovery and Revalidation ---------- */ /** @@ -277,8 +381,7 @@ static void sas_discover_domain(struct work_struct *work) container_of(work, struct sas_discovery_event, work); struct asd_sas_port *port = ev->port; - sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock, - &port->disc.pending); + clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); if (port->port_dev) return; @@ -318,11 +421,12 @@ static void sas_discover_domain(struct work_struct *work) 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); spin_unlock_irq(&port->dev_list_lock); - kfree(dev); /* not kobject_register-ed yet */ + sas_put_device(dev); port->port_dev = NULL; } @@ -336,21 +440,51 @@ static void sas_revalidate_domain(struct work_struct *work) struct sas_discovery_event *ev = container_of(work, struct sas_discovery_event, work); struct asd_sas_port *port = ev->port; + struct sas_ha_struct *ha = port->ha; + + /* prevent revalidation from finding sata links in recovery */ + mutex_lock(&ha->disco_mutex); + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { + SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n", + port->id, task_pid_nr(current)); + goto out; + } - sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock, - &port->disc.pending); + clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending); SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, task_pid_nr(current)); + if (port->port_dev) res = sas_ex_revalidate_domain(port->port_dev); SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", port->id, task_pid_nr(current), res); + out: + mutex_unlock(&ha->disco_mutex); } /* ---------- Events ---------- */ +static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) +{ + /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ + scsi_queue_work(ha->core.shost, work); +} + +static void sas_chain_event(int event, unsigned long *pending, + struct work_struct *work, + 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); + } +} + int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) { struct sas_discovery *disc; @@ -361,8 +495,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) BUG_ON(ev >= DISC_NUM_EVENTS); - sas_queue_event(ev, &disc->disc_event_lock, &disc->pending, - &disc->disc_work[ev].work, port->ha); + sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); return 0; } @@ -380,9 +513,10 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, + [DISCE_PROBE] = sas_probe_devices, + [DISCE_DESTRUCT] = sas_destruct_devices, }; - spin_lock_init(&disc->disc_event_lock); disc->pending = 0; for (i = 0; i < DISC_NUM_EVENTS; i++) { INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 9db30fb5caf..16639bbae62 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -22,15 +22,103 @@ * */ +#include <linux/export.h> #include <scsi/scsi_host.h> #include "sas_internal.h" #include "sas_dump.h" +void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +{ + 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); +} + +static void sas_queue_event(int event, unsigned long *pending, + struct work_struct *work, + struct sas_ha_struct *ha) +{ + if (!test_and_set_bit(event, pending)) { + unsigned long flags; + + spin_lock_irqsave(&ha->state_lock, flags); + sas_queue_work(ha, work); + spin_unlock_irqrestore(&ha->state_lock, flags); + } +} + + +void __sas_drain_work(struct sas_ha_struct *ha) +{ + struct workqueue_struct *wq = ha->core.shost->work_q; + struct work_struct *w, *_w; + + set_bit(SAS_HA_DRAINING, &ha->state); + /* flush submitters */ + spin_lock_irq(&ha->state_lock); + spin_unlock_irq(&ha->state_lock); + + drain_workqueue(wq); + + spin_lock_irq(&ha->state_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); + } + spin_unlock_irq(&ha->state_lock); +} + +int sas_drain_work(struct sas_ha_struct *ha) +{ + int err; + + err = mutex_lock_interruptible(&ha->drain_mutex); + if (err) + return err; + if (test_bit(SAS_HA_REGISTERED, &ha->state)) + __sas_drain_work(ha); + mutex_unlock(&ha->drain_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(sas_drain_work); + +void sas_disable_revalidation(struct sas_ha_struct *ha) +{ + mutex_lock(&ha->disco_mutex); + set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); + mutex_unlock(&ha->disco_mutex); +} + +void sas_enable_revalidation(struct sas_ha_struct *ha) +{ + int i; + + mutex_lock(&ha->disco_mutex); + clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + const int ev = DISCE_REVALIDATE_DOMAIN; + struct sas_discovery *d = &port->disc; + + if (!test_and_clear_bit(ev, &d->pending)) + continue; + + sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); + } + mutex_unlock(&ha->disco_mutex); +} + static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) { BUG_ON(event >= HA_NUM_EVENTS); - sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, + sas_queue_event(event, &sas_ha->pending, &sas_ha->ha_events[event].work, sas_ha); } @@ -40,7 +128,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) BUG_ON(event >= PORT_NUM_EVENTS); - sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, + sas_queue_event(event, &phy->port_events_pending, &phy->port_events[event].work, ha); } @@ -50,7 +138,7 @@ static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) BUG_ON(event >= PHY_NUM_EVENTS); - sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, + sas_queue_event(event, &phy->phy_events_pending, &phy->phy_events[event].work, ha); } @@ -62,8 +150,6 @@ int sas_init_events(struct sas_ha_struct *sas_ha) int i; - spin_lock_init(&sas_ha->event_lock); - for (i = 0; i < HA_NUM_EVENTS; i++) { INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); sas_ha->ha_events[i].ha = sas_ha; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 1b831c55ec6..05acd9e35fc 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -28,6 +28,7 @@ #include "sas_internal.h" +#include <scsi/sas_ata.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> #include "../scsi_sas_internal.h" @@ -71,11 +72,18 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); + mutex_lock(&dev->ex_dev.cmd_mutex); for (retry = 0; retry < 3; retry++) { - task = sas_alloc_task(GFP_KERNEL); - if (!task) - return -ENOMEM; + if (test_bit(SAS_DEV_GONE, &dev->state)) { + res = -ECOMM; + break; + } + task = sas_alloc_task(GFP_KERNEL); + if (!task) { + res = -ENOMEM; + break; + } task->dev = dev; task->task_proto = dev->tproto; sg_init_one(&task->smp_task.smp_req, req, req_size); @@ -93,7 +101,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, if (res) { del_timer(&task->timer); SAS_DPRINTK("executing SMP task failed:%d\n", res); - goto ex_err; + break; } wait_for_completion(&task->completion); @@ -103,24 +111,30 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, i->dft->lldd_abort_task(task); if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { SAS_DPRINTK("SMP task aborted and not done\n"); - goto ex_err; + break; } } if (task->task_status.resp == SAS_TASK_COMPLETE && task->task_status.stat == SAM_STAT_GOOD) { res = 0; break; - } if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_UNDERRUN) { + } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { /* no error, but return the number of bytes of * underrun */ res = task->task_status.residual; break; - } if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAS_DATA_OVERRUN) { + } + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { res = -EMSGSIZE; break; - } else { + } + if (task->task_status.resp == SAS_TASK_UNDELIVERED && + task->task_status.stat == SAS_DEVICE_UNKNOWN) + break; + else { SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " "status 0x%x\n", __func__, SAS_ADDR(dev->sas_addr), @@ -130,11 +144,10 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task = NULL; } } -ex_err: + mutex_unlock(&dev->ex_dev.cmd_mutex); + BUG_ON(retry == 3 && task != NULL); - if (task != NULL) { - sas_free_task(task); - } + sas_free_task(task); return res; } @@ -153,19 +166,49 @@ static inline void *alloc_smp_resp(int size) return kzalloc(size, GFP_KERNEL); } -/* ---------- Expander configuration ---------- */ +static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) +{ + switch (phy->routing_attr) { + case TABLE_ROUTING: + if (dev->ex_dev.t2t_supp) + return 'U'; + else + return 'T'; + case DIRECT_ROUTING: + return 'D'; + case SUBTRACTIVE_ROUTING: + return 'S'; + default: + return '?'; + } +} + +static enum sas_dev_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 && + dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) + return SATA_PENDING; + else + return dr->attached_dev_type; +} -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, - void *disc_resp) +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) { + enum sas_dev_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 expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; - struct smp_resp *resp = disc_resp; - struct discover_resp *dr = &resp->disc; struct sas_rphy *rphy = dev->rphy; - int rediscover = (phy->phy != NULL); + bool new_phy = !phy->phy; + char *type; - if (!rediscover) { + if (new_phy) { phy->phy = sas_phy_alloc(&rphy->dev, phy_id); /* FIXME: error_handling */ @@ -184,8 +227,13 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, break; } + /* check if anything important changed to squelch debug */ + dev_type = phy->attached_dev_type; + linkrate = phy->linkrate; + memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); + + phy->attached_dev_type = to_dev_type(dr); phy->phy_id = phy_id; - phy->attached_dev_type = dr->attached_dev_type; phy->linkrate = dr->linkrate; phy->attached_sata_host = dr->attached_sata_host; phy->attached_sata_dev = dr->attached_sata_dev; @@ -200,9 +248,11 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->last_da_index = -1; phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); - phy->phy->identify.device_type = phy->attached_dev_type; + phy->phy->identify.device_type = dr->attached_dev_type; phy->phy->identify.initiator_port_protocols = phy->attached_iproto; phy->phy->identify.target_port_protocols = phy->attached_tproto; + if (!phy->attached_tproto && dr->attached_sata_dev) + phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; phy->phy->identify.phy_identifier = phy_id; phy->phy->minimum_linkrate_hw = dr->hmin_linkrate; phy->phy->maximum_linkrate_hw = dr->hmax_linkrate; @@ -210,20 +260,76 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, phy->phy->maximum_linkrate = dr->pmax_linkrate; phy->phy->negotiated_linkrate = phy->linkrate; - if (!rediscover) + if (new_phy) if (sas_phy_add(phy->phy)) { sas_phy_free(phy->phy); return; } - SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n", + switch (phy->attached_dev_type) { + case SATA_PENDING: + type = "stp pending"; + break; + case NO_DEVICE: + type = "no device"; + break; + case SAS_END_DEV: + if (phy->attached_iproto) { + if (phy->attached_tproto) + type = "host+target"; + else + type = "host"; + } else { + if (dr->attached_sata_dev) + type = "stp"; + else + type = "ssp"; + } + break; + case EDGE_DEV: + case FANOUT_DEV: + type = "smp"; + break; + default: + type = "unknown"; + } + + /* this routine is polled by libata error recovery so filter + * unimportant messages + */ + if (new_phy || phy->attached_dev_type != dev_type || + phy->linkrate != linkrate || + SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) + /* pass */; + else + return; + + SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", SAS_ADDR(dev->sas_addr), phy->phy_id, - phy->routing_attr == TABLE_ROUTING ? 'T' : - phy->routing_attr == DIRECT_ROUTING ? 'D' : - phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?', - SAS_ADDR(phy->attached_sas_addr)); + sas_route_char(dev, phy), phy->linkrate, + SAS_ADDR(phy->attached_sas_addr), type); +} + +/* check if we have an existing attached ata device on this expander phy */ +struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +{ + struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; + struct domain_device *dev; + struct sas_rphy *rphy; + + if (!ex_phy->port) + return NULL; - return; + rphy = ex_phy->port->rphy; + if (!rphy) + return NULL; + + dev = sas_find_dev_by_rphy(rphy); + + if (dev && dev_is_sata(dev)) + return dev; + + return NULL; } #define DISCOVER_REQ_SIZE 16 @@ -232,39 +338,25 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, u8 *disc_resp, int single) { - int i, res; + struct discover_resp *dr; + int res; disc_req[9] = single; - for (i = 1 ; i < 3; i++) { - struct discover_resp *dr; - res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (res) - return res; - /* This is detecting a failure to transmit initial - * dev to host FIS as described in section G.5 of - * sas-2 r 04b */ - dr = &((struct smp_resp *)disc_resp)->disc; - if (memcmp(dev->sas_addr, dr->attached_sas_addr, - SAS_ADDR_SIZE) == 0) { - sas_printk("Found loopback topology, just ignore it!\n"); - return 0; - } - if (!(dr->attached_dev_type == 0 && - dr->attached_sata_dev)) - break; - /* In order to generate the dev to host FIS, we - * send a link reset to the expander port */ - sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); - /* Wait for the reset to trigger the negotiation */ - msleep(500); + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + return res; + dr = &((struct smp_resp *)disc_resp)->disc; + if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { + sas_printk("Found loopback topology, just ignore it!\n"); + return 0; } sas_set_ex_phy(dev, single, disc_resp); return 0; } -static int sas_ex_phy_discover(struct domain_device *dev, int single) +int sas_ex_phy_discover(struct domain_device *dev, int single) { struct expander_device *ex = &dev->ex_dev; int res = 0; @@ -569,9 +661,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) #define RPS_REQ_SIZE 16 #define RPS_RESP_SIZE 60 -static int sas_get_report_phy_sata(struct domain_device *dev, - int phy_id, - struct smp_resp *rps_resp) +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, + struct smp_resp *rps_resp) { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); @@ -657,10 +748,11 @@ static struct domain_device *sas_ex_discover_end_dev( if (phy->attached_sata_host || phy->attached_sata_ps) return NULL; - child = kzalloc(sizeof(*child), GFP_KERNEL); + child = sas_alloc_device(); if (!child) return NULL; + kref_get(&parent->kref); child->parent = parent; child->port = parent->port; child->iproto = phy->attached_iproto; @@ -676,24 +768,13 @@ 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) { - child->dev_type = SATA_DEV; - if (phy->attached_tproto & SAS_PROTOCOL_STP) - child->tproto = phy->attached_tproto; - if (phy->attached_sata_dev) - child->tproto |= SATA_DEV; - res = sas_get_report_phy_sata(parent, phy_id, - &child->sata_dev.rps_resp); - if (res) { - SAS_DPRINTK("report phy sata to %016llx:0x%x returned " - "0x%x\n", SAS_ADDR(parent->sas_addr), - phy_id, res); + res = sas_get_ata_info(child, phy); + if (res) goto out_free; - } - memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, - sizeof(struct dev_to_host_fis)); rphy = sas_end_device_alloc(phy->port); if (unlikely(!rphy)) @@ -703,9 +784,7 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; - spin_lock_irq(&parent->port->dev_list_lock); - list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->disco_list_node, &parent->port->disco_list); res = sas_discover_sata(child); if (res) { @@ -729,9 +808,7 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; sas_fill_in_rphy(child, rphy); - spin_lock_irq(&parent->port->dev_list_lock); - list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock_irq(&parent->port->dev_list_lock); + list_add_tail(&child->disco_list_node, &parent->port->disco_list); res = sas_discover_end_dev(child); if (res) { @@ -755,6 +832,7 @@ static struct domain_device *sas_ex_discover_end_dev( 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); spin_unlock_irq(&parent->port->dev_list_lock); @@ -762,7 +840,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_port_delete(phy->port); out_err: phy->port = NULL; - kfree(child); + sas_put_device(child); return NULL; } @@ -809,7 +887,7 @@ static struct domain_device *sas_ex_discover_expander( phy->attached_phy_id); return NULL; } - child = kzalloc(sizeof(*child), GFP_KERNEL); + child = sas_alloc_device(); if (!child) return NULL; @@ -835,6 +913,7 @@ static struct domain_device *sas_ex_discover_expander( child->rphy = rphy; edev = rphy_to_expander_device(rphy); child->dev_type = phy->attached_dev_type; + kref_get(&parent->kref); child->parent = parent; child->port = port; child->iproto = phy->attached_iproto; @@ -858,7 +937,7 @@ static struct domain_device *sas_ex_discover_expander( spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); - kfree(child); + sas_put_device(child); return NULL; } list_add_tail(&child->siblings, &parent->ex_dev.children); @@ -908,7 +987,8 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) 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 != EDGE_DEV && + ex_phy->attached_dev_type != 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), @@ -934,6 +1014,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) switch (ex_phy->attached_dev_type) { case SAS_END_DEV: + case SATA_PENDING: child = sas_ex_discover_end_dev(dev, phy_id); break; case FANOUT_DEV: @@ -1128,32 +1209,25 @@ static void sas_print_parent_topology_bug(struct domain_device *child, struct ex_phy *parent_phy, struct ex_phy *child_phy) { - static const char ra_char[] = { - [DIRECT_ROUTING] = 'D', - [SUBTRACTIVE_ROUTING] = 'S', - [TABLE_ROUTING] = 'T', - }; static const char *ex_type[] = { [EDGE_DEV] = "edge", [FANOUT_DEV] = "fanout", }; struct domain_device *parent = child->parent; - sas_printk("%s ex %016llx (T2T supp:%d) phy 0x%x <--> %s ex %016llx " - "(T2T supp:%d) phy 0x%x has %c:%c routing link!\n", + sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx " + "phy 0x%x has %c:%c routing link!\n", ex_type[parent->dev_type], SAS_ADDR(parent->sas_addr), - parent->ex_dev.t2t_supp, parent_phy->phy_id, ex_type[child->dev_type], SAS_ADDR(child->sas_addr), - child->ex_dev.t2t_supp, child_phy->phy_id, - ra_char[parent_phy->routing_attr], - ra_char[child_phy->routing_attr]); + sas_route_char(parent, parent_phy), + sas_route_char(child, child_phy)); } static int sas_check_eeds(struct domain_device *child, @@ -1610,8 +1684,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, return res; } -static int sas_get_phy_attached_sas_addr(struct domain_device *dev, - int phy_id, u8 *attached_sas_addr) +static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, + u8 *sas_addr, enum sas_dev_type *type) { int res; struct smp_resp *disc_resp; @@ -1623,10 +1697,11 @@ static int sas_get_phy_attached_sas_addr(struct domain_device *dev, dr = &disc_resp->disc; res = sas_get_phy_discover(dev, phy_id, disc_resp); - if (!res) { - memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8); - if (dr->attached_dev_type == 0) - memset(attached_sas_addr, 0, 8); + if (res == 0) { + memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8); + *type = to_dev_type(dr); + if (*type == 0) + memset(sas_addr, 0, 8); } kfree(disc_resp); return res; @@ -1748,7 +1823,7 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi struct domain_device *child, *n; list_for_each_entry_safe(child, n, &ex->children, siblings) { - child->gone = 1; + set_bit(SAS_DEV_GONE, &child->state); if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) sas_unregister_ex_tree(port, child); @@ -1763,27 +1838,28 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, { struct expander_device *ex_dev = &parent->ex_dev; struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; - struct domain_device *child, *n; + struct domain_device *child, *n, *found = NULL; if (last) { list_for_each_entry_safe(child, n, &ex_dev->children, siblings) { if (SAS_ADDR(child->sas_addr) == SAS_ADDR(phy->attached_sas_addr)) { - child->gone = 1; + set_bit(SAS_DEV_GONE, &child->state); if (child->dev_type == EDGE_DEV || child->dev_type == FANOUT_DEV) sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); + found = child; break; } } - parent->gone = 1; sas_disable_routing(parent, phy->attached_sas_addr); } memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); if (phy->port) { sas_port_delete_phy(phy->port, phy->phy); + sas_device_set_phy(found, phy->port); if (phy->port->num_phys == 0) sas_port_delete(phy->port); phy->port = NULL; @@ -1874,39 +1950,71 @@ out: return res; } +static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_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 + */ + if ((old == SATA_PENDING && new == SAS_END_DEV) || + (old == SAS_END_DEV && new == SATA_PENDING)) + return true; + + return false; +} + 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]; - u8 attached_sas_addr[8]; + enum sas_dev_type type = NO_DEVICE; + u8 sas_addr[8]; int res; - res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr); + res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); switch (res) { case SMP_RESP_NO_PHY: phy->phy_state = PHY_NOT_PRESENT; sas_unregister_devs_sas_addr(dev, phy_id, last); - goto out; break; + return res; case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; sas_unregister_devs_sas_addr(dev, phy_id, last); - goto out; break; + return res; case SMP_RESP_FUNC_ACC: break; } - if (SAS_ADDR(attached_sas_addr) == 0) { + if (SAS_ADDR(sas_addr) == 0) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); - } else if (SAS_ADDR(attached_sas_addr) == - SAS_ADDR(phy->attached_sas_addr)) { - SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n", - SAS_ADDR(dev->sas_addr), phy_id); + return res; + } 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); + char *action = ""; + sas_ex_phy_discover(dev, phy_id); - } else - res = sas_discover_new(dev, phy_id); -out: - return res; + + if (ata_dev && phy->attached_dev_type == SATA_PENDING) + action = ", needs recovery"; + SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", + SAS_ADDR(dev->sas_addr), phy_id, action); + return res; + } + + /* delete the old link */ + if (SAS_ADDR(phy->attached_sas_addr) && + SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr)) { + SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n", + SAS_ADDR(dev->sas_addr), phy_id, + SAS_ADDR(phy->attached_sas_addr)); + sas_unregister_devs_sas_addr(dev, phy_id, last); + } + + return sas_discover_new(dev, phy_id); } /** diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 3814d3eed40..d2479257516 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -187,11 +187,14 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); struct sas_phy_linkrates rates; + struct asd_sas_phy *asd_phy; if (phy_id >= sas_ha->num_phys) { resp_data[2] = SMP_RESP_NO_PHY; return; } + + asd_phy = sas_ha->sas_phy[phy_id]; switch (phy_op) { case PHY_FUNC_NOP: case PHY_FUNC_LINK_RESET: @@ -210,7 +213,13 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, rates.minimum_linkrate = min; rates.maximum_linkrate = max; - if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates)) + /* filter reset requests through libata eh */ + if (phy_op == PHY_FUNC_LINK_RESET && sas_try_ata_reset(asd_phy) == 0) { + resp_data[2] = SMP_RESP_FUNC_ACC; + return; + } + + if (i->dft->lldd_control_phy(asd_phy, phy_op, &rates)) resp_data[2] = SMP_RESP_FUNC_FAILED; else resp_data[2] = SMP_RESP_FUNC_ACC; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index d81c3b1989f..120bff64be3 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -28,6 +28,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/spinlock.h> +#include <scsi/sas_ata.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> #include <scsi/scsi_transport.h> @@ -97,14 +98,14 @@ void sas_hae_reset(struct work_struct *work) container_of(work, struct sas_ha_event, work); struct sas_ha_struct *ha = ev->ha; - sas_begin_event(HAE_RESET, &ha->event_lock, - &ha->pending); + clear_bit(HAE_RESET, &ha->pending); } int sas_register_ha(struct sas_ha_struct *sas_ha) { int error = 0; + mutex_init(&sas_ha->disco_mutex); spin_lock_init(&sas_ha->phy_port_lock); sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); @@ -113,8 +114,10 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) else if (sas_ha->lldd_queue_size == -1) sas_ha->lldd_queue_size = 128; /* Sanity */ - sas_ha->state = SAS_HA_REGISTERED; + set_bit(SAS_HA_REGISTERED, &sas_ha->state); spin_lock_init(&sas_ha->state_lock); + mutex_init(&sas_ha->drain_mutex); + INIT_LIST_HEAD(&sas_ha->defer_q); error = sas_register_phys(sas_ha); if (error) { @@ -144,6 +147,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) } INIT_LIST_HEAD(&sas_ha->eh_done_q); + INIT_LIST_HEAD(&sas_ha->eh_ata_q); return 0; @@ -156,17 +160,23 @@ Undo_phys: int sas_unregister_ha(struct sas_ha_struct *sas_ha) { - unsigned long flags; - - /* Set the state to unregistered to avoid further - * events to be queued */ - spin_lock_irqsave(&sas_ha->state_lock, flags); - sas_ha->state = SAS_HA_UNREGISTERED; - spin_unlock_irqrestore(&sas_ha->state_lock, flags); - scsi_flush_work(sas_ha->core.shost); + /* 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); + clear_bit(SAS_HA_REGISTERED, &sas_ha->state); + spin_unlock_irq(&sas_ha->state_lock); + __sas_drain_work(sas_ha); + mutex_unlock(&sas_ha->drain_mutex); sas_unregister_ports(sas_ha); + /* flush unregistration work */ + mutex_lock(&sas_ha->drain_mutex); + __sas_drain_work(sas_ha); + mutex_unlock(&sas_ha->drain_mutex); + if (sas_ha->lldd_max_execute_num > 1) { sas_shutdown_queue(sas_ha); sas_ha->lldd_max_execute_num = 1; @@ -190,15 +200,75 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } -int sas_phy_enable(struct sas_phy *phy, int enable) +int sas_try_ata_reset(struct asd_sas_phy *asd_phy) +{ + struct domain_device *dev = NULL; + + /* try to route user requested link resets through libata */ + if (asd_phy->port) + dev = asd_phy->port->port_dev; + + /* validate that dev has been probed */ + if (dev) + dev = sas_find_dev_by_rphy(dev->rphy); + + if (dev && dev_is_sata(dev)) { + sas_ata_schedule_reset(dev); + sas_ata_wait_eh(dev); + return 0; + } + + return -ENODEV; +} + +/** + * transport_sas_phy_reset - reset a phy and permit libata to manage the link + * + * phy reset request via sysfs in host workqueue context so we know we + * can block on eh and safely traverse the domain_device topology + */ +static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + enum phy_func reset_type; + + if (hard_reset) + reset_type = PHY_FUNC_HARD_RESET; + else + reset_type = PHY_FUNC_LINK_RESET; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) + return 0; + return i->dft->lldd_control_phy(asd_phy, reset_type, NULL); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number); + + if (ata_dev && !hard_reset) { + sas_ata_schedule_reset(ata_dev); + sas_ata_wait_eh(ata_dev); + return 0; + } else + return sas_smp_phy_control(ddev, phy->number, reset_type, NULL); + } +} + +static int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; - enum phy_func command; + enum phy_func cmd; if (enable) - command = PHY_FUNC_LINK_RESET; + cmd = PHY_FUNC_LINK_RESET; else - command = PHY_FUNC_DISABLE; + cmd = PHY_FUNC_DISABLE; if (scsi_is_sas_phy_local(phy)) { struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); @@ -207,15 +277,18 @@ int sas_phy_enable(struct sas_phy *phy, int enable) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - if (!enable) { - sas_phy_disconnected(asd_phy); - sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL); - } - ret = i->dft->lldd_control_phy(asd_phy, command, NULL); + if (enable) + ret = transport_sas_phy_reset(phy, 0); + else + ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL); } else { struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); struct domain_device *ddev = sas_find_dev_by_rphy(rphy); - ret = sas_smp_phy_control(ddev, phy->number, command, NULL); + + if (enable) + ret = transport_sas_phy_reset(phy, 0); + else + ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL); } return ret; } @@ -225,6 +298,9 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) int ret; enum phy_func reset_type; + if (!phy->enabled) + return -ENODEV; + if (hard_reset) reset_type = PHY_FUNC_HARD_RESET; else @@ -285,9 +361,101 @@ int sas_set_phy_speed(struct sas_phy *phy, return ret; } +static void sas_phy_release(struct sas_phy *phy) +{ + kfree(phy->hostdata); + phy->hostdata = NULL; +} + +static void phy_reset_work(struct work_struct *work) +{ + struct sas_phy_data *d = container_of(work, typeof(*d), reset_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); + + d->enable_result = sas_phy_enable(d->phy, d->enable); +} + +static int sas_phy_setup(struct sas_phy *phy) +{ + struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); + + if (!d) + return -ENOMEM; + + mutex_init(&d->event_lock); + INIT_WORK(&d->reset_work, phy_reset_work); + INIT_WORK(&d->enable_work, phy_enable_work); + d->phy = phy; + phy->hostdata = d; + + return 0; +} + +static int queue_phy_reset(struct sas_phy *phy, int hard_reset) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_phy_data *d = phy->hostdata; + int rc; + + if (!d) + return -ENOMEM; + + /* libsas workqueue coordinates ata-eh reset with discovery */ + mutex_lock(&d->event_lock); + d->reset_result = 0; + d->hard_reset = hard_reset; + + spin_lock_irq(&ha->state_lock); + sas_queue_work(ha, &d->reset_work); + spin_unlock_irq(&ha->state_lock); + + rc = sas_drain_work(ha); + if (rc == 0) + rc = d->reset_result; + mutex_unlock(&d->event_lock); + + return rc; +} + +static int queue_phy_enable(struct sas_phy *phy, int enable) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct sas_phy_data *d = phy->hostdata; + int rc; + + if (!d) + return -ENOMEM; + + /* libsas workqueue coordinates ata-eh reset with discovery */ + mutex_lock(&d->event_lock); + d->enable_result = 0; + d->enable = enable; + + spin_lock_irq(&ha->state_lock); + sas_queue_work(ha, &d->enable_work); + spin_unlock_irq(&ha->state_lock); + + rc = sas_drain_work(ha); + if (rc == 0) + rc = d->enable_result; + mutex_unlock(&d->event_lock); + + return rc; +} + static struct sas_function_template sft = { - .phy_enable = sas_phy_enable, - .phy_reset = sas_phy_reset, + .phy_enable = queue_phy_enable, + .phy_reset = queue_phy_reset, + .phy_setup = sas_phy_setup, + .phy_release = sas_phy_release, .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, .smp_handler = sas_smp_handler, diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 14e21b5fb8b..f05c6387994 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -30,6 +30,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_transport_sas.h> #include <scsi/libsas.h> +#include <scsi/sas_ata.h> #define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) @@ -38,6 +39,18 @@ #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) +struct sas_phy_data { + /* let reset be performed in sas_queue_work() context */ + struct sas_phy *phy; + struct mutex event_lock; + int hard_reset; + int reset_result; + struct work_struct reset_work; + int enable; + int enable_result; + struct work_struct enable_work; +}; + void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_show_class(enum sas_class class, char *buf); @@ -56,6 +69,9 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *); int sas_init_queue(struct sas_ha_struct *sas_ha); int sas_init_events(struct sas_ha_struct *sas_ha); void sas_shutdown_queue(struct sas_ha_struct *sas_ha); +void sas_disable_revalidation(struct sas_ha_struct *ha); +void sas_enable_revalidation(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); @@ -64,6 +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); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); @@ -72,10 +89,17 @@ 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_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); +int sas_ex_phy_discover(struct domain_device *dev, int single); +int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, + struct smp_resp *rps_resp); +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); + #ifdef CONFIG_SCSI_SAS_HOST_SMP extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, struct request *rsp); @@ -90,36 +114,13 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif -static inline void sas_queue_event(int event, spinlock_t *lock, - unsigned long *pending, - struct work_struct *work, - struct sas_ha_struct *sas_ha) +static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) { - unsigned long flags; - - spin_lock_irqsave(lock, flags); - if (test_bit(event, pending)) { - spin_unlock_irqrestore(lock, flags); - return; - } - __set_bit(event, pending); - spin_unlock_irqrestore(lock, flags); - - spin_lock_irqsave(&sas_ha->state_lock, flags); - if (sas_ha->state != SAS_HA_UNREGISTERED) { - scsi_queue_work(sas_ha->core.shost, work); - } - spin_unlock_irqrestore(&sas_ha->state_lock, flags); -} - -static inline void sas_begin_event(int event, spinlock_t *lock, - unsigned long *pending) -{ - unsigned long flags; - - spin_lock_irqsave(lock, flags); - __clear_bit(event, pending); - spin_unlock_irqrestore(lock, flags); + SAS_DPRINTK("%s: for %s device %16llx returned %d\n", + func, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + sas_unregister_dev(dev->port, dev); } static inline void sas_fill_in_rphy(struct domain_device *dev, @@ -132,6 +133,7 @@ static inline void sas_fill_in_rphy(struct domain_device *dev, case SATA_DEV: /* FIXME: need sata device type */ case SAS_END_DEV: + case SATA_PENDING: rphy->identify.device_type = SAS_END_DEVICE; break; case EDGE_DEV: @@ -146,6 +148,22 @@ static inline void sas_fill_in_rphy(struct domain_device *dev, } } +static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_device *dev) +{ + struct sas_phy *phy = p->phy; + + if (dev) { + if (dev_is_sata(dev)) + phy->identify.device_type = SAS_END_DEVICE; + else + phy->identify.device_type = dev->dev_type; + phy->identify.target_port_protocols = dev->tproto; + } else { + phy->identify.device_type = SAS_PHY_UNUSED; + phy->identify.target_port_protocols = 0; + } +} + static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) { struct expander_device *ex = &dev->ex_dev; @@ -161,4 +179,23 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) 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); + + if (dev) { + INIT_LIST_HEAD(&dev->siblings); + INIT_LIST_HEAD(&dev->dev_list_node); + INIT_LIST_HEAD(&dev->disco_list_node); + kref_init(&dev->kref); + spin_lock_init(&dev->done_lock); + } + return dev; +} + +static inline void sas_put_device(struct domain_device *dev) +{ + kref_put(&dev->kref, sas_free_device); +} + #endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index e0f5018e907..dcfd4a9105c 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -36,8 +36,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); phy->error = 0; sas_deform_port(phy, 1); } @@ -48,8 +47,7 @@ static void sas_phye_oob_done(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); phy->error = 0; } @@ -63,8 +61,7 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); sas_deform_port(phy, 1); @@ -95,8 +92,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock, - &phy->phy_events_pending); + clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending); phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 42fd1f25b66..eb19c016d50 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -104,13 +104,11 @@ static void sas_form_port(struct asd_sas_phy *phy) /* add the phy to the port */ list_add_tail(&phy->port_phy_el, &port->phy_list); + sas_phy_set_target(phy, port->port_dev); phy->port = port; port->num_phys++; port->phy_mask |= (1U << phy->id); - if (!port->phy) - port->phy = phy->phy; - if (*(u64 *)port->attached_sas_addr == 0) { port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, @@ -125,7 +123,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, port->id); + port->port = sas_port_alloc(phy->phy->dev.parent, phy->id); BUG_ON(!port->port); sas_port_add(port->port); } @@ -170,13 +168,13 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) dev->pathways--; if (port->num_phys == 1) { - if (dev && gone) - dev->gone = 1; - sas_unregister_domain_devices(port); + sas_unregister_domain_devices(port, gone); sas_port_delete(port->port); port->port = NULL; - } else + } else { sas_port_delete_phy(port->port, phy->phy); + sas_device_set_phy(dev, port->port); + } if (si->dft->lldd_port_deformed) si->dft->lldd_port_deformed(phy); @@ -185,6 +183,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) spin_lock(&port->phy_list_lock); list_del_init(&phy->port_phy_el); + sas_phy_set_target(phy, NULL); phy->port = NULL; port->num_phys--; port->phy_mask &= ~(1U << phy->id); @@ -213,8 +212,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); sas_form_port(phy); } @@ -227,8 +225,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) unsigned long flags; u32 prim; - sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending); spin_lock_irqsave(&phy->sas_prim_lock, flags); prim = phy->sas_prim; @@ -244,8 +241,7 @@ void sas_porte_link_reset_err(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); sas_deform_port(phy, 1); } @@ -256,8 +252,7 @@ void sas_porte_timer_event(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); sas_deform_port(phy, 1); } @@ -268,8 +263,7 @@ void sas_porte_hard_reset(struct work_struct *work) container_of(work, struct asd_sas_event, work); struct asd_sas_phy *phy = ev->phy; - sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock, - &phy->port_events_pending); + clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); sas_deform_port(phy, 1); } @@ -282,6 +276,8 @@ static void sas_init_port(struct asd_sas_port *port, memset(port, 0, sizeof(*port)); port->id = i; INIT_LIST_HEAD(&port->dev_list); + INIT_LIST_HEAD(&port->disco_list); + INIT_LIST_HEAD(&port->destroy_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); port->ha = sas_ha; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index b6e233d9a0a..f0b9b7bf188 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -49,27 +49,12 @@ #include <linux/scatterlist.h> #include <linux/libata.h> -/* ---------- SCSI Host glue ---------- */ - -static void sas_scsi_task_done(struct sas_task *task) +/* record final status and free the task */ +static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) { struct task_status_struct *ts = &task->task_status; - struct scsi_cmnd *sc = task->uldd_task; int hs = 0, stat = 0; - if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - /* Aborted tasks will be completed by the error handler */ - SAS_DPRINTK("task done but aborted\n"); - return; - } - - if (unlikely(!sc)) { - SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); - list_del_init(&task->list); - sas_free_task(task); - return; - } - if (ts->resp == SAS_TASK_UNDELIVERED) { /* transport error */ hs = DID_NO_CONNECT; @@ -124,10 +109,41 @@ static void sas_scsi_task_done(struct sas_task *task) break; } } - ASSIGN_SAS_TASK(sc, NULL); + sc->result = (hs << 16) | stat; + ASSIGN_SAS_TASK(sc, NULL); list_del_init(&task->list); sas_free_task(task); +} + +static void sas_scsi_task_done(struct sas_task *task) +{ + struct scsi_cmnd *sc = task->uldd_task; + struct domain_device *dev = task->dev; + struct sas_ha_struct *ha = dev->port->ha; + unsigned long flags; + + spin_lock_irqsave(&dev->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &ha->state)) + task = NULL; + else + ASSIGN_SAS_TASK(sc, NULL); + spin_unlock_irqrestore(&dev->done_lock, flags); + + if (unlikely(!task)) { + /* task will be completed by the error handler */ + SAS_DPRINTK("task done but aborted\n"); + return; + } + + if (unlikely(!sc)) { + SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); + list_del_init(&task->list); + sas_free_task(task); + return; + } + + sas_end_task(sc, task); sc->scsi_done(sc); } @@ -192,17 +208,15 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) int res = 0; /* If the device fell off, no sense in issuing commands */ - if (dev->gone) { + if (test_bit(SAS_DEV_GONE, &dev->state)) { cmd->result = DID_BAD_TARGET << 16; goto out_done; } if (dev_is_sata(dev)) { - unsigned long flags; - - spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + spin_lock_irq(dev->sata_dev.ap->lock); res = ata_sas_queuecmd(cmd, dev->sata_dev.ap); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + spin_unlock_irq(dev->sata_dev.ap->lock); return res; } @@ -235,24 +249,38 @@ out_done: static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) { - struct sas_task *task = TO_SAS_TASK(cmd); struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host); + struct sas_task *task = TO_SAS_TASK(cmd); + + /* At this point, we only get called following an actual abort + * of the task, so we should be guaranteed not to be racing with + * any completions from the LLD. Task is freed after this. + */ + sas_end_task(cmd, task); - /* remove the aborted task flag to allow the task to be - * completed now. At this point, we only get called following - * an actual abort of the task, so we should be guaranteed not - * to be racing with any completions from the LLD (hence we - * don't need the task state lock to clear the flag) */ - task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; - /* Now call task_done. However, task will be free'd after - * this */ - task->task_done(task); /* now finish the command and move it on to the error * handler done list, this also takes it off the - * error handler pending list */ + * error handler pending list. + */ scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q); } +static void sas_eh_defer_cmd(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_ha_struct *ha = dev->port->ha; + struct sas_task *task = TO_SAS_TASK(cmd); + + if (!dev_is_sata(dev)) { + sas_eh_finish_cmd(cmd); + return; + } + + /* report the timeout to libata */ + sas_end_task(cmd, task); + list_move_tail(&cmd->eh_entry, &ha->eh_ata_q); +} + static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) { struct scsi_cmnd *cmd, *n; @@ -260,7 +288,7 @@ static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd list_for_each_entry_safe(cmd, n, error_q, eh_entry) { if (cmd->device->sdev_target == my_cmd->device->sdev_target && cmd->device->lun == my_cmd->device->lun) - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); } } @@ -295,6 +323,7 @@ enum task_disposition { TASK_IS_DONE, TASK_IS_ABORTED, TASK_IS_AT_LU, + TASK_IS_NOT_AT_HA, TASK_IS_NOT_AT_LU, TASK_ABORT_FAILED, }; @@ -311,19 +340,18 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) struct scsi_core *core = &ha->core; struct sas_task *t, *n; + mutex_lock(&core->task_queue_flush); spin_lock_irqsave(&core->task_queue_lock, flags); - list_for_each_entry_safe(t, n, &core->task_queue, list) { + list_for_each_entry_safe(t, n, &core->task_queue, list) if (task == t) { list_del_init(&t->list); - spin_unlock_irqrestore(&core->task_queue_lock, - flags); - SAS_DPRINTK("%s: task 0x%p aborted from " - "task_queue\n", - __func__, task); - return TASK_IS_ABORTED; + break; } - } spin_unlock_irqrestore(&core->task_queue_lock, flags); + mutex_unlock(&core->task_queue_flush); + + if (task == t) + return TASK_IS_NOT_AT_HA; } for (i = 0; i < 5; i++) { @@ -411,30 +439,26 @@ static int sas_recover_I_T(struct domain_device *dev) return res; } -/* Find the sas_phy that's attached to this device */ -struct sas_phy *sas_find_local_phy(struct domain_device *dev) +/* take a reference on the last known good phy for this device */ +struct sas_phy *sas_get_local_phy(struct domain_device *dev) { - struct domain_device *pdev = dev->parent; - struct ex_phy *exphy = NULL; - int i; + struct sas_ha_struct *ha = dev->port->ha; + struct sas_phy *phy; + unsigned long flags; - /* Directly attached device */ - if (!pdev) - return dev->port->phy; + /* a published domain device always has a valid phy, it may be + * stale, but it is never NULL + */ + BUG_ON(!dev->phy); - /* Otherwise look in the expander */ - for (i = 0; i < pdev->ex_dev.num_phys; i++) - if (!memcmp(dev->sas_addr, - pdev->ex_dev.ex_phy[i].attached_sas_addr, - SAS_ADDR_SIZE)) { - exphy = &pdev->ex_dev.ex_phy[i]; - break; - } + spin_lock_irqsave(&ha->phy_port_lock, flags); + phy = dev->phy; + get_device(&phy->dev); + spin_unlock_irqrestore(&ha->phy_port_lock, flags); - BUG_ON(!exphy); - return exphy->phy; + return phy; } -EXPORT_SYMBOL_GPL(sas_find_local_phy); +EXPORT_SYMBOL_GPL(sas_get_local_phy); /* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) @@ -461,7 +485,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); int res; res = sas_phy_reset(phy, 1); @@ -469,6 +493,8 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) SAS_DPRINTK("Bus reset of %s failed 0x%x\n", kobject_name(&phy->dev.kobj), res); + sas_put_local_phy(phy); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; @@ -495,9 +521,7 @@ try_bus_reset: return FAILED; } -static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, - struct list_head *work_q, - struct list_head *done_q) +static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *work_q) { struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; @@ -505,13 +529,28 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct sas_internal *i = to_sas_internal(shost->transportt); unsigned long flags; struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + LIST_HEAD(done); -Again: + /* clean out any commands that won the completion vs eh race */ list_for_each_entry_safe(cmd, n, work_q, eh_entry) { - struct sas_task *task = TO_SAS_TASK(cmd); + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_task *task; + + spin_lock_irqsave(&dev->done_lock, flags); + /* by this point the lldd has either observed + * SAS_HA_FROZEN and is leaving the task alone, or has + * won the race with eh and decided to complete it + */ + task = TO_SAS_TASK(cmd); + spin_unlock_irqrestore(&dev->done_lock, flags); if (!task) - continue; + list_move_tail(&cmd->eh_entry, &done); + } + + Again: + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { + struct sas_task *task = TO_SAS_TASK(cmd); list_del_init(&cmd->eh_entry); @@ -531,15 +570,23 @@ Again: cmd->eh_eflags = 0; switch (res) { + case TASK_IS_NOT_AT_HA: + SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n", + __func__, task, + cmd->retries ? "retry" : "aborted"); + if (cmd->retries) + cmd->retries--; + sas_eh_finish_cmd(cmd); + continue; case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __func__, task); - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __func__, task); - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); continue; case TASK_IS_AT_LU: SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); @@ -550,7 +597,7 @@ Again: "recovered\n", SAS_ADDR(task->dev), cmd->device->lun); - sas_eh_finish_cmd(cmd); + sas_eh_defer_cmd(cmd); sas_scsi_clear_queue_lu(work_q, cmd); goto Again; } @@ -560,7 +607,8 @@ Again: SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n", task); tmf_resp = sas_recover_I_T(task->dev); - if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { + if (tmf_resp == TMF_RESP_FUNC_COMPLETE || + tmf_resp == -ENODEV) { struct domain_device *dev = task->dev; SAS_DPRINTK("I_T %016llx recovered\n", SAS_ADDR(task->dev->sas_addr)); @@ -607,13 +655,16 @@ Again: goto clear_q; } } - return list_empty(work_q); -clear_q: + out: + list_splice_tail(&done, work_q); + list_splice_tail_init(&ha->eh_ata_q, work_q); + return; + + clear_q: SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__); list_for_each_entry_safe(cmd, n, work_q, eh_entry) sas_eh_finish_cmd(cmd); - - return list_empty(work_q); + goto out; } void sas_scsi_recover_host(struct Scsi_Host *shost) @@ -627,12 +678,17 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) shost->host_eh_scheduled = 0; spin_unlock_irqrestore(shost->host_lock, flags); - SAS_DPRINTK("Enter %s\n", __func__); + SAS_DPRINTK("Enter %s busy: %d failed: %d\n", + __func__, shost->host_busy, shost->host_failed); /* * Deal with commands that still have SAS tasks (i.e. they didn't - * complete via the normal sas_task completion mechanism) + * complete via the normal sas_task completion mechanism), + * SAS_HA_FROZEN gives eh dominion over all sas_task completion. */ - if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q)) + set_bit(SAS_HA_FROZEN, &ha->state); + sas_eh_handle_sas_errors(shost, &eh_work_q); + clear_bit(SAS_HA_FROZEN, &ha->state); + if (list_empty(&eh_work_q)) goto out; /* @@ -641,59 +697,26 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q)) - if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) - scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q); + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + if (ha->lldd_max_execute_num > 1) + wake_up_process(ha->core.queue_thread); + /* 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\n", __func__); - return; + SAS_DPRINTK("--- Exit %s: busy: %d failed: %d\n", + __func__, shost->host_busy, shost->host_failed); } enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) { - struct sas_task *task = TO_SAS_TASK(cmd); - unsigned long flags; - enum blk_eh_timer_return rtn; - - if (sas_ata_timed_out(cmd, task, &rtn)) - return rtn; - - if (!task) { - cmd->request->timeout /= 2; - SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n", - cmd, task, (cmd->request->timeout ? - "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED")); - if (!cmd->request->timeout) - return BLK_EH_NOT_HANDLED; - return BLK_EH_RESET_TIMER; - } - - spin_lock_irqsave(&task->task_state_lock, flags); - BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); - if (task->task_state_flags & SAS_TASK_STATE_DONE) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: " - "BLK_EH_HANDLED\n", cmd, task); - return BLK_EH_HANDLED; - } - if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: " - "BLK_EH_RESET_TIMER\n", - cmd, task); - return BLK_EH_RESET_TIMER; - } - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n", - cmd, task); + scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd); return BLK_EH_NOT_HANDLED; } @@ -737,27 +760,15 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) return found_dev; } -static inline struct domain_device *sas_find_target(struct scsi_target *starget) -{ - struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); - - return sas_find_dev_by_rphy(rphy); -} - int sas_target_alloc(struct scsi_target *starget) { - struct domain_device *found_dev = sas_find_target(starget); - int res; + struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); + struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); if (!found_dev) return -ENODEV; - if (dev_is_sata(found_dev)) { - res = sas_ata_init_host_and_port(found_dev, starget); - if (res) - return res; - } - + kref_get(&found_dev->kref); starget->hostdata = found_dev; return 0; } @@ -797,14 +808,6 @@ int sas_slave_configure(struct scsi_device *scsi_dev) return 0; } -void sas_slave_destroy(struct scsi_device *scsi_dev) -{ - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) - sas_to_ata_dev(dev)->class = ATA_DEV_NONE; -} - int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason) { struct domain_device *dev = sdev_to_domain_dev(sdev); @@ -871,9 +874,11 @@ static void sas_queue(struct sas_ha_struct *sas_ha) int res; struct sas_internal *i = to_sas_internal(core->shost->transportt); + mutex_lock(&core->task_queue_flush); spin_lock_irqsave(&core->task_queue_lock, flags); while (!kthread_should_stop() && - !list_empty(&core->task_queue)) { + !list_empty(&core->task_queue) && + !test_bit(SAS_HA_FROZEN, &sas_ha->state)) { can_queue = sas_ha->lldd_queue_size - core->task_queue_size; if (can_queue >= 0) { @@ -909,6 +914,7 @@ static void sas_queue(struct sas_ha_struct *sas_ha) } } spin_unlock_irqrestore(&core->task_queue_lock, flags); + mutex_unlock(&core->task_queue_flush); } /** @@ -935,6 +941,7 @@ int sas_init_queue(struct sas_ha_struct *sas_ha) struct scsi_core *core = &sas_ha->core; spin_lock_init(&core->task_queue_lock); + mutex_init(&core->task_queue_flush); core->task_queue_size = 0; INIT_LIST_HEAD(&core->task_queue); @@ -972,49 +979,6 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha) } /* - * Call the LLDD task abort routine directly. This function is intended for - * use by upper layers that need to tell the LLDD to abort a task. - */ -int __sas_task_abort(struct sas_task *task) -{ - struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - unsigned long flags; - int res; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (task->task_state_flags & SAS_TASK_STATE_ABORTED || - task->task_state_flags & SAS_TASK_STATE_DONE) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: Task %p already finished.\n", __func__, - task); - return 0; - } - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - if (!si->dft->lldd_abort_task) - return -ENODEV; - - res = si->dft->lldd_abort_task(task); - - spin_lock_irqsave(&task->task_state_lock, flags); - if ((task->task_state_flags & SAS_TASK_STATE_DONE) || - (res == TMF_RESP_FUNC_COMPLETE)) - { - spin_unlock_irqrestore(&task->task_state_lock, flags); - task->task_done(task); - return 0; - } - - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - return -EAGAIN; -} - -/* * Tell an upper layer that it needs to initiate an abort for a given task. * This should only ever be called by an LLDD. */ @@ -1043,27 +1007,15 @@ void sas_task_abort(struct sas_task *task) } } -int sas_slave_alloc(struct scsi_device *scsi_dev) -{ - struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - - if (dev_is_sata(dev)) - return ata_sas_port_init(dev->sata_dev.ap); - - return 0; -} - void sas_target_destroy(struct scsi_target *starget) { - struct domain_device *found_dev = sas_find_target(starget); + struct domain_device *found_dev = starget->hostdata; if (!found_dev) return; - if (dev_is_sata(found_dev)) - ata_sas_port_destroy(found_dev->sata_dev.ap); - - return; + starget->hostdata = NULL; + sas_put_device(found_dev); } static void sas_parse_addr(u8 *sas_addr, const char *p) @@ -1108,16 +1060,12 @@ EXPORT_SYMBOL_GPL(sas_request_addr); EXPORT_SYMBOL_GPL(sas_queuecommand); EXPORT_SYMBOL_GPL(sas_target_alloc); EXPORT_SYMBOL_GPL(sas_slave_configure); -EXPORT_SYMBOL_GPL(sas_slave_destroy); EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); -EXPORT_SYMBOL_GPL(__sas_task_abort); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); -EXPORT_SYMBOL_GPL(sas_phy_enable); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); -EXPORT_SYMBOL_GPL(sas_slave_alloc); EXPORT_SYMBOL_GPL(sas_target_destroy); EXPORT_SYMBOL_GPL(sas_ioctl); |