From 95ac7fd189b7e81a200b4d00b2bb6669b31acf3a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:45 -0800 Subject: [SCSI] libsas: remove unused ata_task_resp fields Commit 1e34c838 "[SCSI] libsas: remove spurious sata control register read/write" removed the routines to fake the presence of the sata control registers, now remove the unused data structure fields to kill any remaining confusion. Acked-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index db9238f2ecb..83118d0b6d0 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -121,10 +121,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) { -- cgit v1.2.3-70-g09d2 From 312d3e56119a4bc5c36a96818f87f650c069ddc2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:50 -0800 Subject: [SCSI] libsas: remove ata_port.lock management duties from lldds Each libsas driver (mvsas, pm8001, and isci) has invented a different method for managing the ap->lock. The lock is held by the ata ->queuecommand() path. mvsas drops it prior to acquiring any internal locks which allows it to hold its internal lock across calls to task->task_done(). This capability is important as it is the only way the driver can flush task->task_done() instances to guarantee that it no longer has any in-flight references to a domain_device at ->lldd_dev_gone() time. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/request.c | 3 +-- drivers/scsi/isci/task.c | 6 ++---- drivers/scsi/isci/task.h | 36 ------------------------------------ drivers/scsi/libsas/sas_ata.c | 36 +++++++++++++++++++++++------------- drivers/scsi/libsas/sas_scsi_host.c | 6 ++---- drivers/scsi/mvsas/mv_sas.c | 6 ------ drivers/scsi/pm8001/pm8001_sas.c | 6 +----- 7 files changed, 29 insertions(+), 70 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 751368b46b4..788daeedc89 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3796,8 +3796,7 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide /* Cause this task to be scheduled in the SCSI error * handler thread. */ - isci_execpath_callback(ihost, task, - sas_task_abort); + sas_task_abort(task); /* Change the status, since we are holding * the I/O until it is managed by the SCSI diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index f5a3f7d2bda..4bd88ef83cd 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -96,8 +96,7 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, __func__, task, response, status); task->lldd_task = NULL; - - isci_execpath_callback(ihost, task, task->task_done); + task->task_done(task); break; case isci_perform_aborted_io_completion: @@ -117,8 +116,7 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, "%s: Error - task = %p, response=%d, " "status=%d\n", __func__, task, response, status); - - isci_execpath_callback(ihost, task, sas_task_abort); + sas_task_abort(task); break; default: diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 1b27b3797c6..bb472c33952 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -321,40 +321,4 @@ isci_task_set_completion_status( return task_notification_selection; } -/** -* isci_execpath_callback() - This function is called from the task -* execute path when the task needs to callback libsas about the submit-time -* task failure. The callback occurs either through the task's done function -* or through sas_task_abort. In the case of regular non-discovery SATA/STP I/O -* requests, libsas takes the host lock before calling execute task. Therefore -* in this situation the host lock must be managed before calling the func. -* -* @ihost: This parameter is the controller to which the I/O request was sent. -* @task: This parameter is the I/O request. -* @func: This parameter is the function to call in the correct context. -* @status: This parameter is the status code for the completed task. -* -*/ -static inline void isci_execpath_callback(struct isci_host *ihost, - struct sas_task *task, - void (*func)(struct sas_task *)) -{ - struct domain_device *dev = task->dev; - - if (dev_is_sata(dev) && task->uldd_task) { - unsigned long flags; - - /* Since we are still in the submit path, and since - * libsas takes the host lock on behalf of SATA - * devices before I/O starts (in the non-discovery case), - * we need to unlock before we can call the callback function. - */ - raw_local_irq_save(flags); - spin_unlock(dev->sata_dev.ap->lock); - func(task); - spin_lock(dev->sata_dev.ap->lock); - raw_local_irq_restore(flags); - } else - func(task); -} #endif /* !defined(_SCI_TASK_H_) */ diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 83118d0b6d0..81ce39d166d 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -166,23 +166,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; + 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; @@ -227,21 +234,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) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 2a163c73fd8..fd60465d4b2 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -198,11 +198,9 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) } 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; } diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index b118e632bc7..cd882230591 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -893,9 +893,6 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, mvi = ((struct mvs_device *)task->dev->lldd_dev)->mvi_info; - if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL)) - spin_unlock_irq(dev->sata_dev.ap->lock); - spin_lock_irqsave(&mvi->lock, flags); rc = mvs_task_prep(task, mvi, is_tmf, tmf, &pass); if (rc) @@ -906,9 +903,6 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, (MVS_CHIP_SLOT_SZ - 1)); spin_unlock_irqrestore(&mvi->lock, flags); - if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL)) - spin_lock_irq(dev->sata_dev.ap->lock); - return rc; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 50837933a1e..310860e37d9 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -364,7 +364,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num, struct pm8001_ccb_info *ccb; u32 tag = 0xdeadbeef, rc, n_elem = 0; u32 n = num; - unsigned long flags = 0, flags_libsas = 0; + unsigned long flags = 0; if (!dev->port) { struct task_status_struct *tsm = &t->task_status; @@ -388,11 +388,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num, ts->stat = SAS_PHY_DOWN; spin_unlock_irqrestore(&pm8001_ha->lock, flags); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, - flags_libsas); t->task_done(t); - spin_lock_irqsave(dev->sata_dev.ap->lock, - flags_libsas); spin_lock_irqsave(&pm8001_ha->lock, flags); if (n > 1) t = list_entry(t->list.next, -- cgit v1.2.3-70-g09d2 From e139942d77a6e3ac83bc322e826668054a8601d6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 7 Jan 2012 08:52:39 +0000 Subject: [SCSI] libsas: convert dev->gone to flags In preparation for adding tracking of another device state "destroy". Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 2 +- drivers/scsi/libsas/sas_expander.c | 6 +++--- drivers/scsi/libsas/sas_port.c | 2 +- drivers/scsi/libsas/sas_scsi_host.c | 2 +- include/scsi/libsas.h | 7 +++++-- 5 files changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 81ce39d166d..2fc5a3961ca 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -184,7 +184,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) spin_unlock(ap->lock); /* If the device fell off, no sense in issuing commands */ - if (dev->gone) + if (test_bit(SAS_DEV_GONE, &dev->state)) goto out; task = sas_alloc_task(GFP_ATOMIC); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 15d2239a378..f33d0c9911c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1750,7 +1750,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); @@ -1771,7 +1771,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, &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); @@ -1780,7 +1780,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, break; } } - parent->gone = 1; + set_bit(SAS_DEV_GONE, &parent->state); sas_disable_routing(parent, phy->attached_sas_addr); } memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index a47c7a75327..d88e55f9732 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -171,7 +171,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) if (port->num_phys == 1) { if (dev && gone) - dev->gone = 1; + set_bit(SAS_DEV_GONE, &dev->state); sas_unregister_domain_devices(port); sas_port_delete(port->port); port->port = NULL; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index fd60465d4b2..15533a17eb9 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -192,7 +192,7 @@ 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; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 42900fa95a0..d792b13cfcf 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -173,7 +173,10 @@ struct sata_device { struct ata_taskfile tf; }; -/* ---------- Domain device ---------- */ +enum { + SAS_DEV_GONE, +}; + struct domain_device { enum sas_dev_type dev_type; @@ -205,7 +208,7 @@ struct domain_device { }; void *lldd_dev; - int gone; + unsigned long state; struct kref kref; }; -- cgit v1.2.3-70-g09d2 From 87c8331fcf72e501c3a3c0cdc5c9391ec72f7cf2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:51 -0800 Subject: [SCSI] libsas: prevent domain rediscovery competing with ata error handling libata error handling provides for a timeout for link recovery. libsas must not rescan for previously known devices in this interval otherwise it may remove a device that is simply waiting for its link to recover. Let libata-eh make the determination of when the link is stable and prevent libsas (host workqueue) from taking action while this determination is pending. Using a mutex (ha->disco_mutex) to flush and disable revalidation while eh is running requires any discovery action that may block on eh be moved to its own context outside the lock. Probing ATA devices explicitly waits on ata-eh and the cache-flush-io issued during device removal may also pend awaiting eh completion. Essentially any rphy add/remove activity needs to run outside the lock. This adds two new cleanup states for sas_unregister_domain_devices() 'allocated-but-not-probed', and 'flagged-for-destruction'. In the 'allocated-but-not-probed' state dev->rphy points to a rphy that is known to have not been through a sas_rphy_add() event. At domain teardown check if this device is still pending probe and cleanup accordingly. Similarly if a device has already been queued for removal then sas_unregister_domain_devices has nothing to do. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 55 ++++++++++++++++++++++++++++++--- drivers/scsi/libsas/sas_discover.c | 63 ++++++++++++++++++++++++++++++++++---- drivers/scsi/libsas/sas_event.c | 26 ++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 5 ++- drivers/scsi/libsas/sas_init.c | 2 ++ drivers/scsi/libsas/sas_internal.h | 3 ++ drivers/scsi/libsas/sas_port.c | 2 ++ drivers/scsi/scsi_transport_sas.c | 18 +++++++++-- include/scsi/libsas.h | 12 ++++++-- include/scsi/sas_ata.h | 5 +++ include/scsi/scsi_transport_sas.h | 1 + 11 files changed, 174 insertions(+), 18 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 2fc5a3961ca..4b6365c6410 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -758,6 +758,35 @@ static int sas_discover_sata_pm(struct domain_device *dev) return -ENODEV; } +void sas_probe_sata(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); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + + err = sas_rphy_add(dev->rphy); + + if (err) { + 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(port, dev); + } else + list_del_init(&dev->disco_list_node); + } +} + /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest @@ -794,10 +823,15 @@ int sas_discover_sata(struct domain_device *dev) break; } sas_notify_lldd_dev_gone(dev); - if (!res) { - sas_notify_lldd_dev_found(dev); - res = sas_rphy_add(dev->rphy); - } + + if (res) + return res; + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + sas_discover_event(dev->port, DISCE_PROBE); return res; } @@ -805,6 +839,17 @@ int sas_discover_sata(struct domain_device *dev) void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct scsi_device *sdev; + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + + /* 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); shost_for_each_device(sdev, shost) { struct domain_device *ddev = sdev_to_domain_dev(sdev); @@ -816,6 +861,8 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); ata_scsi_port_error_handler(shost, ap); } + + sas_enable_revalidation(sas_ha); } int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 32e01176604..7e8fdcb202b 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -148,9 +148,14 @@ static int sas_get_port_device(struct asd_sas_port *port) port->disc.max_level = 0; 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)) + 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; } @@ -255,14 +260,43 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d 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); + + sas_put_device(dev); + } +} + +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) @@ -271,6 +305,8 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) 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; @@ -335,6 +371,7 @@ 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); @@ -353,16 +390,28 @@ 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; + } 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 ---------- */ @@ -414,6 +463,8 @@ 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_sata, + [DISCE_DESTRUCT] = sas_destruct_devices, }; disc->pending = 0; diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index e5035aa4c2a..933d757499b 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -81,6 +81,32 @@ int sas_drain_work(struct sas_ha_struct *ha) } 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); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f33d0c9911c..e45b259dac4 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -704,9 +704,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) { @@ -756,6 +754,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); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 572b943d760..52cd11d7666 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -104,6 +104,7 @@ 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); @@ -168,6 +169,7 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) sas_drain_work(sas_ha); sas_unregister_ports(sas_ha); + sas_drain_work(sas_ha); if (sas_ha->lldd_max_execute_num > 1) { sas_shutdown_queue(sas_ha); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 948ea64cc2e..ebe9b81ddef 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -56,6 +56,8 @@ 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_deform_port(struct asd_sas_phy *phy, int gone); @@ -138,6 +140,7 @@ static inline struct domain_device *sas_alloc_device(void) 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); } return dev; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index d88e55f9732..2980bde4e34 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -277,6 +277,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/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 9d9330ae421..9421bae8af1 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1602,6 +1602,20 @@ sas_rphy_delete(struct sas_rphy *rphy) } EXPORT_SYMBOL(sas_rphy_delete); +/** + * sas_rphy_unlink - unlink SAS remote PHY + * @rphy: SAS remote phy to unlink from its parent port + * + * Removes port reference to an rphy + */ +void sas_rphy_unlink(struct sas_rphy *rphy) +{ + struct sas_port *parent = dev_to_sas_port(rphy->dev.parent); + + parent->rphy = NULL; +} +EXPORT_SYMBOL(sas_rphy_unlink); + /** * sas_rphy_remove - remove SAS remote PHY * @rphy: SAS remote phy to remove @@ -1612,7 +1626,6 @@ void sas_rphy_remove(struct sas_rphy *rphy) { struct device *dev = &rphy->dev; - struct sas_port *parent = dev_to_sas_port(dev->parent); switch (rphy->identify.device_type) { case SAS_END_DEVICE: @@ -1626,10 +1639,9 @@ sas_rphy_remove(struct sas_rphy *rphy) break; } + sas_rphy_unlink(rphy); transport_remove_device(dev); device_del(dev); - - parent->rphy = NULL; } EXPORT_SYMBOL(sas_rphy_remove); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index d792b13cfcf..bd6e89ece2a 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -86,7 +86,9 @@ enum discover_event { DISCE_DISCOVER_DOMAIN = 0U, DISCE_REVALIDATE_DOMAIN = 1, DISCE_PORT_GONE = 2, - DISC_NUM_EVENTS = 3, + DISCE_PROBE = 3, + DISCE_DESTRUCT = 4, + DISC_NUM_EVENTS = 5, }; /* ---------- Expander Devices ---------- */ @@ -175,6 +177,7 @@ struct sata_device { enum { SAS_DEV_GONE, + SAS_DEV_DESTROY, }; struct domain_device { @@ -191,6 +194,7 @@ struct domain_device { struct asd_sas_port *port; /* shortcut to root of the tree */ struct list_head dev_list_node; + struct list_head disco_list_node; /* awaiting probe or destruct */ enum sas_protocol iproto; enum sas_protocol tproto; @@ -226,7 +230,6 @@ struct sas_discovery { int max_level; }; - /* The port struct is Class:RW, driver:RO */ struct asd_sas_port { /* private: */ @@ -236,6 +239,8 @@ struct asd_sas_port { struct domain_device *port_dev; spinlock_t dev_list_lock; struct list_head dev_list; + struct list_head disco_list; + struct list_head destroy_list; enum sas_linkrate linkrate; struct sas_phy *phy; @@ -334,6 +339,7 @@ struct sas_ha_event { enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, + SAS_HA_ATA_EH_ACTIVE, }; struct sas_ha_struct { @@ -346,6 +352,8 @@ struct sas_ha_struct { unsigned long state; spinlock_t state_lock; + struct mutex disco_mutex; + struct scsi_core core; /* public: */ diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 7d5013f8653..557fc9a8559 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -45,6 +45,7 @@ int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, enum blk_eh_timer_return *rtn); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); +void sas_probe_sata(struct work_struct *work); #else @@ -78,6 +79,10 @@ static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, return 0; } +static inline void sas_probe_sata(struct work_struct *work) +{ +} + #endif #endif /* _SAS_ATA_H_ */ diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index ffeebc34a4f..6d14daac758 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -194,6 +194,7 @@ void sas_rphy_free(struct sas_rphy *); extern int sas_rphy_add(struct sas_rphy *); extern void sas_rphy_remove(struct sas_rphy *); extern void sas_rphy_delete(struct sas_rphy *); +extern void sas_rphy_unlink(struct sas_rphy *); extern int scsi_is_sas_rphy(const struct device *); struct sas_port *sas_port_alloc(struct device *, int); -- cgit v1.2.3-70-g09d2 From b91bb296188118eea9fdc6093cfcf76bbe8589ba Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:52 -0800 Subject: [SCSI] libsas: use ->set_dmamode to notify lldds of NCQ parameters sas_discover_sata() notifies lldds of sata devices twice. Once to allow the 'identify' to be sent, and a second time to allow aic94xx (the only libsas driver that cares about sata_dev.identify) to setup NCQ parameters before the device becomes known to the midlayer. Replace this double notification and intervening 'identify' with an explicit ->lldd_ata_set_dmamode notification. With this change all ata internal commands are issued by libata, so we no longer need sas_issue_ata_cmd(). The data from the identify command only needs to be cached in one location so ata_device.id replaces domain_device.sata_dev.identify. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx.h | 2 + drivers/scsi/aic94xx/aic94xx_dev.c | 38 +++-- drivers/scsi/aic94xx/aic94xx_init.c | 2 + drivers/scsi/libsas/sas_ata.c | 324 +++--------------------------------- drivers/scsi/libsas/sas_discover.c | 5 - include/scsi/libsas.h | 4 +- 6 files changed, 49 insertions(+), 326 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h index 2863a9d2285..66cda669b41 100644 --- a/drivers/scsi/aic94xx/aic94xx.h +++ b/drivers/scsi/aic94xx/aic94xx.h @@ -80,6 +80,8 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id); int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags); +void asd_set_dmamode(struct domain_device *dev); + /* ---------- TMFs ---------- */ int asd_abort_task(struct sas_task *); int asd_abort_task_set(struct domain_device *, u8 *lun); diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c index 2e2ddec9c0b..64136c56e70 100644 --- a/drivers/scsi/aic94xx/aic94xx_dev.c +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -109,26 +109,37 @@ static int asd_init_sata_tag_ddb(struct domain_device *dev) return 0; } -static int asd_init_sata(struct domain_device *dev) +void asd_set_dmamode(struct domain_device *dev) { struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + struct ata_device *ata_dev = sas_to_ata_dev(dev); int ddb = (int) (unsigned long) dev->lldd_dev; u32 qdepth = 0; - int res = 0; - asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); - if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) && - dev->sata_dev.identify_device && - dev->sata_dev.identify_device[10] != 0) { - u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]); - u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]); - - if (w76 & 0x100) /* NCQ? */ - qdepth = (w75 & 0x1F) + 1; + if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) { + if (ata_id_has_ncq(ata_dev->id)) + qdepth = ata_id_queue_depth(ata_dev->id); asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, (1ULL< 0) + if (asd_init_sata_tag_ddb(dev) != 0) { + unsigned long flags; + + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); + ata_dev->flags |= ATA_DFLAG_NCQ_OFF; + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + } +} + +static int asd_init_sata(struct domain_device *dev) +{ + struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; + int ddb = (int) (unsigned long) dev->lldd_dev; + + asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF); if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || dev->dev_type == SATA_PM_PORT) { struct dev_to_host_fis *fis = (struct dev_to_host_fis *) @@ -136,9 +147,8 @@ static int asd_init_sata(struct domain_device *dev) asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status); } asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF); - if (qdepth > 0) - res = asd_init_sata_tag_ddb(dev); - return res; + + return 0; } static int asd_init_target_ddb(struct domain_device *dev) diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 2b3717f6d22..eea988a04f9 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -1009,6 +1009,8 @@ static struct sas_domain_function_template aic94xx_transport_functions = { .lldd_clear_nexus_ha = asd_clear_nexus_ha, .lldd_control_phy = asd_control_phy, + + .lldd_ata_set_dmamode = asd_set_dmamode, }; static const struct pci_device_id aic94xx_pci_table[] __devinitdata = { diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4b6365c6410..71af919b856 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -367,6 +367,17 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) } } + +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 = + to_sas_internal(dev->port->ha->core.shost->transportt); + + 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, @@ -380,6 +391,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 = { @@ -442,163 +454,6 @@ 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 = @@ -642,122 +497,6 @@ 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) -{ - int res; - __le16 *identify_x; - u8 command; - - 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; - } - - 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; - } -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); - */ - - sas_fill_in_rphy(dev, dev->rphy); - - return 0; -out_err: - dev->sata_dev.identify_packet_device = NULL; - dev->sata_dev.identify_device = NULL; - kfree(identify_x); - return res; -} - -static int sas_discover_sata_pm(struct domain_device *dev) -{ - return -ENODEV; -} - void sas_probe_sata(struct work_struct *work) { struct domain_device *dev, *n; @@ -791,49 +530,26 @@ void sas_probe_sata(struct work_struct *work) * 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; - sas_get_ata_command_set(dev); - - 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 (dev->dev_type == SATA_PM) + return -ENODEV; - if (res) - return res; + sas_get_ata_command_set(dev); + sas_fill_in_rphy(dev, dev->rphy); res = sas_notify_lldd_dev_found(dev); if (res) return res; sas_discover_event(dev->port, DISCE_PROBE); - - return res; + return 0; } void sas_ata_strategy_handler(struct Scsi_Host *shost) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 7e8fdcb202b..bad5eba4a92 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -237,11 +237,6 @@ void sas_free_device(struct kref *kref) if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) kfree(dev->ex_dev.ex_phy); - if (dev_is_sata(dev)) { - kfree(dev->sata_dev.identify_device); - kfree(dev->sata_dev.identify_packet_device); - } - kfree(dev); } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index bd6e89ece2a..9c13a5c0bb3 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -164,9 +164,6 @@ enum ata_command_set { struct sata_device { enum ata_command_set command_set; struct smp_resp rps_resp; /* report_phy_sata_resp */ - __le16 *identify_device; - __le16 *identify_packet_device; - u8 port_no; /* port number, if this is a PM (Port) */ struct list_head children; /* PM Ports if this is a PM */ @@ -609,6 +606,7 @@ struct sas_domain_function_template { int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); int (*lldd_ata_soft_reset)(struct domain_device *); + void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); int (*lldd_query_task)(struct sas_task *); -- cgit v1.2.3-70-g09d2 From e500a34b0257def5b9ec07563afeeada1ead87bb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Nov 2011 17:11:33 -0800 Subject: [SCSI] libsas: kill invocation of scsi_eh_finish_cmd from sas_ata_task_done Prior to the conversion to the new-style libata-eh sas_ata_task_done() may have been the last opportunity to clean up the scmd, but now libata-eh explicitly handles this case. It also races against sas-eh. If a lldd completes a task after SAS_TASK_STATE_ABORTED is set it could trigger a spurious decrement of shost->host_failed. Current lldds have the band-aid of checking SAS_TASK_STATE_ABORTED before calling ->task_done(), but better to just let the scmds escalate to libata for race free cleanup. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 71af919b856..5cb0a2ae592 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -145,20 +145,6 @@ static void sas_ata_task_done(struct sas_task *task) 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); - qc_already_gone: list_del_init(&task->list); sas_free_task(task); -- cgit v1.2.3-70-g09d2 From 3dff5721e4f67e6231dfc419d30aaa7563bfffd4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Nov 2011 12:08:22 -0800 Subject: [SCSI] libsas: close error handling vs sas_ata_task_done() race Since sas_ata does not implement ->freeze(), completions for scmds and internal commands can still arrive concurrent with ata_scsi_cmd_error_handler() and sas_ata_post_internal() respectively. By the time either of those is called libata has committed to completing the qc, and the ATA_PFLAG_FROZEN flag tells sas_ata_task_done() it has lost the race. In the sas_ata_post_internal() case we take on the additional responsibility of freeing the sas_task to close the race with sas_ata_task_done() freeing the the task while sas_ata_post_internal() is in the process of invoking ->lldd_abort_task(). Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 84 +++++++++++++++++++++++++++++++++---- drivers/scsi/libsas/sas_scsi_host.c | 44 ------------------- include/scsi/libsas.h | 1 - 3 files changed, 75 insertions(+), 54 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5cb0a2ae592..903bb441b9f 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -100,15 +100,31 @@ static void sas_ata_task_done(struct sas_task *task) enum ata_completion_errors ac; unsigned long flags; struct ata_link *link; + struct ata_port *ap; if (!qc) goto qc_already_gone; - dev = qc->ap->private_data; + ap = qc->ap; + dev = ap->private_data; sas_ha = dev->port->ha; - link = &dev->sata_dev.ap->link; + 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))) { @@ -143,7 +159,7 @@ static void sas_ata_task_done(struct sas_task *task) if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, NULL); ata_qc_complete(qc); - spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + spin_unlock_irqrestore(ap->lock, flags); qc_already_gone: list_del_init(&task->list); @@ -325,6 +341,54 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, 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 = + 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); + 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) @@ -332,10 +396,12 @@ 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; @@ -348,7 +414,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) spin_unlock_irqrestore(&task->task_state_lock, flags); task->uldd_task = NULL; - __sas_task_abort(task); + sas_ata_internal_abort(task); } } } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 15533a17eb9..ba5876ccd29 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -956,49 +956,6 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha) spin_unlock_irqrestore(&core->task_queue_lock, flags); } -/* - * 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. @@ -1097,7 +1054,6 @@ EXPORT_SYMBOL_GPL(sas_slave_configure); 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); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 9c13a5c0bb3..10eb2ea7443 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -662,7 +662,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *); void sas_init_dev(struct domain_device *); void sas_task_abort(struct sas_task *); -int __sas_task_abort(struct sas_task *); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd); -- cgit v1.2.3-70-g09d2 From 9095a64a9aead653df320e3a6fc70835c15d46e4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Nov 2011 11:29:20 -0800 Subject: [SCSI] libsas: fix timeout vs completion race Until we have told the lldd to forget a task a timed out operation can return from the hardware at any time. Since completion frees the task we need to make sure that no tasks run their normal completion handler once eh has decided to manage the task. Similar to ata_scsi_cmd_error_handler() freeze completions to let eh judge the outcome of the race. Task collector mode is problematic because it presents a situation where a task can be timed out and aborted before the lldd has even seen it. For this case we need to guarantee that a task that an lldd has been told to forget does not get queued after the lldd says "never seen it". With sas_scsi_timed_out we achieve this with the ->task_queue_flush mutex, rather than adding more time. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 35 +++++------- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_scsi_host.c | 104 ++++++++++++++++++------------------ include/scsi/libsas.h | 3 ++ include/scsi/sas_ata.h | 8 --- 5 files changed, 68 insertions(+), 83 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 903bb441b9f..4c2a1402373 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -93,21 +93,30 @@ 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; ap = qc->ap; - dev = ap->private_data; - sas_ha = dev->port->ha; link = &ap->link; spin_lock_irqsave(ap->lock, flags); @@ -156,8 +165,6 @@ 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(ap->lock, flags); @@ -633,22 +640,6 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) sas_enable_revalidation(sas_ha); } -int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, - enum blk_eh_timer_return *rtn) -{ - struct domain_device *ddev = cmd_to_domain_dev(cmd); - - if (!dev_is_sata(ddev) || task) - return 0; - - /* 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; -} - int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index ebe9b81ddef..662ffcba99d 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -142,6 +142,7 @@ static inline struct domain_device *sas_alloc_device(void) 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; } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 50db8f971a0..0e3fdba7b51 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -119,9 +119,19 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_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_state_flags & SAS_TASK_STATE_ABORTED)) { - /* Aborted tasks will be completed by the error handler */ + if (unlikely(!task)) { + /* task will be completed by the error handler */ SAS_DPRINTK("task done but aborted\n"); return; } @@ -133,7 +143,6 @@ static void sas_scsi_task_done(struct sas_task *task) return; } - ASSIGN_SAS_TASK(sc, NULL); sas_end_task(sc, task); sc->scsi_done(sc); } @@ -298,6 +307,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, }; @@ -314,19 +324,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++) { @@ -499,8 +508,7 @@ try_bus_reset: } static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, - struct list_head *work_q, - struct list_head *done_q) + struct list_head *work_q) { struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; @@ -511,7 +519,16 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, Again: 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; @@ -534,6 +551,14 @@ 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); @@ -635,7 +660,8 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism) */ - if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q)) + set_bit(SAS_HA_FROZEN, &ha->state); + if (sas_eh_handle_sas_errors(shost, &eh_work_q)) goto out; /* @@ -649,6 +675,10 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); out: + clear_bit(SAS_HA_FROZEN, &ha->state); + 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); @@ -660,43 +690,7 @@ out: 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; } @@ -861,9 +855,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) { @@ -899,6 +895,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); } /** @@ -925,6 +922,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); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 071041b290d..aa7192ff435 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -178,6 +178,7 @@ enum { }; struct domain_device { + spinlock_t done_lock; enum sas_dev_type dev_type; enum sas_linkrate linkrate; @@ -321,6 +322,7 @@ struct asd_sas_phy { struct scsi_core { struct Scsi_Host *shost; + struct mutex task_queue_flush; spinlock_t task_queue_lock; struct list_head task_queue; int task_queue_size; @@ -337,6 +339,7 @@ enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, SAS_HA_ATA_EH_ACTIVE, + SAS_HA_FROZEN, }; struct sas_ha_struct { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 557fc9a8559..9f7a23d1146 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -41,8 +41,6 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); -int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task, - enum blk_eh_timer_return *rtn); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); @@ -67,12 +65,6 @@ static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) { } -static inline int sas_ata_timed_out(struct scsi_cmnd *cmd, - struct sas_task *task, - enum blk_eh_timer_return *rtn) -{ - return 0; -} static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { -- cgit v1.2.3-70-g09d2 From 3a2cdf391b62919d3d2862cdce3d70b9a7a99673 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 29 Nov 2011 14:54:28 -0800 Subject: [SCSI] libsas: defer SAS_TASK_NEED_DEV_RESET commands to libata lldds use the SAS_TASK_NEED_DEV_RESET interface to request that eh perform a reset. In the sata device case defer the commands that triggered the reset to libata-eh context so it can perform its pre and post reset management. In the sas_ata_post_internal() case the reset request is falling on deaf ears as the sas_task is immediately destroyed without any reset action. Since it is currently a nop, and likely superfluous given the conversion to new-style libata-eh, just drop the request. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 14 ++++---------- drivers/scsi/libsas/sas_scsi_host.c | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4c2a1402373..a8ace8d24e6 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -411,18 +411,12 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) * 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_ata_internal_abort(task); - } + if (!task) + return; + task->uldd_task = NULL; + sas_ata_internal_abort(task); } } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e02ca3d570f..af71a6d0eda 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -288,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); } } @@ -594,7 +594,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; } -- cgit v1.2.3-70-g09d2 From b52df4174dff7e587f6fbfb21e3c2cb57109e5cf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 30 Nov 2011 23:23:33 -0800 Subject: [SCSI] libsas: use libata-eh-reset for sata rediscovery fis transmit failures Since sata devices can take several seconds to recover the link on reset the 0.5 seconds that libsas currently waits may not be enough. Instead if we are rediscovering a phy that was previously attached to a sata device let libata handle any resets to encourage the device to transmit the initial fis. Once sas_ata_hard_reset() and lldds learn how to honor 'deadline' libsas should stop encountering phys in an intermediate state, until then this will loop until the fis is transmitted or ->attached_sas_addr gets cleared, but in the more likely initial discovery case we keep existing behavior. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 19 ++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 44 +++++++++++++++++++++++++++++++++----- include/scsi/sas_ata.h | 6 +++++- 3 files changed, 63 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a8ace8d24e6..48cadf88c39 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -679,3 +679,22 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, return rtn; } + +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); +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e45b259dac4..f4894b0f537 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -28,6 +28,7 @@ #include "sas_internal.h" +#include #include #include #include "../scsi_sas_internal.h" @@ -226,12 +227,35 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, return; } +/* check if we have an existing attached ata device on this expander phy */ +static 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; + + 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 #define DISCOVER_RESP_SIZE 56 static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, u8 *disc_resp, int single) { + struct domain_device *ata_dev = sas_ex_to_ata(dev, single); int i, res; disc_req[9] = single; @@ -242,20 +266,30 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, 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; } + + /* 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 == 0 && dr->attached_sata_dev)) break; - /* In order to generate the dev to host FIS, we - * send a link reset to the expander port */ + + /* In order to generate the dev to host FIS, we send a + * link reset to the expander port. If a device was + * previously detected on this port we ask libata to + * manage the reset and link recovery. + */ + if (ata_dev) { + sas_ata_schedule_reset(ata_dev); + break; + } sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); /* Wait for the reset to trigger the negotiation */ msleep(500); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 9f7a23d1146..c0bcd30eec5 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -44,7 +44,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost); int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); - +void sas_ata_schedule_reset(struct domain_device *dev); #else @@ -75,6 +75,10 @@ static inline void sas_probe_sata(struct work_struct *work) { } +static inline void sas_ata_schedule_reset(struct domain_device *dev) +{ +} + #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3-70-g09d2 From 81c757bc696284f39f07766f0c2ca67af64ce9bd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Dec 2011 16:07:01 -0800 Subject: [SCSI] libsas: execute transport link resets with libata-eh via host workqueue Link resets leave ata affiliations intact, so arrange for libsas to make an effort to avoid dropping the device due to a slow-to-recover link. Towards this end carry out reset in the host workqueue so that it can check for ata devices and kick the reset request to libata. Hard resets, in contrast, bypass libata since they are meant for associating an ata device with another initiator in the domain (tears down affiliations). Need to add a new transport_sas_phy_reset() since the current sas_phy_reset() is a utility function to libsas lldds. They are not prepared for it to loop back into eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata.h | 1 - drivers/scsi/libsas/sas_ata.c | 11 ++++++++ drivers/scsi/libsas/sas_expander.c | 2 +- drivers/scsi/libsas/sas_init.c | 56 +++++++++++++++++++++++++++++++++++++- drivers/scsi/libsas/sas_internal.h | 1 + include/linux/libata.h | 1 + include/scsi/sas_ata.h | 4 +++ 8 files changed, 74 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a9b28203800..c61316e9d2f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -863,6 +863,7 @@ void ata_port_wait_eh(struct ata_port *ap) goto retry; } } +EXPORT_SYMBOL_GPL(ata_port_wait_eh); static int ata_eh_nr_in_flight(struct ata_port *ap) { diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 814486d35c4..1fab235ee51 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -151,7 +151,6 @@ extern void ata_eh_acquire(struct ata_port *ap); extern void ata_eh_release(struct ata_port *ap); extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); -extern void ata_port_wait_eh(struct ata_port *ap); extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); extern void ata_dev_disable(struct ata_device *dev); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 48cadf88c39..03930a04a67 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -698,3 +698,14 @@ void sas_ata_schedule_reset(struct domain_device *dev) ata_port_schedule_eh(ap); spin_unlock_irqrestore(ap->lock, flags); } + +void sas_ata_wait_eh(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return; + + ap = dev->sata_dev.ap; + ata_port_wait_eh(ap); +} diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index f4894b0f537..d3c1a29b8a2 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -228,7 +228,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, } /* check if we have an existing attached ata device on this expander phy */ -static struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) +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; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index cb65adf4ab1..a15fb861dab 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,59 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } +/** + * 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) +{ + int ret; + 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); + struct domain_device *dev = NULL; + + 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) && !hard_reset) { + sas_ata_schedule_reset(dev); + sas_ata_wait_eh(dev); + ret = 0; + } else + ret = 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); + ret = 0; + } else + ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL); + } + + return ret; +} + int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; @@ -300,7 +354,7 @@ static void phy_reset_work(struct work_struct *work) { struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); - d->reset_result = sas_phy_reset(d->phy, d->hard_reset); + d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static int sas_phy_setup(struct sas_phy *phy) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9ba65e0c6f9..ae9698d9d85 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -85,6 +85,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, int sas_smp_get_phy_events(struct sas_phy *phy); 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); void sas_hae_reset(struct work_struct *work); diff --git a/include/linux/libata.h b/include/linux/libata.h index cafc09a64fe..aa427047756 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1147,6 +1147,7 @@ static inline int ata_acpi_cbl_80wire(struct ata_port *ap, * EH - drivers/ata/libata-eh.c */ extern void ata_port_schedule_eh(struct ata_port *ap); +extern void ata_port_wait_eh(struct ata_port *ap); extern int ata_link_abort(struct ata_link *link); extern int ata_port_abort(struct ata_port *ap); extern int ata_port_freeze(struct ata_port *ap); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index c0bcd30eec5..da3f3772738 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -45,6 +45,7 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); +void sas_ata_wait_eh(struct domain_device *dev); #else @@ -79,6 +80,9 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) { } +static inline void sas_ata_wait_eh(struct domain_device *dev) +{ +} #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3-70-g09d2 From 50824d6c5657ce340e3911171865a8d99fdd8eba Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 4 Dec 2011 01:06:24 -0800 Subject: [SCSI] libsas: async ata-eh Once sas_ata_hard_reset() starts honoring the 'deadline' parameter a pathological configuration could take 25 seconds per ata device (serialized) to recover. Run per-port recoveries in parallel. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 03930a04a67..4beca66728b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "sas_internal.h" @@ -605,10 +606,21 @@ int sas_discover_sata(struct domain_device *dev) return 0; } +static void async_sas_ata_eh(void *data, async_cookie_t cookie) +{ + struct domain_device *dev = data; + struct ata_port *ap = dev->sata_dev.ap; + struct sas_ha_struct *ha = dev->port->ha; + + ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); + ata_scsi_port_error_handler(ha->core.shost, ap); +} + void sas_ata_strategy_handler(struct Scsi_Host *shost) { struct scsi_device *sdev; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + LIST_HEAD(async); /* it's ok to defer revalidation events during ata eh, these * disks are in one of three states: @@ -622,14 +634,13 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) shost_for_each_device(sdev, shost) { struct domain_device *ddev = sdev_to_domain_dev(sdev); - struct ata_port *ap = ddev->sata_dev.ap; if (!dev_is_sata(ddev)) continue; - ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); - ata_scsi_port_error_handler(shost, ap); + async_schedule_domain(async_sas_ata_eh, ddev, &async); } + async_synchronize_full_domain(&async); sas_enable_revalidation(sas_ha); } -- cgit v1.2.3-70-g09d2 From 36a399473902a57218dc493c5a814708a56b73ab Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 17 Nov 2011 17:59:54 -0800 Subject: [SCSI] libsas: poll for ata device readiness after reset Use ata_wait_after_reset() to poll for link recovery after a reset. This combined with sas_ha->eh_mutex prevents expander rediscovery from probing phys in an intermediate state. Local discovery does not have a mechanism to filter link status changes during this timeout, so it remains the responsibility of lldds to prevent premature port teardown. Although once all lldd's support ->lldd_ata_check_ready() that could be used as a gate to local port teardown. The signature fis is re-transmitted when the link comes back so we should be revalidating the ata device class, but that is left to a future patch. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 104 ++++++++++++++++++++++++++----------- drivers/scsi/libsas/sas_expander.c | 10 ++-- drivers/scsi/libsas/sas_internal.h | 3 +- include/scsi/libsas.h | 1 + 4 files changed, 83 insertions(+), 35 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 4beca66728b..5fdb63ad94b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -272,39 +272,84 @@ 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 int smp_ata_check_ready(struct ata_link *link) { + int res; + u8 addr[8]; 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_find_local_phy(dev); - if (i->dft->lldd_I_T_nexus_reset) - res = i->dft->lldd_I_T_nexus_reset(dev); + res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + /* break the wait early if the expander is unreachable, + * otherwise keep polling + */ + if (res == -ECOMM) + return res; + if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0) + return 0; + else + return 1; +} - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__); - ret = -EAGAIN; +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; } +} +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int ret = 0, res; + struct ata_port *ap = link->ap; + int (*check_ready)(struct ata_link *link); + struct domain_device *dev = ap->private_data; + struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_internal *i = dev_to_sas_internal(dev); + + res = i->dft->lldd_I_T_nexus_reset(dev); + + if (res != TMF_RESP_FUNC_COMPLETE) + SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + + if (scsi_is_sas_phy_local(phy)) + check_ready = local_ata_check_ready; + else + check_ready = smp_ata_check_ready; + + ret = ata_wait_after_reset(link, deadline, check_ready); + if (ret && ret != -EAGAIN) + ata_link_err(link, "COMRESET 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; + case ATA_COMMAND_SET: + *class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + *class = ATA_DEV_ATAPI; + break; } ap->cbl = ATA_CBL_SATA; @@ -316,8 +361,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, { 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); + struct sas_internal *i = dev_to_sas_internal(dev); int res = TMF_RESP_FUNC_FAILED; int ret = 0; @@ -355,8 +399,7 @@ static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, */ static void sas_ata_internal_abort(struct sas_task *task) { - struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); + struct sas_internal *si = dev_to_sas_internal(task->dev); unsigned long flags; int res; @@ -425,8 +468,7 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) 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 = - to_sas_internal(dev->port->ha->core.shost->transportt); + struct sas_internal *i = dev_to_sas_internal(dev); if (i->dft->lldd_ata_set_dmamode) i->dft->lldd_ata_set_dmamode(dev); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 7c59f97c028..32e417e6c2f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -125,7 +125,11 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, 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), @@ -1648,8 +1652,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) +int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, + u8 *attached_sas_addr) { int res; struct smp_resp *disc_resp; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 9e960b2d535..a9a3bb94c1b 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -89,7 +89,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy); 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_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, + u8 *attached_sas_addr); void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 18704a2e4f0..2079b18467a 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -615,6 +615,7 @@ struct sas_domain_function_template { int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); int (*lldd_ata_soft_reset)(struct domain_device *); + int (*lldd_ata_check_ready)(struct domain_device *); void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); int (*lldd_query_task)(struct sas_task *); -- cgit v1.2.3-70-g09d2 From f41a0c441c3fe43e79ebeb75584dbb5bfa83e5cd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Dec 2011 21:33:17 -0800 Subject: [SCSI] libsas: fix sas_find_local_phy(), take phy references In the direct-attached case this routine returns the phy on which this device was first discovered. Which is broken if we want to support wide-targets, as this phy reference can become stale even though the port is still active. In the expander-attached case this routine tries to lookup the phy by scanning the attached sas addresses of the parent expander, and BUG_ONs if it can't find it. However since eh and the libsas workqueue run independently we can still be attempting device recovery via eh after libsas has recorded the device as detached. This is even easier to hit now that eh is blocked while device domain rediscovery takes place, and that libata is fed more timed out commands increasing the chances that it will try to recover the ata device. Arrange for dev->phy to always point to a last known good phy, it may be stale after the port is torn down, but it will catch up for wide port reconfigurations, and never be NULL. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_tmf.c | 9 ++++++--- drivers/scsi/isci/task.c | 9 +++++---- drivers/scsi/libsas/sas_ata.c | 7 +++++-- drivers/scsi/libsas/sas_discover.c | 24 +++++++++++++++++++++++ drivers/scsi/libsas/sas_expander.c | 5 ++++- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_port.c | 7 +++---- drivers/scsi/libsas/sas_scsi_host.c | 38 ++++++++++++++++++------------------- drivers/scsi/mvsas/mv_sas.c | 3 ++- drivers/scsi/pm8001/pm8001_sas.c | 19 ++++++++++++------- drivers/scsi/scsi_transport_sas.c | 23 ++++++++++++++++++++++ include/scsi/libsas.h | 9 +++++++-- include/scsi/scsi_transport_sas.h | 6 ++++++ 13 files changed, 116 insertions(+), 44 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 0add73bdf2a..50b914ffab9 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -181,7 +181,7 @@ static int asd_clear_nexus_I_T(struct domain_device *dev, int asd_I_T_nexus_reset(struct domain_device *dev) { int res, tmp_res, i; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); /* Standard mandates link reset for ATA (type 0) and * hard reset for SSP (type 1) */ int reset_type = (dev->dev_type == SATA_DEV || @@ -201,7 +201,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev) for (i = 0 ; i < 3; i++) { tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME); if (tmp_res == TC_RESUME) - return res; + goto out; msleep(500); } @@ -211,7 +211,10 @@ int asd_I_T_nexus_reset(struct domain_device *dev) dev_printk(KERN_ERR, &phy->dev, "Failed to resume nexus after reset 0x%x\n", tmp_res); - return TMF_RESP_FUNC_FAILED; + res = TMF_RESP_FUNC_FAILED; + out: + sas_put_local_phy(phy); + return res; } static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 4bd88ef83cd..b96e6044eda 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -1332,7 +1332,7 @@ isci_task_request_complete(struct isci_host *ihost, static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { - struct sas_phy *phy = sas_find_local_phy(idev->domain_dev); + struct sas_phy *phy = sas_get_local_phy(idev->domain_dev); enum sci_status status; unsigned long flags; int rc; @@ -1347,8 +1347,8 @@ static int isci_reset_device(struct isci_host *ihost, dev_dbg(&ihost->pdev->dev, "%s: sci_remote_device_reset(%p) returned %d!\n", __func__, idev, status); - - return TMF_RESP_FUNC_FAILED; + rc = TMF_RESP_FUNC_FAILED; + goto out; } spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1369,7 +1369,8 @@ static int isci_reset_device(struct isci_host *ihost, } dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); - + out: + sas_put_local_phy(phy); return rc; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 5fdb63ad94b..92f7e78a096 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -284,9 +284,10 @@ static int smp_ata_check_ready(struct ata_link *link) struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; struct domain_device *ex_dev = dev->parent; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + sas_put_local_phy(phy); /* break the wait early if the expander is unreachable, * otherwise keep polling */ @@ -319,10 +320,10 @@ 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_phy *phy = sas_find_local_phy(dev); struct sas_internal *i = dev_to_sas_internal(dev); res = i->dft->lldd_I_T_nexus_reset(dev); @@ -330,10 +331,12 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, if (res != TMF_RESP_FUNC_COMPLETE) SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + 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) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c56cc640081..789b50861bb 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -147,6 +147,7 @@ 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; @@ -234,6 +235,9 @@ void sas_free_device(struct kref *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); @@ -308,6 +312,26 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) } +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 ---------- */ /** diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 6fb1f3afd1e..68a80a00f73 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -723,6 +723,7 @@ 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) { @@ -1810,7 +1811,7 @@ 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) { @@ -1822,6 +1823,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); + found = child; break; } } @@ -1830,6 +1832,7 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, 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; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a9a3bb94c1b..c8febc71c40 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -87,6 +87,7 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); +void sas_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_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 2980bde4e34..31adcd1b419 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -108,9 +108,6 @@ static void sas_form_port(struct asd_sas_phy *phy) 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, @@ -175,8 +172,10 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) sas_unregister_domain_devices(port); 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); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5cc44fddfe9..94ef76316c3 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -439,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) @@ -489,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); @@ -497,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; diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index cd882230591..b68a65390f0 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -1474,10 +1474,11 @@ static int mvs_debug_issue_ssp_tmf(struct domain_device *dev, static int mvs_debug_I_T_nexus_reset(struct domain_device *dev) { int rc; - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); int reset_type = (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; rc = sas_phy_reset(phy, reset_type); + sas_put_local_phy(phy); msleep(2000); return rc; } diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 310860e37d9..3b11edd4a50 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -967,12 +967,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) pm8001_dev = dev->lldd_dev; pm8001_ha = pm8001_find_ha_by_dev(dev); - phy = sas_find_local_phy(dev); + phy = sas_get_local_phy(dev); if (dev_is_sata(dev)) { DECLARE_COMPLETION_ONSTACK(completion_setstate); - if (scsi_is_sas_phy_local(phy)) - return 0; + if (scsi_is_sas_phy_local(phy)) { + rc = 0; + goto out; + } rc = sas_phy_reset(phy, 1); msleep(2000); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , @@ -981,12 +983,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev) rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, pm8001_dev, 0x01); wait_for_completion(&completion_setstate); - } else{ - rc = sas_phy_reset(phy, 1); - msleep(2000); + } else { + rc = sas_phy_reset(phy, 1); + msleep(2000); } PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n", pm8001_dev->device_id, rc)); + out: + sas_put_local_phy(phy); return rc; } @@ -998,10 +1002,11 @@ int pm8001_lu_reset(struct domain_device *dev, u8 *lun) struct pm8001_device *pm8001_dev = dev->lldd_dev; struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev); if (dev_is_sata(dev)) { - struct sas_phy *phy = sas_find_local_phy(dev); + struct sas_phy *phy = sas_get_local_phy(dev); rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev , dev, 1, 0); rc = sas_phy_reset(phy, 1); + sas_put_local_phy(phy); rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, pm8001_dev, 0x01); msleep(2000); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index ab3bd0b5ffd..7d69a25d200 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1059,6 +1059,29 @@ int scsi_is_sas_port(const struct device *dev) } EXPORT_SYMBOL(scsi_is_sas_port); +/** + * sas_port_get_phy - try to take a reference on a port member + * @port: port to check + */ +struct sas_phy *sas_port_get_phy(struct sas_port *port) +{ + struct sas_phy *phy; + + mutex_lock(&port->phy_list_mutex); + if (list_empty(&port->phy_list)) + phy = NULL; + else { + struct list_head *ent = port->phy_list.next; + + phy = list_entry(ent, typeof(*phy), port_siblings); + get_device(&phy->dev); + } + mutex_unlock(&port->phy_list_mutex); + + return phy; +} +EXPORT_SYMBOL(sas_port_get_phy); + /** * sas_port_add_phy - add another phy to a port to form a wide port * @port: port to add the phy to diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 2079b18467a..55bab863380 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -192,6 +192,7 @@ struct domain_device { struct domain_device *parent; struct list_head siblings; /* devices on the same level */ struct asd_sas_port *port; /* shortcut to root of the tree */ + struct sas_phy *phy; struct list_head dev_list_node; struct list_head disco_list_node; /* awaiting probe or destruct */ @@ -243,7 +244,6 @@ struct asd_sas_port { struct list_head destroy_list; enum sas_linkrate linkrate; - struct sas_phy *phy; struct work_struct work; /* public: */ @@ -429,6 +429,11 @@ static inline unsigned int to_sas_gpio_od(int device, int bit) return 3 * device + bit; } +static inline void sas_put_local_phy(struct sas_phy *phy) +{ + put_device(&phy->dev); +} + #ifdef CONFIG_SCSI_SAS_HOST_SMP int try_test_sas_gpio_gp_bit(unsigned int od, u8 *data, u8 index, u8 count); #else @@ -684,7 +689,7 @@ extern int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, extern void sas_ssp_task_response(struct device *dev, struct sas_task *task, struct ssp_response_iu *iu); -struct sas_phy *sas_find_local_phy(struct domain_device *dev); +struct sas_phy *sas_get_local_phy(struct domain_device *dev); int sas_request_addr(struct Scsi_Host *shost, u8 *addr); diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 42817facaed..98b3a20a010 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -209,6 +209,12 @@ void sas_port_add_phy(struct sas_port *, struct sas_phy *); void sas_port_delete_phy(struct sas_port *, struct sas_phy *); void sas_port_mark_backlink(struct sas_port *); int scsi_is_sas_port(const struct device *); +struct sas_phy *sas_port_get_phy(struct sas_port *port); +static inline void sas_port_put_phy(struct sas_phy *phy) +{ + if (phy) + put_device(&phy->dev); +} extern struct scsi_transport_template * sas_attach_transport(struct sas_function_template *); -- cgit v1.2.3-70-g09d2 From cb48d672bfeb4147f3e20d5b16fa2eb6377231e2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 Dec 2011 14:58:24 -0800 Subject: [SCSI] libsas: don't recover 'gone' devices in sas_ata_hard_reset() The commands that timeout when a disk is forcibly removed may trigger libata to attempt recovery of the device. If libsas has decided to remove the device don't permit ata to continue to issue resets to its last known phy. The primary motivation for this patch is hotplug testing by writing 0 to /sys/class/sas_phy/phyX/enable. Without this check this test leads to libata issuing a reset and re-enabling the device that wants to be torn down. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 92f7e78a096..0cb538f8478 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -326,6 +326,9 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, struct domain_device *dev = ap->private_data; struct sas_internal *i = dev_to_sas_internal(dev); + if (test_bit(SAS_DEV_GONE, &dev->state)) + return -ENODEV; + res = i->dft->lldd_I_T_nexus_reset(dev); if (res != TMF_RESP_FUNC_COMPLETE) -- cgit v1.2.3-70-g09d2 From 43a5ab151f0268459c4368292c2ddb2266b8f243 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 8 Dec 2011 23:20:44 -0800 Subject: [SCSI] isci: stop interpreting ->lldd_lu_reset() as an ata soft-reset Driving resets from libsas-eh is pre-mature as libata will make a decision about performing a softreset. Currently libata determines whether to perform a softreset based on ata_eh_followup_srst_needed(), and none of those conditions apply to isci. Remove the srst implementation and translate ->lldd_lu_reset() for ata devices as a request to drive a reset via libata-eh. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/isci/request.c | 195 ++---------------------------------------- drivers/scsi/isci/request.h | 9 +- drivers/scsi/isci/task.c | 93 ++------------------ drivers/scsi/isci/task.h | 2 - drivers/scsi/libsas/sas_ata.c | 2 + 5 files changed, 15 insertions(+), 286 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 788daeedc89..1a39ce50529 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -666,18 +666,12 @@ sci_io_request_construct_sata(struct isci_request *ireq, if (test_bit(IREQ_TMF, &ireq->flags)) { struct isci_tmf *tmf = isci_request_access_tmf(ireq); - if (tmf->tmf_code == isci_tmf_sata_srst_high || - tmf->tmf_code == isci_tmf_sata_srst_low) { - scu_stp_raw_request_construct_task_context(ireq); - return SCI_SUCCESS; - } else { - dev_err(&ireq->owning_controller->pdev->dev, - "%s: Request 0x%p received un-handled SAT " - "management protocol 0x%x.\n", - __func__, ireq, tmf->tmf_code); + dev_err(&ireq->owning_controller->pdev->dev, + "%s: Request 0x%p received un-handled SAT " + "management protocol 0x%x.\n", + __func__, ireq, tmf->tmf_code); - return SCI_FAILURE; - } + return SCI_FAILURE; } if (!sas_protocol_ata(task->task_proto)) { @@ -774,34 +768,6 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request * return status; } -enum sci_status sci_task_request_construct_sata(struct isci_request *ireq) -{ - enum sci_status status = SCI_SUCCESS; - - /* check for management protocols */ - if (test_bit(IREQ_TMF, &ireq->flags)) { - struct isci_tmf *tmf = isci_request_access_tmf(ireq); - - if (tmf->tmf_code == isci_tmf_sata_srst_high || - tmf->tmf_code == isci_tmf_sata_srst_low) { - scu_stp_raw_request_construct_task_context(ireq); - } else { - dev_err(&ireq->owning_controller->pdev->dev, - "%s: Request 0x%p received un-handled SAT " - "Protocol 0x%x.\n", - __func__, ireq, tmf->tmf_code); - - return SCI_FAILURE; - } - } - - if (status != SCI_SUCCESS) - return status; - sci_change_state(&ireq->sm, SCI_REQ_CONSTRUCTED); - - return status; -} - /** * sci_req_tx_bytes - bytes transferred when reply underruns request * @ireq: request that was terminated early @@ -903,9 +869,6 @@ sci_io_request_terminate(struct isci_request *ireq) case SCI_REQ_STP_PIO_WAIT_FRAME: case SCI_REQ_STP_PIO_DATA_IN: case SCI_REQ_STP_PIO_DATA_OUT: - case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED: - case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG: - case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: case SCI_REQ_ATAPI_WAIT_H2D: case SCI_REQ_ATAPI_WAIT_PIO_SETUP: case SCI_REQ_ATAPI_WAIT_D2H: @@ -2085,59 +2048,6 @@ sci_io_request_frame_handler(struct isci_request *ireq, return status; } - case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: { - struct dev_to_host_fis *frame_header; - u32 *frame_buffer; - - status = sci_unsolicited_frame_control_get_header(&ihost->uf_control, - frame_index, - (void **)&frame_header); - if (status != SCI_SUCCESS) { - dev_err(&ihost->pdev->dev, - "%s: SCIC IO Request 0x%p could not get frame " - "header for frame index %d, status %x\n", - __func__, - stp_req, - frame_index, - status); - return status; - } - - switch (frame_header->fis_type) { - case FIS_REGD2H: - sci_unsolicited_frame_control_get_buffer(&ihost->uf_control, - frame_index, - (void **)&frame_buffer); - - sci_controller_copy_sata_response(&ireq->stp.rsp, - frame_header, - frame_buffer); - - /* The command has completed with error */ - ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; - ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; - break; - - default: - dev_warn(&ihost->pdev->dev, - "%s: IO Request:0x%p Frame Id:%d protocol " - "violation occurred\n", - __func__, - stp_req, - frame_index); - - ireq->scu_status = SCU_TASK_DONE_UNEXP_FIS; - ireq->sci_status = SCI_FAILURE_PROTOCOL_VIOLATION; - break; - } - - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); - - /* Frame has been decoded return it to the controller */ - sci_controller_release_frame(ihost, frame_index); - - return status; - } case SCI_REQ_ATAPI_WAIT_PIO_SETUP: { struct sas_task *task = isci_request_access_task(ireq); @@ -2235,57 +2145,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq return status; } -static enum sci_status -stp_request_soft_reset_await_h2d_asserted_tc_event(struct isci_request *ireq, - u32 completion_code) -{ - switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): - ireq->scu_status = SCU_TASK_DONE_GOOD; - ireq->sci_status = SCI_SUCCESS; - sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG); - break; - - default: - /* - * All other completion status cause the IO to be complete. - * If a NAK was received, then it is up to the user to retry - * the request. - */ - ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); - ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR; - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); - break; - } - - return SCI_SUCCESS; -} - -static enum sci_status -stp_request_soft_reset_await_h2d_diagnostic_tc_event(struct isci_request *ireq, - u32 completion_code) -{ - switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): - ireq->scu_status = SCU_TASK_DONE_GOOD; - ireq->sci_status = SCI_SUCCESS; - sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_D2H); - break; - - default: - /* All other completion status cause the IO to be complete. If - * a NAK was received, then it is up to the user to retry the - * request. - */ - ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); - ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR; - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); - break; - } - - return SCI_SUCCESS; -} - static enum sci_status atapi_raw_completion(struct isci_request *ireq, u32 completion_code, enum sci_base_request_states next) { @@ -2431,14 +2290,6 @@ sci_io_request_tc_completion(struct isci_request *ireq, case SCI_REQ_STP_PIO_DATA_OUT: return pio_data_out_tx_done_tc_event(ireq, completion_code); - case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED: - return stp_request_soft_reset_await_h2d_asserted_tc_event(ireq, - completion_code); - - case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG: - return stp_request_soft_reset_await_h2d_diagnostic_tc_event(ireq, - completion_code); - case SCI_REQ_ABORTING: return request_aborting_state_tc_event(ireq, completion_code); @@ -3212,10 +3063,6 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm) */ if (!task && dev->dev_type == SAS_END_DEV) { state = SCI_REQ_TASK_WAIT_TC_COMP; - } else if (!task && - (isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_high || - isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_low)) { - state = SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED; } else if (task && task->task_proto == SAS_PROTOCOL_SMP) { state = SCI_REQ_SMP_WAIT_RESP; } else if (task && sas_protocol_ata(task->task_proto) && @@ -3272,31 +3119,6 @@ static void sci_stp_request_started_pio_await_h2d_completion_enter(struct sci_ba ireq->target_device->working_request = ireq; } -static void sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter(struct sci_base_state_machine *sm) -{ - struct isci_request *ireq = container_of(sm, typeof(*ireq), sm); - - ireq->target_device->working_request = ireq; -} - -static void sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter(struct sci_base_state_machine *sm) -{ - struct isci_request *ireq = container_of(sm, typeof(*ireq), sm); - struct scu_task_context *tc = ireq->tc; - struct host_to_dev_fis *h2d_fis; - enum sci_status status; - - /* Clear the SRST bit */ - h2d_fis = &ireq->stp.cmd; - h2d_fis->control = 0; - - /* Clear the TC control bit */ - tc->control_frame = 0; - - status = sci_controller_continue_io(ireq); - WARN_ONCE(status != SCI_SUCCESS, "isci: continue io failure\n"); -} - static const struct sci_base_state sci_request_state_table[] = { [SCI_REQ_INIT] = { }, [SCI_REQ_CONSTRUCTED] = { }, @@ -3315,13 +3137,6 @@ static const struct sci_base_state sci_request_state_table[] = { [SCI_REQ_STP_PIO_DATA_OUT] = { }, [SCI_REQ_STP_UDMA_WAIT_TC_COMP] = { }, [SCI_REQ_STP_UDMA_WAIT_D2H] = { }, - [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED] = { - .enter_state = sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter, - }, - [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG] = { - .enter_state = sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter, - }, - [SCI_REQ_STP_SOFT_RESET_WAIT_D2H] = { }, [SCI_REQ_TASK_WAIT_TC_COMP] = { }, [SCI_REQ_TASK_WAIT_TC_RESP] = { }, [SCI_REQ_SMP_WAIT_RESP] = { }, diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index be38933dd6d..bcf2f37f23e 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -211,10 +211,6 @@ enum sci_base_request_states { SCI_REQ_STP_NON_DATA_WAIT_H2D, SCI_REQ_STP_NON_DATA_WAIT_D2H, - SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED, - SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG, - SCI_REQ_STP_SOFT_RESET_WAIT_D2H, - /* * While in this state the IO request object is waiting for the TC * completion notification for the H2D Register FIS @@ -446,10 +442,7 @@ sci_task_request_construct(struct isci_host *ihost, struct isci_remote_device *idev, u16 io_tag, struct isci_request *ireq); -enum sci_status -sci_task_request_construct_ssp(struct isci_request *ireq); -enum sci_status -sci_task_request_construct_sata(struct isci_request *ireq); +enum sci_status sci_task_request_construct_ssp(struct isci_request *ireq); void sci_smp_request_copy_response(struct isci_request *ireq); static inline int isci_task_is_ncq_recovery(struct sas_task *task) diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index c4d324ccee1..3f04e97128a 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -247,46 +247,6 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) return 0; } -static enum sci_status isci_sata_management_task_request_build(struct isci_request *ireq) -{ - struct isci_tmf *isci_tmf; - enum sci_status status; - - if (!test_bit(IREQ_TMF, &ireq->flags)) - return SCI_FAILURE; - - isci_tmf = isci_request_access_tmf(ireq); - - switch (isci_tmf->tmf_code) { - - case isci_tmf_sata_srst_high: - case isci_tmf_sata_srst_low: { - struct host_to_dev_fis *fis = &ireq->stp.cmd; - - memset(fis, 0, sizeof(*fis)); - - fis->fis_type = 0x27; - fis->flags &= ~0x80; - fis->flags &= 0xF0; - if (isci_tmf->tmf_code == isci_tmf_sata_srst_high) - fis->control |= ATA_SRST; - else - fis->control &= ~ATA_SRST; - break; - } - /* other management commnd go here... */ - default: - return SCI_FAILURE; - } - - /* core builds the protocol specific request - * based on the h2d fis. - */ - status = sci_task_request_construct_sata(ireq); - - return status; -} - static struct isci_request *isci_task_request_build(struct isci_host *ihost, struct isci_remote_device *idev, u16 tag, struct isci_tmf *isci_tmf) @@ -326,13 +286,6 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost, return NULL; } - if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { - isci_tmf->proto = SAS_PROTOCOL_SATA; - status = isci_sata_management_task_request_build(ireq); - - if (status != SCI_SUCCESS) - return NULL; - } return ireq; } @@ -871,53 +824,20 @@ static int isci_task_send_lu_reset_sas( return ret; } -static int isci_task_send_lu_reset_sata(struct isci_host *ihost, - struct isci_remote_device *idev, u8 *lun) +int isci_task_lu_reset(struct domain_device *dev, u8 *lun) { - int ret = TMF_RESP_FUNC_FAILED; - struct isci_tmf tmf; - - /* Send the soft reset to the target */ - #define ISCI_SRST_TIMEOUT_MS 25000 /* 25 second timeout. */ - isci_task_build_tmf(&tmf, isci_tmf_sata_srst_high, NULL, NULL); - - ret = isci_task_execute_tmf(ihost, idev, &tmf, ISCI_SRST_TIMEOUT_MS); - - if (ret != TMF_RESP_FUNC_COMPLETE) { - dev_dbg(&ihost->pdev->dev, - "%s: Assert SRST failed (%p) = %x", - __func__, idev, ret); - - /* Return the failure so that the LUN reset is escalated - * to a target reset. - */ - } - return ret; -} - -/** - * isci_task_lu_reset() - This function is one of the SAS Domain Template - * functions. This is one of the Task Management functoins called by libsas, - * to reset the given lun. Note the assumption that while this call is - * executing, no I/O will be sent by the host to the device. - * @lun: This parameter specifies the lun to be reset. - * - * status, zero indicates success. - */ -int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) -{ - struct isci_host *isci_host = dev_to_ihost(domain_device); + struct isci_host *isci_host = dev_to_ihost(dev); struct isci_remote_device *isci_device; unsigned long flags; int ret; spin_lock_irqsave(&isci_host->scic_lock, flags); - isci_device = isci_lookup_device(domain_device); + isci_device = isci_lookup_device(dev); spin_unlock_irqrestore(&isci_host->scic_lock, flags); dev_dbg(&isci_host->pdev->dev, "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", - __func__, domain_device, isci_host, isci_device); + __func__, dev, isci_host, isci_device); if (!isci_device) { /* If the device is gone, stop the escalations. */ @@ -929,8 +849,9 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun) set_bit(IDEV_EH, &isci_device->flags); /* Send the task management part of the reset. */ - if (sas_protocol_ata(domain_device->tproto)) { - ret = isci_task_send_lu_reset_sata(isci_host, isci_device, lun); + if (dev_is_sata(dev)) { + sas_ata_schedule_reset(dev); + ret = TMF_RESP_FUNC_COMPLETE; } else ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun); diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index bb472c33952..5ba00c3081f 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -86,8 +86,6 @@ enum isci_tmf_function_codes { isci_tmf_func_none = 0, isci_tmf_ssp_task_abort = TMF_ABORT_TASK, isci_tmf_ssp_lun_reset = TMF_LU_RESET, - isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */ - isci_tmf_sata_srst_low = TMF_LU_RESET + 0x101 /* Non SCSI */ }; /** * struct isci_tmf - This class represents the task management object which diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 0cb538f8478..37a9e73870d 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "sas_internal.h" @@ -757,6 +758,7 @@ void sas_ata_schedule_reset(struct domain_device *dev) 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) { -- cgit v1.2.3-70-g09d2 From 8abda4d28a55ecb91e39ceb5e3ee264c5a3cd1af Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 10 Jan 2012 15:14:09 -0800 Subject: [SCSI] libsas: close scsi_remove_target() vs libata-eh race ata_port lifetime in libata follows the host. In libsas it follows the scsi_target. Once scsi_remove_device() has caused all commands to be completed it allows scsi_remove_target() to immediately proceed to freeing the ata_port causing bug reports like: [ 848.393333] BUG: spinlock bad magic on CPU#4, kworker/u:2/5107 [ 848.400262] general protection fault: 0000 [#1] SMP [ 848.406244] CPU 4 [ 848.408310] Modules linked in: nls_utf8 ipv6 uinput i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support ioatdma dca sg sd_mod sr_mod cdrom ahci libahci isci libsas libata scsi_transport_sas [last unloaded: scsi_wait_scan] [ 848.432060] [ 848.434137] Pid: 5107, comm: kworker/u:2 Not tainted 3.2.0-isci+ #8 Intel Corporation S2600CP/S2600CP [ 848.445310] RIP: 0010:[] [] spin_dump+0x5e/0x8c [ 848.454787] RSP: 0018:ffff8807f868dca0 EFLAGS: 00010002 [ 848.461137] RAX: 0000000000000048 RBX: ffff8807fe86a630 RCX: ffffffff817d0be0 [ 848.469520] RDX: 0000000000000000 RSI: ffffffff814af1cf RDI: 0000000000000002 [ 848.477959] RBP: ffff8807f868dcb0 R08: 00000000ffffffff R09: 000000006b6b6b6b [ 848.486327] R10: 000000000003fb8c R11: ffffffff81a19448 R12: 6b6b6b6b6b6b6b6b [ 848.494699] R13: ffff8808027dc520 R14: 0000000000000000 R15: 000000000000001e [ 848.503067] FS: 0000000000000000(0000) GS:ffff88083fd00000(0000) knlGS:0000000000000000 [ 848.512899] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 848.519710] CR2: 00007ff77d001000 CR3: 00000007f7a5d000 CR4: 00000000000406e0 [ 848.528072] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 848.536446] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 848.544831] Process kworker/u:2 (pid: 5107, threadinfo ffff8807f868c000, task ffff8807ff348000) [ 848.555327] Stack: [ 848.557959] ffff8807fe86a630 ffff8807fe86a630 ffff8807f868dcd0 ffffffff8126a6e0 [ 848.567072] ffffffff817c142f ffff8807fe86a630 ffff8807f868dcf0 ffffffff8126a703 [ 848.576190] ffff8808027dc520 0000000000000286 ffff8807f868dd10 ffffffff814af1bb [ 848.585281] Call Trace: [ 848.588409] [] spin_bug+0x26/0x28 [ 848.594357] [] do_raw_spin_unlock+0x21/0x88 [ 848.601283] [] _raw_spin_unlock_irqrestore+0x2c/0x65 [ 848.609089] [] ata_scsi_port_error_handler+0x548/0x557 [libata] [ 848.618331] [] ? async_schedule+0x17/0x17 [ 848.625060] [] async_sas_ata_eh+0x45/0x69 [libsas] [ 848.632655] [] async_run_entry_fn+0x97/0x125 [ 848.639670] [] process_one_work+0x207/0x38d [ 848.646577] [] ? process_one_work+0x15a/0x38d [ 848.653681] [] worker_thread+0x138/0x21c [ 848.660305] [] ? process_one_work+0x38d/0x38d [ 848.667493] [] kthread+0x9d/0xa5 [ 848.673382] [] ? trace_hardirqs_on_caller+0x12f/0x166 [ 848.681304] [] kernel_thread_helper+0x4/0x10 [ 848.688324] [] ? retint_restore_args+0x13/0x13 [ 848.695530] [] ? __init_kthread_worker+0x5b/0x5b [ 848.702929] [] ? gs_change+0x13/0x13 [ 848.709155] Code: 00 00 48 8d 88 38 04 00 00 44 8b 80 84 02 00 00 31 c0 e8 cf 1b 24 00 41 83 c8 ff 44 8b 4b 08 48 c7 c1 e0 0b 7d 81 4d 85 e4 74 10 <45> 8b 84 24 84 02 00 00 49 8d 8c 24 38 04 00 00 8b 53 04 48 89 [ 848.732467] RIP [] spin_dump+0x5e/0x8c [ 848.738905] RSP [ 848.743743] ---[ end trace 143161646eee8caa ]--- ...so arrange for the ata_port to have the same end of life as the domain device. Reported-by: Marcin Tomczak Acked-by: Jeff Garzik Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 5 +++++ drivers/scsi/libsas/sas_discover.c | 5 +++++ drivers/scsi/libsas/sas_scsi_host.c | 3 --- 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 37a9e73870d..26a943eb153 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -661,8 +661,13 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) struct ata_port *ap = dev->sata_dev.ap; struct sas_ha_struct *ha = dev->port->ha; + /* hold a reference over eh since we may be racing with final + * remove once all commands are completed + */ + kref_get(&dev->kref); ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); ata_scsi_port_error_handler(ha->core.shost, ap); + sas_put_device(dev); } void sas_ata_strategy_handler(struct Scsi_Host *shost) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index b91866a8233..4be5ddad7be 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -242,6 +242,11 @@ void sas_free_device(struct kref *kref) 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); } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 731c8925063..b563ff27626 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -1028,9 +1028,6 @@ void sas_target_destroy(struct scsi_target *starget) if (!found_dev) return; - if (dev_is_sata(found_dev)) - ata_sas_port_destroy(found_dev->sata_dev.ap); - starget->hostdata = NULL; sas_put_device(found_dev); } -- cgit v1.2.3-70-g09d2 From d230ce691c7712c4f56ba3378d6d2f44628a49f1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 11 Jan 2012 12:08:36 -0800 Subject: [SCSI] libsas: fix mixed topology recovery If we have a domain with sas and sata devices there may still be sas recovery actions to take after peeling off the commands to send to libata. Reported-by: Andrzej Jakowski Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 8 ++------ drivers/scsi/libsas/sas_scsi_host.c | 13 +++++++------ include/scsi/sas_ata.h | 9 ++++----- 3 files changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 26a943eb153..40edf520d69 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -699,10 +699,9 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) 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; @@ -719,7 +718,6 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, if (ap && ap != ddev->sata_dev.ap) continue; ap = ddev->sata_dev.ap; - rtn = 1; list_move(&cmd->eh_entry, &sata_q); } @@ -741,8 +739,6 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, list_del_init(sata_q.next); } } while (ap); - - return rtn; } void sas_ata_schedule_reset(struct domain_device *dev) diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index b563ff27626..e58ca50517d 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -678,7 +678,8 @@ 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) @@ -693,9 +694,9 @@ 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: clear_bit(SAS_HA_FROZEN, &ha->state); @@ -707,8 +708,8 @@ out: 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) diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index da3f3772738..cb724fd010f 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -41,8 +41,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); -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); void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); @@ -66,10 +66,9 @@ static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) { } -static inline int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q) +static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, + struct list_head *done_q) { - return 0; } static inline void sas_probe_sata(struct work_struct *work) -- cgit v1.2.3-70-g09d2 From d214d81e883b6fc6f11cc772ff88585565d45cce Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 16 Jan 2012 11:56:50 -0800 Subject: [SCSI] libsas: improve debug statements It's difficult to determine which domain_device is triggering error recovery, so convert messages like: sas: ex 5001b4da000e703f phy08:T attached: 5001b4da000e7028 sas: ex 5001b4da000e703f phy09:T attached: 5001b4da000e7029 ... ata7: sas eh calling libata port error handler ata8: sas eh calling libata port error handler ...into: sas: ex 5001517e85cfefff phy05:T:9 attached: 5001517e85cfefe5 (stp) sas: ex 5001517e3b0af0bf phy11:T:8 attached: 5001517e3b0af0ab (stp) ... sas: ata7: end_device-21:1: dev error handler sas: ata8: end_device-20:0:5: dev error handler which shows attached link rate, device type, and associates a domain_device with its ata_port id to correlate messages emitted from libata-eh. As Doug notes, we can also take the opportunity to clarify expander phy routing capabilities. [dgilbert@interlog.com: clarify table2table with 'U'] Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 43 ++++++++++++++++------ drivers/scsi/libsas/sas_expander.c | 74 ++++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 32 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 40edf520d69..ba1ebfe991d 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -317,6 +317,28 @@ static int local_ata_check_ready(struct ata_link *link) } } +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_hard_reset(struct ata_link *link, unsigned int *class, unsigned long deadline) { @@ -333,7 +355,7 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, res = i->dft->lldd_I_T_nexus_reset(dev); if (res != TMF_RESP_FUNC_COMPLETE) - SAS_DPRINTK("%s: Unable to reset ata device?\n", __func__); + 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)) @@ -344,7 +366,7 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, ret = ata_wait_after_reset(link, deadline, check_ready); if (ret && ret != -EAGAIN) - ata_link_err(link, "COMRESET failed (errno=%d)\n", ret); + 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 @@ -665,7 +687,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) * remove once all commands are completed */ kref_get(&dev->kref); - ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler"); + sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); ata_scsi_port_error_handler(ha->core.shost, ap); sas_put_device(dev); } @@ -703,26 +725,27 @@ void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) { 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; + 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 @@ -738,7 +761,7 @@ void 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) diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 68a80a00f73..4b2ecd35dc5 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -166,7 +166,22 @@ 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 void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *disc_resp) @@ -176,9 +191,10 @@ static void sas_set_ex_phy(struct domain_device *dev, int 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 */ @@ -223,20 +239,41 @@ 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", - 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)); + switch (phy->attached_dev_type) { + 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"; + } - return; + SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + SAS_ADDR(dev->sas_addr), phy->phy_id, + 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 */ @@ -1176,32 +1213,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, -- cgit v1.2.3-70-g09d2 From 354cf82980e2449e71fdaa3c6f170357ebd65467 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 12 Jan 2012 17:57:35 -0800 Subject: [SCSI] libsas: let libata recover links that fail to transmit initial sig-fis libsas fails to discover all sata devices in the domain. If a device fails negotiation and does not transmit a signature fis the link needs recovery. libata already understands how to manage slow to come up links, so treat these conditions as ata device attach events for the purposes of creating an ata_port. This allows libata to manage retrying link bring up. Rediscovery is modified to be careful about checking changes in dev_type. It looks like libsas leaks old devices if the sas address changes, but that's a fix for another patch. Acked-by: Jack Wang Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 71 +++++++++++++-- drivers/scsi/libsas/sas_discover.c | 1 + drivers/scsi/libsas/sas_expander.c | 178 +++++++++++++++++++++---------------- drivers/scsi/libsas/sas_internal.h | 6 +- include/scsi/sas.h | 4 +- include/scsi/sas_ata.h | 8 +- 6 files changed, 179 insertions(+), 89 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index ba1ebfe991d..25008a42412 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -278,26 +278,84 @@ 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; - u8 addr[8]; struct ata_port *ap = link->ap; struct domain_device *dev = ap->private_data; 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]; - res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr); + res = sas_ex_phy_discover(ex_dev, phy->number); sas_put_local_phy(phy); + /* break the wait early if the expander is unreachable, * otherwise keep polling */ if (res == -ECOMM) return res; - if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0) + if (res != SMP_RESP_FUNC_ACC) return 0; - else - return 1; + + 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; + } } static int local_ata_check_ready(struct ata_link *link) @@ -584,6 +642,9 @@ 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 && diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c1ac99d25f5..8bcfcaa7b2e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -48,6 +48,7 @@ void sas_init_dev(struct domain_device *dev) case SATA_DEV: case SATA_PM: case SATA_PM_PORT: + case SATA_PENDING: INIT_LIST_HEAD(&dev->sata_dev.children); break; default: diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 4b2ecd35dc5..7e2d3c4c617 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -183,13 +183,27 @@ static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) } } -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, - void *disc_resp) +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 *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; bool new_phy = !phy->phy; char *type; @@ -213,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; @@ -229,7 +248,7 @@ 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; phy->phy->identify.phy_identifier = phy_id; @@ -246,6 +265,9 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, } switch (phy->attached_dev_type) { + case SATA_PENDING: + type = "stp pending"; + break; case NO_DEVICE: type = "no device"; break; @@ -270,6 +292,16 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, 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, sas_route_char(dev, phy), phy->linkrate, @@ -304,50 +336,25 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, u8 *disc_resp, int single) { - struct domain_device *ata_dev = sas_ex_to_ata(dev, 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; - 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; - } - - /* 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 == 0 && - dr->attached_sata_dev)) - break; - - /* In order to generate the dev to host FIS, we send a - * link reset to the expander port. If a device was - * previously detected on this port we ask libata to - * manage the reset and link recovery. - */ - if (ata_dev) { - sas_ata_schedule_reset(ata_dev); - break; - } - 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; @@ -652,9 +659,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); @@ -764,21 +770,9 @@ static struct domain_device *sas_ex_discover_end_dev( #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)) @@ -993,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), @@ -1019,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: @@ -1688,8 +1684,8 @@ static int sas_get_phy_change_count(struct domain_device *dev, return res; } -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; @@ -1701,10 +1697,11 @@ int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, 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; @@ -1953,39 +1950,62 @@ 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; + } + + return sas_discover_new(dev, phy_id); } /** diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 7818c4673c3..e028d7a4420 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -91,8 +91,9 @@ 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_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id, - u8 *attached_sas_addr); +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); @@ -122,6 +123,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: diff --git a/include/scsi/sas.h b/include/scsi/sas.h index 3673d685e6a..a577a833603 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -89,8 +89,7 @@ enum sas_oob_mode { SAS_OOB_MODE }; -/* See sas_discover.c if you plan on changing these. - */ +/* See sas_discover.c if you plan on changing these */ enum sas_dev_type { NO_DEVICE = 0, /* protocol */ SAS_END_DEV = 1, /* protocol */ @@ -100,6 +99,7 @@ enum sas_dev_type { SATA_DEV = 5, SATA_PM = 7, SATA_PM_PORT= 8, + SATA_PENDING = 9, }; enum sas_protocol { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index cb724fd010f..0ca2f8a6bc6 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -33,9 +33,10 @@ static inline int dev_is_sata(struct domain_device *dev) { return dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM || - dev->dev_type == SATA_PM_PORT; + dev->dev_type == SATA_PM_PORT || dev->dev_type == SATA_PENDING; } +int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); int sas_ata_init_host_and_port(struct domain_device *found_dev, struct scsi_target *starget); @@ -82,6 +83,11 @@ static inline void sas_ata_schedule_reset(struct domain_device *dev) static inline void sas_ata_wait_eh(struct domain_device *dev) { } + +static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) +{ + return 0; +} #endif #endif /* _SAS_ATA_H_ */ -- cgit v1.2.3-70-g09d2 From 92625f9bff3853951cc75f5bc084ee67c1317d2f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 18 Jan 2012 20:14:01 -0800 Subject: [SCSI] libsas: restore scan order ata devices are always scanned after ssp. Prior to the ata error handling reworks libsas would tend to scan devices in ascending expander phy order. Restore this ordering by deferring ssp discovery to a DISCE_PROBE event, and keep the probe order consistent with the discovery order, not the placement of sata devices. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 29 ------------------------- drivers/scsi/libsas/sas_discover.c | 44 +++++++++++++++++++++++++++----------- drivers/scsi/libsas/sas_expander.c | 4 +--- include/scsi/sas_ata.h | 5 ----- 4 files changed, 32 insertions(+), 50 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 25008a42412..a9ec1643ee9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -683,35 +683,6 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } -void sas_probe_sata(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); - - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { - int err; - - spin_lock_irq(&port->dev_list_lock); - list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock_irq(&port->dev_list_lock); - - err = sas_rphy_add(dev->rphy); - - if (err) { - 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(port, dev); - } else - list_del_init(&dev->disco_list_node); - } -} - /** * sas_discover_sata -- discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 8bcfcaa7b2e..18fa364aa00 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -152,7 +152,7 @@ static int sas_get_port_device(struct asd_sas_port *port) dev->rphy = rphy; - if (dev_is_sata(dev)) + 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); @@ -198,8 +198,34 @@ void sas_notify_lldd_dev_gone(struct domain_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); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + int err; + + spin_lock_irq(&port->dev_list_lock); + list_add_tail(&dev->dev_list_node, &port->dev_list); + spin_unlock_irq(&port->dev_list_lock); + + err = sas_rphy_add(dev->rphy); + + if (err) { + 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(port, dev); + } else + list_del_init(&dev->disco_list_node); + } +} /** * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -213,18 +239,10 @@ 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 ---------- */ @@ -491,7 +509,7 @@ 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_sata, + [DISCE_PROBE] = sas_probe_devices, [DISCE_DESTRUCT] = sas_destruct_devices, }; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index d63f0fbcd10..14e3244c1b2 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -806,9 +806,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) { diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 0ca2f8a6bc6..1556eff4cc4 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -44,7 +44,6 @@ void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); -void sas_probe_sata(struct work_struct *work); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); #else @@ -72,10 +71,6 @@ static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, { } -static inline void sas_probe_sata(struct work_struct *work) -{ -} - static inline void sas_ata_schedule_reset(struct domain_device *dev) { } -- cgit v1.2.3-70-g09d2 From 9508a66f898d46e726a318469312b45e0b1d078b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 18 Jan 2012 20:47:01 -0800 Subject: [SCSI] libsas: async ata scanning libsas ata error handling is already async but this does not help the scan case. Move initial link recovery out from under host->scan_mutex, and delay synchronization with eh until after all port probe/recovery work has been queued. Device ordering is maintained with scan order by still calling sas_rphy_add() in order of domain discovery. Since we now scan the domain list when invoking libata-eh we need to be careful to check for fully initialized ata ports. Acked-by: Jack Wang Acked-by: Jeff Garzik Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/ata/libata-core.c | 34 +++++++++-------- drivers/ata/libata-scsi.c | 13 +++++++ drivers/ata/libata.h | 1 + drivers/scsi/aic94xx/aic94xx_init.c | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/libsas/sas_ata.c | 74 +++++++++++++++++++++++++++++++------ drivers/scsi/libsas/sas_discover.c | 22 +++++------ drivers/scsi/libsas/sas_internal.h | 9 +++++ drivers/scsi/libsas/sas_scsi_host.c | 18 --------- drivers/scsi/mvsas/mv_init.c | 1 - drivers/scsi/pm8001/pm8001_init.c | 1 - include/linux/libata.h | 1 + include/scsi/libsas.h | 1 - include/scsi/sas_ata.h | 12 +++--- 14 files changed, 123 insertions(+), 66 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c06e0ec1155..e0bda9ff89c 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5936,29 +5936,31 @@ void ata_host_init(struct ata_host *host, struct device *dev, host->ops = ops; } -int ata_port_probe(struct ata_port *ap) +void __ata_port_probe(struct ata_port *ap) { - int rc = 0; + struct ata_eh_info *ehi = &ap->link.eh_info; + unsigned long flags; - /* probe */ - if (ap->ops->error_handler) { - struct ata_eh_info *ehi = &ap->link.eh_info; - unsigned long flags; + /* kick EH for boot probing */ + spin_lock_irqsave(ap->lock, flags); - /* kick EH for boot probing */ - spin_lock_irqsave(ap->lock, flags); + ehi->probe_mask |= ATA_ALL_DEVICES; + ehi->action |= ATA_EH_RESET; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; - ehi->probe_mask |= ATA_ALL_DEVICES; - ehi->action |= ATA_EH_RESET; - ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + ap->pflags &= ~ATA_PFLAG_INITIALIZING; + ap->pflags |= ATA_PFLAG_LOADING; + ata_port_schedule_eh(ap); - ap->pflags &= ~ATA_PFLAG_INITIALIZING; - ap->pflags |= ATA_PFLAG_LOADING; - ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); +} - spin_unlock_irqrestore(ap->lock, flags); +int ata_port_probe(struct ata_port *ap) +{ + int rc = 0; - /* wait for EH to finish */ + if (ap->ops->error_handler) { + __ata_port_probe(ap); ata_port_wait_eh(ap); } else { DPRINTK("ata%u: bus probe begin\n", ap->print_id); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 508a60bfe5c..1ee00c8b5b0 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3838,6 +3838,19 @@ void ata_sas_port_stop(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_stop); +int ata_sas_async_port_init(struct ata_port *ap) +{ + int rc = ap->ops->port_start(ap); + + if (!rc) { + ap->print_id = ata_print_id++; + __ata_port_probe(ap); + } + + return rc; +} +EXPORT_SYMBOL_GPL(ata_sas_async_port_init); + /** * ata_sas_port_init - Initialize a SATA device * @ap: SATA port to initialize diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 1fab235ee51..2e26fcaf635 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -105,6 +105,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern const char *sata_spd_string(unsigned int spd); extern int ata_port_probe(struct ata_port *ap); +extern void __ata_port_probe(struct ata_port *ap); /* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index eea988a04f9..ff80552ead8 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -81,7 +81,6 @@ static struct scsi_host_template aic94xx_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, }; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index c3fe39bcacd..c9af456e7df 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -165,7 +165,6 @@ static struct scsi_host_template isci_sht = { .sg_tablesize = SG_ALL, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = isci_host_attrs, diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index a9ec1643ee9..eb8b77c8616 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -585,11 +585,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, @@ -607,6 +606,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; @@ -683,6 +684,38 @@ static void sas_get_ata_command_set(struct domain_device *dev) dev->sata_dev.command_set = ATAPI_COMMAND_SET; } +void sas_probe_sata(struct asd_sas_port *port) +{ + struct domain_device *dev, *n; + int err; + + mutex_lock(&port->ha->disco_mutex); + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + err = sas_ata_init_host_and_port(dev); + if (err) + sas_fail_probe(dev, __func__, err); + else + ata_sas_async_port_init(dev->sata_dev.ap); + } + mutex_unlock(&port->ha->disco_mutex); + + list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + if (!dev_is_sata(dev)) + continue; + + sas_ata_wait_eh(dev); + + /* 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 @@ -724,11 +757,23 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) sas_put_device(dev); } +static bool sas_ata_dev_eh_valid(struct domain_device *dev) +{ + struct ata_port *ap; + + if (!dev_is_sata(dev)) + return false; + ap = dev->sata_dev.ap; + /* consume fully initialized ata ports */ + smp_rmb(); + return !!ap; +} + void sas_ata_strategy_handler(struct Scsi_Host *shost) { - struct scsi_device *sdev; 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: @@ -740,14 +785,21 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) */ sas_disable_revalidation(sas_ha); - shost_for_each_device(sdev, shost) { - struct domain_device *ddev = sdev_to_domain_dev(sdev); - - if (!dev_is_sata(ddev)) - continue; + 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; - async_schedule_domain(async_sas_ata_eh, ddev, &async); + 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); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 18fa364aa00..0d58a8beaa3 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -207,22 +207,22 @@ static void sas_probe_devices(struct work_struct *work) clear_bit(DISCE_PROBE, &port->disc.pending); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { - int err; - + /* 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); + } - err = sas_rphy_add(dev->rphy); + sas_probe_sata(port); - if (err) { - 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(port, dev); - } else + 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); } } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index e028d7a4420..d0d9bf10f79 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -113,6 +113,15 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost, } #endif +static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) +{ + 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, struct sas_rphy *rphy) { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index e58ca50517d..3701ff7e726 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -762,17 +762,10 @@ int sas_target_alloc(struct scsi_target *starget) { struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); struct domain_device *found_dev = sas_find_dev_by_rphy(rphy); - int res; 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; @@ -1012,16 +1005,6 @@ 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 = starget->hostdata; @@ -1082,6 +1065,5 @@ EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); 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); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index d45878b3125..cc59dff3810 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -73,7 +73,6 @@ static struct scsi_host_template mvs_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = mvst_host_attrs, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index bd165ea6191..36efaa7c3a5 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -75,7 +75,6 @@ static struct scsi_host_template pm8001_sht = { .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, - .slave_alloc = sas_slave_alloc, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, .shost_attrs = pm8001_host_attrs, diff --git a/include/linux/libata.h b/include/linux/libata.h index aa427047756..42378d637ff 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,6 +996,7 @@ extern int ata_sas_scsi_ioctl(struct ata_port *ap, struct scsi_device *dev, extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); +extern int ata_sas_async_port_init(struct ata_port *); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 4a42be34fad..20153d58e4e 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -646,7 +646,6 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset); int sas_queue_up(struct sas_task *task); extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *); extern int sas_target_alloc(struct scsi_target *); -extern int sas_slave_alloc(struct scsi_device *); extern int sas_slave_configure(struct scsi_device *); extern int sas_change_queue_depth(struct scsi_device *, int new_depth, int reason); diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h index 1556eff4cc4..cdccd2eb7b6 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,15 +37,14 @@ static inline int dev_is_sata(struct domain_device *dev) } int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy); -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); void sas_ata_task_abort(struct sas_task *task); void sas_ata_strategy_handler(struct Scsi_Host *shost); void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q); void sas_ata_schedule_reset(struct domain_device *dev); void sas_ata_wait_eh(struct domain_device *dev); +void sas_probe_sata(struct asd_sas_port *port); #else @@ -53,8 +52,7 @@ static inline int dev_is_sata(struct domain_device *dev) { return 0; } -static inline int sas_ata_init_host_and_port(struct domain_device *found_dev, - struct scsi_target *starget) +static inline int sas_ata_init_host_and_port(struct domain_device *found_dev) { return 0; } @@ -79,6 +77,10 @@ static inline void sas_ata_wait_eh(struct domain_device *dev) { } +static inline void sas_probe_sata(struct asd_sas_port *port) +{ +} + static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) { return 0; -- cgit v1.2.3-70-g09d2 From 9a10b33caf78f897356ac006c455e6060a40af15 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Jan 2012 15:26:03 -0800 Subject: [SCSI] libsas: revert ata srst libata issues follow up srsts when the controller has a hard time recording the signature-fis after a reset, or if the link supports port multipliers. libsas does not support port multipliers and no current libsas lldds appear to need help retrieving the signature fis. Revert it for now to remove confusion. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 38 -------------------------------------- include/scsi/libsas.h | 1 - 2 files changed, 39 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index eb8b77c8616..08d2103a45b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -443,43 +443,6 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, return ret; } -static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - struct ata_port *ap = link->ap; - struct domain_device *dev = ap->private_data; - struct sas_internal *i = dev_to_sas_internal(dev); - int res = TMF_RESP_FUNC_FAILED; - int ret = 0; - - if (i->dft->lldd_ata_soft_reset) - res = i->dft->lldd_ata_soft_reset(dev); - - if (res != TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: Unable to soft reset\n", __func__); - ret = -EAGAIN; - } - - 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 @@ -563,7 +526,6 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_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, diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 20153d58e4e..5f5ed1b8b41 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -619,7 +619,6 @@ struct sas_domain_function_template { int (*lldd_clear_aca)(struct domain_device *, u8 *lun); int (*lldd_clear_task_set)(struct domain_device *, u8 *lun); int (*lldd_I_T_nexus_reset)(struct domain_device *); - int (*lldd_ata_soft_reset)(struct domain_device *); int (*lldd_ata_check_ready)(struct domain_device *); void (*lldd_ata_set_dmamode)(struct domain_device *); int (*lldd_lu_reset)(struct domain_device *, u8 *lun); -- cgit v1.2.3-70-g09d2 From 26a2e68f816ebd736a0484ca293457b280af4ef1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 30 Jan 2012 21:40:45 -0800 Subject: [SCSI] libsas: don't recover end devices attached to disabled phys If userspace has decided to disable a phy the kernel should honor that and not inadvertantly re-enable the phy via error recovery. This is more straightforward in the sata case where link recovery (via libata-eh) is separate from sas_task cancelling in libsas-eh. Teach libsas to accept -ENODEV as a successful response from I_T_nexus_reset ('successful' in terms of not escalating further). This is a more comprehensive fix then "libsas: don't recover 'gone' devices in sas_ata_hard_reset()", as it is no longer sata-specific. aic94xx does check the return value from sas_phy_reset() so if the phy is disabled we proceed with clearing the I_T_nexus. Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_tmf.c | 2 +- drivers/scsi/libsas/sas_ata.c | 5 ++--- drivers/scsi/libsas/sas_init.c | 3 +++ drivers/scsi/libsas/sas_scsi_host.c | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers/scsi/libsas/sas_ata.c') diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index 50b914ffab9..cf9040933da 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c @@ -192,7 +192,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev) ASD_DPRINTK("sending %s reset to %s\n", reset_type ? "hard" : "soft", dev_name(&phy->dev)); res = sas_phy_reset(phy, reset_type); - if (res == TMF_RESP_FUNC_COMPLETE) { + if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) { /* wait for the maximum settle time */ msleep(500); /* clear all outstanding commands (keep nexus suspended) */ diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 08d2103a45b..bc0cecc6ad6 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -407,10 +407,9 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, struct domain_device *dev = ap->private_data; struct sas_internal *i = dev_to_sas_internal(dev); - if (test_bit(SAS_DEV_GONE, &dev->state)) - return -ENODEV; - res = i->dft->lldd_I_T_nexus_reset(dev); + if (res == -ENODEV) + return res; if (res != TMF_RESP_FUNC_COMPLETE) sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n"); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 09c14ca3fbd..120bff64be3 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -298,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 diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index fd3291337c1..f0b9b7bf188 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -607,7 +607,8 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * 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)); -- cgit v1.2.3-70-g09d2