From 6f381fa344911d5a234b13574433cf23036f9467 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 12 Apr 2012 13:50:38 +0800 Subject: [SCSI] scsi_lib: use correct DMA device in __scsi_alloc_queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, __scsi_alloc_queue uses SCSI host's parent device as DMA device to set segment boundary. But the parent device may not refer to the DMA device. For example, for ATA disk, SCSI host's parent device now refers to ATA port. Since commit d139b9b([SCSI] scsi_lib_dma: fix bug with dma maps on nested scsi objects), a new field Scsi_Host->dma_dev was introduced to refer to the real DMA device. Use ->dma_dev in __scsi_alloc_queue to correctly set segment boundary. Bug report: http://marc.info/?l=linux-ide&m=133177818318187&w=2 Reported-and-tested-by: Jörg Sommer Signed-off-by: Lin Ming Signed-off-by: James Bottomley --- drivers/scsi/scsi_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ead6405f3e5..5dfd7495d1a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1638,7 +1638,7 @@ struct request_queue *__scsi_alloc_queue(struct Scsi_Host *shost, request_fn_proc *request_fn) { struct request_queue *q; - struct device *dev = shost->shost_gendev.parent; + struct device *dev = shost->dma_dev; q = blk_init_queue(request_fn, NULL); if (!q) -- cgit v1.2.3-70-g09d2 From 22b9153faa2263aa89625de25e71c7d44c8dbd16 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 9 Mar 2012 11:00:06 -0800 Subject: [SCSI] libsas: introduce sas_work to fix sas_drain_work vs sas_queue_work When requeuing work to a draining workqueue the last work instance may not be idle, so sas_queue_work() must not touch work->entry. Introduce sas_work with a drain_node list_head to have a private list for collecting work deferred due to drain collision. Fixes reports like: BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] process_one_work+0x2e/0x338 Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 28 +++++++++++++------------- drivers/scsi/libsas/sas_event.c | 24 ++++++++++++----------- drivers/scsi/libsas/sas_init.c | 11 +++++------ drivers/scsi/libsas/sas_internal.h | 6 +++--- drivers/scsi/libsas/sas_phy.c | 21 +++++++------------- drivers/scsi/libsas/sas_port.c | 15 +++++--------- include/scsi/libsas.h | 40 ++++++++++++++++++++++++++++++++++---- 7 files changed, 83 insertions(+), 62 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 36467967560..c7ac88288bf 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -205,8 +205,7 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) static void sas_probe_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_PROBE, &port->disc.pending); @@ -291,8 +290,7 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d static void sas_destruct_devices(struct work_struct *work) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DESTRUCT, &port->disc.pending); @@ -377,8 +375,7 @@ static void sas_discover_domain(struct work_struct *work) { struct domain_device *dev; int error = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); @@ -437,8 +434,7 @@ static void sas_discover_domain(struct work_struct *work) static void sas_revalidate_domain(struct work_struct *work) { int res = 0; - struct sas_discovery_event *ev = - container_of(work, struct sas_discovery_event, work); + struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; struct sas_ha_struct *ha = port->ha; @@ -466,21 +462,25 @@ static void sas_revalidate_domain(struct work_struct *work) /* ---------- Events ---------- */ -static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work) +static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) { - /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */ - scsi_queue_work(ha->core.shost, work); + /* chained work is not subject to SA_HA_DRAINING or + * SAS_HA_REGISTERED, because it is either submitted in the + * workqueue, or known to be submitted from a context that is + * not racing against draining + */ + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_chain_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *sw, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { unsigned long flags; spin_lock_irqsave(&ha->state_lock, flags); - sas_chain_work(ha, work); + sas_chain_work(ha, sw); spin_unlock_irqrestore(&ha->state_lock, flags); } } @@ -519,7 +519,7 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) disc->pending = 0; for (i = 0; i < DISC_NUM_EVENTS; i++) { - INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]); + INIT_SAS_WORK(&disc->disc_work[i].work, sas_event_fns[i]); disc->disc_work[i].port = port; } } diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 16639bbae62..4e4292d210c 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -27,19 +27,21 @@ #include "sas_internal.h" #include "sas_dump.h" -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { if (!test_bit(SAS_HA_REGISTERED, &ha->state)) return; - if (test_bit(SAS_HA_DRAINING, &ha->state)) - list_add(&work->entry, &ha->defer_q); - else - scsi_queue_work(ha->core.shost, work); + if (test_bit(SAS_HA_DRAINING, &ha->state)) { + /* add it to the defer list, if not already pending */ + if (list_empty(&sw->drain_node)) + list_add(&sw->drain_node, &ha->defer_q); + } else + scsi_queue_work(ha->core.shost, &sw->work); } static void sas_queue_event(int event, unsigned long *pending, - struct work_struct *work, + struct sas_work *work, struct sas_ha_struct *ha) { if (!test_and_set_bit(event, pending)) { @@ -55,7 +57,7 @@ static void sas_queue_event(int event, unsigned long *pending, void __sas_drain_work(struct sas_ha_struct *ha) { struct workqueue_struct *wq = ha->core.shost->work_q; - struct work_struct *w, *_w; + struct sas_work *sw, *_sw; set_bit(SAS_HA_DRAINING, &ha->state); /* flush submitters */ @@ -66,9 +68,9 @@ void __sas_drain_work(struct sas_ha_struct *ha) spin_lock_irq(&ha->state_lock); clear_bit(SAS_HA_DRAINING, &ha->state); - list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { - list_del_init(&w->entry); - sas_queue_work(ha, w); + list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { + list_del_init(&sw->drain_node); + sas_queue_work(ha, sw); } spin_unlock_irq(&ha->state_lock); } @@ -151,7 +153,7 @@ int sas_init_events(struct sas_ha_struct *sas_ha) int i; for (i = 0; i < HA_NUM_EVENTS; i++) { - INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); + INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); sas_ha->ha_events[i].ha = sas_ha; } diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 120bff64be3..10cb5ae3097 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -94,8 +94,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) void sas_hae_reset(struct work_struct *work) { - struct sas_ha_event *ev = - container_of(work, struct sas_ha_event, work); + struct sas_ha_event *ev = to_sas_ha_event(work); struct sas_ha_struct *ha = ev->ha; clear_bit(HAE_RESET, &ha->pending); @@ -369,14 +368,14 @@ static void sas_phy_release(struct sas_phy *phy) static void phy_reset_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); + struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work); d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); } static void phy_enable_work(struct work_struct *work) { - struct sas_phy_data *d = container_of(work, typeof(*d), enable_work); + struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work); d->enable_result = sas_phy_enable(d->phy, d->enable); } @@ -389,8 +388,8 @@ static int sas_phy_setup(struct sas_phy *phy) return -ENOMEM; mutex_init(&d->event_lock); - INIT_WORK(&d->reset_work, phy_reset_work); - INIT_WORK(&d->enable_work, phy_enable_work); + INIT_SAS_WORK(&d->reset_work, phy_reset_work); + INIT_SAS_WORK(&d->enable_work, phy_enable_work); d->phy = phy; phy->hostdata = d; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index f05c6387994..507e4cf12e5 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -45,10 +45,10 @@ struct sas_phy_data { struct mutex event_lock; int hard_reset; int reset_result; - struct work_struct reset_work; + struct sas_work reset_work; int enable; int enable_result; - struct work_struct enable_work; + struct sas_work enable_work; }; void sas_scsi_recover_host(struct Scsi_Host *shost); @@ -80,7 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work); -void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work); +void sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index dcfd4a9105c..521422e857a 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -32,8 +32,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); @@ -43,8 +42,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) static void sas_phye_oob_done(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); @@ -53,8 +51,7 @@ static void sas_phye_oob_done(struct work_struct *work) static void sas_phye_oob_error(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; @@ -85,8 +82,7 @@ static void sas_phye_oob_error(struct work_struct *work) static void sas_phye_spinup_hold(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = @@ -127,14 +123,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) phy->error = 0; INIT_LIST_HEAD(&phy->port_phy_el); for (k = 0; k < PORT_NUM_EVENTS; k++) { - INIT_WORK(&phy->port_events[k].work, - sas_port_event_fns[k]); + INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]); phy->port_events[k].phy = phy; } for (k = 0; k < PHY_NUM_EVENTS; k++) { - INIT_WORK(&phy->phy_events[k].work, - sas_phy_event_fns[k]); + INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); phy->phy_events[k].phy = phy; } @@ -144,8 +138,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) spin_lock_init(&phy->sas_prim_lock); phy->frame_rcvd_size = 0; - phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, - i); + phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); if (!phy->phy) return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index eb19c016d50..1cf7d75ad5e 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -208,8 +208,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) void sas_porte_bytes_dmaed(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); @@ -219,8 +218,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work) void sas_porte_broadcast_rcvd(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; unsigned long flags; u32 prim; @@ -237,8 +235,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) void sas_porte_link_reset_err(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); @@ -248,8 +245,7 @@ void sas_porte_link_reset_err(struct work_struct *work) void sas_porte_timer_event(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); @@ -259,8 +255,7 @@ void sas_porte_timer_event(struct work_struct *work) void sas_porte_hard_reset(struct work_struct *work) { - struct asd_sas_event *ev = - container_of(work, struct asd_sas_event, work); + struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 5f5ed1b8b41..f4f1c96dca7 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -217,11 +217,29 @@ struct domain_device { struct kref kref; }; -struct sas_discovery_event { +struct sas_work { + struct list_head drain_node; struct work_struct work; +}; + +static inline void INIT_SAS_WORK(struct sas_work *sw, void (*fn)(struct work_struct *)) +{ + INIT_WORK(&sw->work, fn); + INIT_LIST_HEAD(&sw->drain_node); +} + +struct sas_discovery_event { + struct sas_work work; struct asd_sas_port *port; }; +static inline struct sas_discovery_event *to_sas_discovery_event(struct work_struct *work) +{ + struct sas_discovery_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + struct sas_discovery { struct sas_discovery_event disc_work[DISC_NUM_EVENTS]; unsigned long pending; @@ -244,7 +262,7 @@ struct asd_sas_port { struct list_head destroy_list; enum sas_linkrate linkrate; - struct work_struct work; + struct sas_work work; /* public: */ int id; @@ -270,10 +288,17 @@ struct asd_sas_port { }; struct asd_sas_event { - struct work_struct work; + struct sas_work work; struct asd_sas_phy *phy; }; +static inline struct asd_sas_event *to_asd_sas_event(struct work_struct *work) +{ + struct asd_sas_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + /* The phy pretty much is controlled by the LLDD. * The class only reads those fields. */ @@ -333,10 +358,17 @@ struct scsi_core { }; struct sas_ha_event { - struct work_struct work; + struct sas_work work; struct sas_ha_struct *ha; }; +static inline struct sas_ha_event *to_sas_ha_event(struct work_struct *work) +{ + struct sas_ha_event *ev = container_of(work, typeof(*ev), work.work); + + return ev; +} + enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, -- cgit v1.2.3-70-g09d2 From 1699490db339e2c6b3037ea8e7dcd6b2755b688e Mon Sep 17 00:00:00 2001 From: Thomas Jackson Date: Fri, 17 Feb 2012 18:33:10 -0800 Subject: [SCSI] libsas: fix sas_find_bcast_phy() in the presence of 'vacant' phys If an expander reports 'PHY VACANT' for a phy index prior to the one that generated a BCN libsas fails rediscovery. Since a vacant phy is defined as a valid phy index that will never have an attached device just continue the search. Cc: Signed-off-by: Thomas Jackson Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 05acd9e35fc..833bea067a7 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1718,9 +1718,17 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, int phy_change_count = 0; res = sas_get_phy_change_count(dev, i, &phy_change_count); - if (res) - goto out; - else if (phy_change_count != ex->ex_phy[i].phy_change_count) { + switch (res) { + case SMP_RESP_PHY_VACANT: + case SMP_RESP_NO_PHY: + continue; + case SMP_RESP_FUNC_ACC: + break; + default: + return res; + } + + if (phy_change_count != ex->ex_phy[i].phy_change_count) { if (update) ex->ex_phy[i].phy_change_count = phy_change_count; @@ -1728,8 +1736,7 @@ static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, return 0; } } -out: - return res; + return 0; } static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) -- cgit v1.2.3-70-g09d2 From ec236e526777ea8825e6e0c3673a40389692eabf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 12 Mar 2012 11:38:26 -0700 Subject: [SCSI] libsas: fix sas_get_port_device regression Commit 899fcf4 "[SCSI] libsas: set attached device type and target protocols for local phys" setup 'phy' to be dereferenced after list_for_each_entry(phy, &port->phy_list, port_phy_el) (i.e. phy == &port->phy_list) resulting in reports like: BUG: unable to handle kernel NULL pointer dereference at 00000000000002b0 IP: [] sas_discover_domain+0x29e/0x4fb [libsas] ...fix by deferring sas_phy_set_target() to the end of sas_get_port_device(). Reported-by: Tom Jackson Tested-by: Tom Jackson Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index c7ac88288bf..658f16cc2f0 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -134,10 +134,6 @@ static int sas_get_port_device(struct asd_sas_port *port) return -ENODEV; } - spin_lock_irq(&port->phy_list_lock); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - sas_phy_set_target(phy, dev); - spin_unlock_irq(&port->phy_list_lock); rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE); sas_fill_in_rphy(dev, rphy); @@ -164,6 +160,11 @@ static int sas_get_port_device(struct asd_sas_port *port) spin_unlock_irq(&port->dev_list_lock); } + spin_lock_irq(&port->phy_list_lock); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + sas_phy_set_target(phy, dev); + spin_unlock_irq(&port->phy_list_lock); + return 0; } -- cgit v1.2.3-70-g09d2 From 9487669fc225092cf315e1291ece28e23e6754f3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Mar 2012 10:53:24 -0700 Subject: [SCSI] libsas: unify domain_device sas_rphy lifetimes Since the domain_device can out live the scsi_target we need the rphy to follow suit otherwise we run into issues like: BUG: unable to handle kernel NULL pointer dereference at 0000000000000050 IP: [] sas_ata_printk+0x43/0x6f [libsas] PGD 0 Oops: 0000 [#1] SMP CPU 1 Modules linked in: ses enclosure isci libsas scsi_transport_sas fuse sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf microcode pcspkr igb joydev iTCO_wdt ioatdma iTCO_vendor_support i2c_i801 i2c_core dca wmi hed ipv6 pata_acpi ata_generic [last unloaded: scsi_wait_scan] Pid: 129, comm: kworker/u:3 Not tainted 3.3.0-rc5-isci+ #1 Intel Corporation SandyBridge Platform/To be filled by O.E.M. RIP: 0010:[] [] sas_ata_printk+0x43/0x6f [libsas] RSP: 0018:ffff88042232dd70 EFLAGS: 00010282 RAX: 0000000000000000 RBX: ffff8804283165b8 RCX: ffff88042232dda0 RDX: ffff88042232dd78 RSI: ffff8804283165b8 RDI: ffffffffa01188d7 RBP: ffff88042232ddd0 R08: ffff880388454000 R09: ffff8803edfde1f8 R10: ffff8803edfde1f8 R11: ffff8803edfde1f8 R12: ffff880428316750 R13: ffff880388454000 R14: ffff8803f88b31d0 R15: ffff8803f8b21d50 FS: 0000000000000000(0000) GS:ffff88042ee20000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000050 CR3: 0000000001a05000 CR4: 00000000000406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process kworker/u:3 (pid: 129, threadinfo ffff88042232c000, task ffff88042230c920) Stack: 0000000000000000 ffff880400000018 ffff88042232dde0 ffff88042232dda0 ffffffffa01188c4 ffff88042ee93af0 ffff88042232ddb0 ffffffff8100e047 ffff88042232de10 ffff880420e5a2c8 ffff8803f8b21d50 ffff8803edfde1f8 Call Trace: [] ? load_TLS+0xb/0xf [] async_sas_ata_eh+0x66/0x95 [libsas] [] async_run_entry_fn+0x9e/0x131 Reported-by: Tom Jackson Tested-by: Tom Jackson Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 11 ++++++----- drivers/scsi/libsas/sas_expander.c | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 658f16cc2f0..13b5891f996 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -151,6 +151,7 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_device_set_phy(dev, port->port); dev->rphy = rphy; + get_device(&dev->rphy->dev); if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV) list_add_tail(&dev->disco_list_node, &port->disco_list); @@ -255,6 +256,9 @@ void sas_free_device(struct kref *kref) { struct domain_device *dev = container_of(kref, typeof(*dev), kref); + put_device(&dev->rphy->dev); + dev->rphy = NULL; + if (dev->parent) sas_put_device(dev->parent); @@ -301,7 +305,6 @@ static void sas_destruct_devices(struct work_struct *work) sas_remove_children(&dev->rphy->dev); sas_rphy_delete(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); } } @@ -313,11 +316,11 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) /* this rphy never saw sas_rphy_add */ list_del_init(&dev->disco_list_node); sas_rphy_free(dev->rphy); - dev->rphy = NULL; sas_unregister_common_dev(port, dev); + return; } - if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { sas_rphy_unlink(dev->rphy); list_move_tail(&dev->disco_list_node, &port->destroy_list); sas_discover_event(dev->port, DISCE_DESTRUCT); @@ -417,8 +420,6 @@ static void sas_discover_domain(struct work_struct *work) if (error) { sas_rphy_free(dev->rphy); - dev->rphy = NULL; - list_del_init(&dev->disco_list_node); spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 833bea067a7..37c3c3f5f35 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -783,6 +783,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_init_dev(child); child->rphy = rphy; + get_device(&rphy->dev); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -806,6 +807,7 @@ static struct domain_device *sas_ex_discover_end_dev( sas_init_dev(child); child->rphy = rphy; + get_device(&rphy->dev); sas_fill_in_rphy(child, rphy); list_add_tail(&child->disco_list_node, &parent->port->disco_list); @@ -830,8 +832,6 @@ static struct domain_device *sas_ex_discover_end_dev( out_list_del: sas_rphy_free(child->rphy); - child->rphy = NULL; - list_del(&child->disco_list_node); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); @@ -911,6 +911,7 @@ static struct domain_device *sas_ex_discover_expander( } port = parent->port; child->rphy = rphy; + get_device(&rphy->dev); edev = rphy_to_expander_device(rphy); child->dev_type = phy->attached_dev_type; kref_get(&parent->kref); @@ -934,6 +935,7 @@ static struct domain_device *sas_ex_discover_expander( res = sas_discover_expander(child); if (res) { + sas_rphy_delete(rphy); spin_lock_irq(&parent->port->dev_list_lock); list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); -- cgit v1.2.3-70-g09d2 From 0f3fce5cc77e1f35758ef0e46a989e76e5046a7b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Mar 2012 13:24:29 -0700 Subject: [SCSI] libsas: fix ata_eh clobbering ex_phys via smp_ata_check_ready The check_ready implementation in the expander-attached ata device case polls on sas_ex_phy_discover(). The effect is that the ex_phy fields (critically ->attached_sas_addr) can change. When ata_eh ends and libsas comes along to revalidate the domain sas_unregister_devs_sas_addr() can fail to lookup devices to remove, or fail to re-add an ata device that ata_eh marked as disabled. So change the code to skip the sas_address and change count updates when ata_eh is active. Cc: Jack Wang Tested-by: Maciej Patelczyk Tested-by: Bartek Nowakowski Tested-by: Jacek Danecki Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 37c3c3f5f35..c1f91b1c27c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -202,6 +202,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) u8 sas_addr[SAS_ADDR_SIZE]; struct smp_resp *resp = rsp; struct discover_resp *dr = &resp->disc; + struct sas_ha_struct *ha = dev->port->ha; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; struct sas_rphy *rphy = dev->rphy; @@ -209,6 +210,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) char *type; if (new_phy) { + if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) + return; phy->phy = sas_phy_alloc(&rphy->dev, phy_id); /* FIXME: error_handling */ @@ -233,6 +236,8 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); phy->attached_dev_type = to_dev_type(dr); + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + goto out; phy->phy_id = phy_id; phy->linkrate = dr->linkrate; phy->attached_sata_host = dr->attached_sata_host; @@ -266,6 +271,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) return; } + out: switch (phy->attached_dev_type) { case SATA_PENDING: type = "stp pending"; @@ -304,7 +310,15 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) else return; - SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + /* if the attached device type changed and ata_eh is active, + * make sure we run revalidation when eh completes (see: + * sas_enable_revalidation) + */ + if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) + set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); + + SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", SAS_ADDR(dev->sas_addr), phy->phy_id, sas_route_char(dev, phy), phy->linkrate, SAS_ADDR(phy->attached_sas_addr), type); -- cgit v1.2.3-70-g09d2 From b2024459252a9d2d312ee562f86f332a1498f412 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Mar 2012 21:09:07 -0700 Subject: [SCSI] libsas, libata: fix start of life for a sas ata_port This changes the ordering of initialization and probing events from: 1/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN 2/ allocate ata_port and schedule port probe in DISCE_PROBE ...to: 1/ allocate ata_port in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN 2/ allocate rphy in PORTE_BYTES_DMAED, DISCE_REVALIDATE_DOMAIN 3/ schedule port probe in DISCE_PROBE This ordering prevents PHYE_SIGNAL_LOSS_EVENTS from sneaking in to destrory ata devices before they have been fully initialized: BUG: unable to handle kernel paging request at 0000000000003b10 IP: [] sas_ata_end_eh+0x12/0x5e [libsas] ... [] sas_unregister_common_dev+0x78/0xc9 [libsas] [] sas_unregister_dev+0x4f/0xad [libsas] [] sas_unregister_domain_devices+0x7f/0xbf [libsas] [] sas_deform_port+0x61/0x1b8 [libsas] [] sas_phye_loss_of_signal+0x29/0x2b [libsas] ...and kills the awkward "sata domain_device briefly existing in the domain without an ata_port" state. Reported-by: Michal Kosciowski Signed-off-by: Dan Williams Acked-by: Jeff Garzik Signed-off-by: James Bottomley --- drivers/ata/libata-scsi.c | 35 ++++++++++++++++++++--------------- drivers/scsi/ipr.c | 6 +++++- drivers/scsi/libsas/sas_ata.c | 33 ++++++++++----------------------- drivers/scsi/libsas/sas_discover.c | 13 ++++++++++--- drivers/scsi/libsas/sas_expander.c | 8 +++++--- include/linux/libata.h | 3 ++- include/scsi/sas_ata.h | 4 ++-- 7 files changed, 54 insertions(+), 48 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7832b1ad232..22226350cd0 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3839,18 +3839,25 @@ 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) +/** + * ata_sas_async_probe - simply schedule probing and return + * @ap: Port to probe + * + * For batch scheduling of probe for sas attached ata devices, assumes + * the port has already been through ata_sas_port_init() + */ +void ata_sas_async_probe(struct ata_port *ap) { - int rc = ap->ops->port_start(ap); - - if (!rc) { - ap->print_id = atomic_inc_return(&ata_print_id); - __ata_port_probe(ap); - } + __ata_port_probe(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_async_probe); - return rc; +int ata_sas_sync_probe(struct ata_port *ap) +{ + return ata_port_probe(ap); } -EXPORT_SYMBOL_GPL(ata_sas_async_port_init); +EXPORT_SYMBOL_GPL(ata_sas_sync_probe); + /** * ata_sas_port_init - Initialize a SATA device @@ -3867,12 +3874,10 @@ int ata_sas_port_init(struct ata_port *ap) { int rc = ap->ops->port_start(ap); - if (!rc) { - ap->print_id = atomic_inc_return(&ata_print_id); - rc = ata_port_probe(ap); - } - - return rc; + if (rc) + return rc; + ap->print_id = atomic_inc_return(&ata_print_id); + return 0; } EXPORT_SYMBOL_GPL(ata_sas_port_init); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index e002cd466e9..467dc38246f 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4549,8 +4549,12 @@ static int ipr_ata_slave_alloc(struct scsi_device *sdev) ENTER; if (sdev->sdev_target) sata_port = sdev->sdev_target->hostdata; - if (sata_port) + if (sata_port) { rc = ata_sas_port_init(sata_port->ap); + if (rc == 0) + rc = ata_sas_sync_probe(sata_port->ap); + } + if (rc) ipr_slave_destroy(sdev); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index bc0cecc6ad6..441d88ad99a 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -546,11 +546,12 @@ static struct ata_port_info sata_port_info = { .port_ops = &sas_sata_ops }; -int sas_ata_init_host_and_port(struct domain_device *found_dev) +int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; struct Scsi_Host *shost = ha->core.shost; struct ata_port *ap; + int rc; ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, @@ -567,8 +568,11 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - /* publish initialized ata port */ - smp_wmb(); + rc = ata_sas_port_init(ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; @@ -648,18 +652,13 @@ static void sas_get_ata_command_set(struct domain_device *dev) void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; - int err; mutex_lock(&port->ha->disco_mutex); - list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) { + list_for_each_entry(dev, &port->disco_list, disco_list_node) { if (!dev_is_sata(dev)) continue; - err = sas_ata_init_host_and_port(dev); - if (err) - sas_fail_probe(dev, __func__, err); - else - ata_sas_async_port_init(dev->sata_dev.ap); + ata_sas_async_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -718,18 +717,6 @@ 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 sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); @@ -753,7 +740,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) spin_lock(&port->dev_list_lock); list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (!sas_ata_dev_eh_valid(dev)) + if (!dev_is_sata(dev)) continue; async_schedule_domain(async_sas_ata_eh, dev, &async); } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 13b5891f996..629a0865b13 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -72,6 +72,7 @@ static int sas_get_port_device(struct asd_sas_port *port) struct asd_sas_phy *phy; struct sas_rphy *rphy; struct domain_device *dev; + int rc = -ENODEV; dev = sas_alloc_device(); if (!dev) @@ -110,9 +111,16 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_init_dev(dev); + dev->port = port; switch (dev->dev_type) { - case SAS_END_DEV: case SATA_DEV: + rc = sas_ata_init(dev); + if (rc) { + rphy = NULL; + break; + } + /* fall through */ + case SAS_END_DEV: rphy = sas_end_device_alloc(port->port); break; case EDGE_DEV: @@ -131,7 +139,7 @@ static int sas_get_port_device(struct asd_sas_port *port) if (!rphy) { sas_put_device(dev); - return -ENODEV; + return rc; } rphy->identify.phy_identifier = phy->phy->identify.phy_identifier; @@ -139,7 +147,6 @@ static int sas_get_port_device(struct asd_sas_port *port) sas_fill_in_rphy(dev, rphy); sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); port->port_dev = dev; - dev->port = port; dev->linkrate = port->linkrate; dev->min_linkrate = port->linkrate; dev->max_linkrate = port->linkrate; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index c1f91b1c27c..75247a176c6 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -790,12 +790,14 @@ static struct domain_device *sas_ex_discover_end_dev( if (res) goto out_free; + sas_init_dev(child); + res = sas_ata_init(child); + if (res) + goto out_free; rphy = sas_end_device_alloc(phy->port); - if (unlikely(!rphy)) + if (!rphy) goto out_free; - sas_init_dev(child); - child->rphy = rphy; get_device(&rphy->dev); diff --git a/include/linux/libata.h b/include/linux/libata.h index 42378d637ff..e926df7b54c 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -996,7 +996,8 @@ 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 void ata_sas_async_probe(struct ata_port *ap); +extern int ata_sas_sync_probe(struct ata_port *ap); 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/sas_ata.h b/include/scsi/sas_ata.h index cdccd2eb7b6..77670e823ed 100644 --- a/include/scsi/sas_ata.h +++ b/include/scsi/sas_ata.h @@ -37,7 +37,7 @@ 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); +int sas_ata_init(struct domain_device *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, @@ -52,7 +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) +static inline int sas_ata_init(struct domain_device *dev) { return 0; } -- cgit v1.2.3-70-g09d2 From 7d1d865181185bdf1316d236b1b4bd02c9020729 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Mar 2012 10:50:27 -0700 Subject: [SCSI] libsas: fix false positive 'device attached' conditions Normalize phy->attached_sas_addr to return a zero-address in the case when device-type == NO_DEVICE or the linkrate is invalid to handle expanders that put non-zero sas addresses in the discovery response: sas: ex 5001b4da000f903f phy02:U:0 attached: 0100000000000000 (no device) sas: ex 5001b4da000f903f phy01:U:0 attached: 0100000000000000 (no device) sas: ex 5001b4da000f903f phy03:U:0 attached: 0100000000000000 (no device) sas: ex 5001b4da000f903f phy00:U:0 attached: 0100000000000000 (no device) Reported-by: Andrzej Jakowski Signed-off-by: Dan Williams Cc: stable@vger.kernel.org Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 75247a176c6..caa0525d252 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -245,7 +245,14 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->attached_sata_ps = dr->attached_sata_ps; phy->attached_iproto = dr->iproto << 1; phy->attached_tproto = dr->tproto << 1; - memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); + /* help some expanders that fail to zero sas_address in the 'no + * device' case + */ + if (phy->attached_dev_type == NO_DEVICE || + phy->linkrate < SAS_LINK_RATE_1_5_GBPS) + memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + else + memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); phy->attached_phy_id = dr->attached_phy_id; phy->phy_change_count = dr->change_count; phy->routing_attr = dr->routing_attr; -- cgit v1.2.3-70-g09d2 From b4698d88585e23d815506f7f38c48192d944b2eb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 19 Apr 2012 23:48:12 -0700 Subject: [SCSI] Revert "[SCSI] libsas: fix sas port naming" This reverts commit a692b0eec5efae382dfa800e8b4b083f172921a7. Tom reports: [ 8.741033] ------------[ cut here ]------------ [ 8.741038] WARNING: at fs/sysfs/dir.c:508 sysfs_add_one+0xc1/0xf0() [ 8.741040] Hardware name: To Be Filled By O.E.M. [ 8.741041] sysfs: cannot create duplicate filename ...and missing 2 out of 4 drives connected to mvsas. Commit a692b0ee made the assumption that all the phy ids an lldd registers to libsas are unique. However, in the "multi-chip" case mvsas does a rather annoying duplication of phy ids in the array passed to libsas. So, for example, chip0 has phy0-3 at ha phy index 0-3 and chip1 has its phy0-3 at ha phy index 4-7. The more natural model would be to create a scsi_host (and sas_ha) per chip (controller), but for now revert the naming fix which unfortunately means dealing with unpredictable end-device names for a bit longer. Cc: Xiangliang Yu Cc: Patrick Thomson Reported-by: Tom Rini Tested-by: Tom Rini Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 1cf7d75ad5e..e884a8c58a0 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -123,7 +123,7 @@ static void sas_form_port(struct asd_sas_phy *phy) spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); if (!port->port) { - port->port = sas_port_alloc(phy->phy->dev.parent, phy->id); + port->port = sas_port_alloc(phy->phy->dev.parent, port->id); BUG_ON(!port->port); sas_port_add(port->port); } -- cgit v1.2.3-70-g09d2 From 93f90e5186053611fe93d889e99ee2852f4da250 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Fri, 6 Apr 2012 15:52:51 -0700 Subject: [SCSI] libfc: update mfs boundry checking A previous commit changed the mfs checking to ensure the new mfs is less or equal to the mfs supported by the FCF. This doesn't work for BRDCM cards as they set an mfs of 2048 regardless of whether the switch returns a larger mfs. This patch validates the new mfs against the upper and lower spec defined boundries for a FCoE mfs. Signed-off-by: Vasu Dev Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/libfc/fc_lport.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index ef9560dff29..cc83b66d45b 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -1742,17 +1742,19 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && - mfs <= lport->mfs) { - lport->mfs = mfs; - fc_host_maxframe_size(lport->host) = mfs; - } else { + + if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) { FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, " "lport->mfs:%hu\n", mfs, lport->mfs); fc_lport_error(lport, fp); goto err; } + if (mfs <= lport->mfs) { + lport->mfs = mfs; + fc_host_maxframe_size(lport->host) = mfs; + } + csp_flags = ntohs(flp->fl_csp.sp_features); r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); -- cgit v1.2.3-70-g09d2 From f4e1648a4f4acc964cefc51c1637aad0dca6c517 Mon Sep 17 00:00:00 2001 From: Vikas Chaudhary Date: Wed, 25 Apr 2012 07:26:13 -0700 Subject: [SCSI] qla2xxx: Fix reset time out as qla2xxx not ack to reset request. Signed-off-by: Vikas Chaudhary Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_nx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index f0528539bbb..de722a93343 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -3125,6 +3125,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) ql_log(ql_log_info, vha, 0x00b7, "HW State: COLD/RE-INIT.\n"); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + qla82xx_set_rst_ready(ha); if (ql2xmdenable) { if (qla82xx_md_collect(vha)) ql_log(ql_log_warn, vha, 0xb02c, -- cgit v1.2.3-70-g09d2 From a49393f2ae13e3a0c61dbdbea77c2ff7614df474 Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Wed, 25 Apr 2012 07:26:14 -0700 Subject: [SCSI] qla2xxx: Block flash access from application when device is initialized for ISP82xx. Signed-off-by: Giridhar Malavali Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_bsg.c | 3 +++ drivers/scsi/qla2xxx/qla_sup.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index f74cc0602f3..bc3cc6d9111 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1367,6 +1367,9 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; + if (ha->flags.isp82xx_reset_hdlr_active) + return -EBUSY; + rval = qla2x00_optrom_setup(bsg_job, vha, 0); if (rval) return rval; diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 3c13c0a6be6..a683e766d1a 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -1017,6 +1017,9 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) return; + if (ha->flags.isp82xx_reset_hdlr_active) + return; + ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); if (hdr.version == __constant_cpu_to_le16(0xffff)) -- cgit v1.2.3-70-g09d2 From 4aee57667e9b237d0bd0c5a167c8b6103a27756a Mon Sep 17 00:00:00 2001 From: Giridhar Malavali Date: Wed, 25 Apr 2012 07:26:15 -0700 Subject: [SCSI] qla2xxx: Proper completion to scsi-ml for scsi status task_set_full and busy. In case of firmmware detected under-run condition and scsi status of task_set_full or busy_condition, return that to the mid layer for proper error handling instead of DID_ERROR (which causes error handler activation and a full retry). Signed-off-by: Giridhar Malavali Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_dbg.c | 2 +- drivers/scsi/qla2xxx/qla_isr.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 897731b93df..62324a1d557 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -15,7 +15,7 @@ * | Mailbox commands | 0x113e | 0x112c-0x112e | * | | | 0x113a | * | Device Discovery | 0x2086 | 0x2020-0x2022 | - * | Queue Command and IO tracing | 0x302f | 0x3006,0x3008 | + * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 | * | | | 0x302d-0x302e | * | DPC Thread | 0x401c | | * | Async Events | 0x505d | 0x502b-0x502f | diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index f79844ce712..ce42288049b 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1715,13 +1715,24 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) res = DID_ERROR << 16; break; } - } else { + } else if (lscsi_status != SAM_STAT_TASK_SET_FULL && + lscsi_status != SAM_STAT_BUSY) { + /* + * scsi status of task set and busy are considered to be + * task not completed. + */ + ql_dbg(ql_dbg_io, fcport->vha, 0x301f, "Dropped frame(s) detected (0x%x " - "of 0x%x bytes).\n", resid, scsi_bufflen(cp)); + "of 0x%x bytes).\n", resid, + scsi_bufflen(cp)); res = DID_ERROR << 16 | lscsi_status; goto check_scsi_status; + } else { + ql_dbg(ql_dbg_io, fcport->vha, 0x3030, + "scsi_status: 0x%x, lscsi_status: 0x%x\n", + scsi_status, lscsi_status); } res = DID_OK << 16 | lscsi_status; -- cgit v1.2.3-70-g09d2 From aaf4d3e2c647b5d1b24082b766c173e6c7edf79b Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Wed, 25 Apr 2012 07:26:16 -0700 Subject: [SCSI] qla2xxx: Properly check for current state after the fabric-login request. [jejb: checkpatch fixes] Signed-off-by: Saurav Kashyap Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index a2f999273a5..7db803377c6 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3577,9 +3577,25 @@ void qla2x00_relogin(struct scsi_qla_host *vha) continue; /* Attempt a retry. */ status = 1; - } else + } else { status = qla2x00_fabric_login(vha, fcport, &next_loopid); + if (status == QLA_SUCCESS) { + int status2; + uint8_t opts; + + opts = 0; + if (fcport->flags & + FCF_FCP2_DEVICE) + opts |= BIT_1; + status2 = + qla2x00_get_port_database( + vha, fcport, + opts); + if (status2 != QLA_SUCCESS) + status = 1; + } + } } else status = qla2x00_local_device_login(vha, fcport); -- cgit v1.2.3-70-g09d2 From 6abd7f132a4b74d9cdd3ef79fc71ca73909631f2 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2012 07:26:17 -0700 Subject: [SCSI] qla2xxx: Update version number to 8.04.00.03-k. Signed-off-by: Giridhar Malavali Signed-off-by: Chad Dupuis Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_version.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 29d780c3804..f5fdb16bec9 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,9 +7,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.07.13-k" +#define QLA2XXX_VERSION "8.04.00.03-k" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 3 -#define QLA_DRIVER_PATCH_VER 7 +#define QLA_DRIVER_MINOR_VER 4 +#define QLA_DRIVER_PATCH_VER 0 #define QLA_DRIVER_BETA_VER 3 -- cgit v1.2.3-70-g09d2 From 3c8d9a957d0ae62c2815393a781ab7ff4d5205e7 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 4 May 2012 09:40:04 +0000 Subject: [SCSI] fix oops in all legacy host adapters caused by 6f381fa Commit 6f381fa344911d5a234b13574433cf23036f9467 Author: Lin Ming [SCSI] scsi_lib: use correct DMA device in __scsi_alloc_queue Caused a regression where we oops in every legacy mode SCSI host driver because they supply a NULL pointer to scsi_add_host(). Fix this by checking for the NULL in scsi_add_host_with_dma() and changing the DMA device to being the platform_bus in that case (which replicates the original behaviour). Reported-by: Al Viro Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 351dc0b86fa..a3a056a9db6 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -218,6 +218,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, if (!shost->shost_gendev.parent) shost->shost_gendev.parent = dev ? dev : &platform_bus; + if (!dma_dev) + dma_dev = shost->shost_gendev.parent; + shost->dma_dev = dma_dev; error = device_add(&shost->shost_gendev); -- cgit v1.2.3-70-g09d2 From e4594bb50518eb89c447be97dabd5bd99f405d71 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 4 May 2012 12:32:04 +0200 Subject: [SCSI] virtio_scsi: fix TMF use-after-free Fix a use-after-free in the TMF path, where cmd may have been already freed by virtscsi_complete_free when wait_for_completion restarts executing virtscsi_tmf. Technically a race, but in practice the command will always be freed long before the completion waiter is awoken. The fix is to make callers specifying a completion responsible for freeing the command in all cases. Signed-off-by: Hu Tao Signed-off-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index efccd72c4a3..1b384311726 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -175,7 +175,8 @@ static void virtscsi_complete_free(void *buf) if (cmd->comp) complete_all(cmd->comp); - mempool_free(cmd, virtscsi_cmd_pool); + else + mempool_free(cmd, virtscsi_cmd_pool); } static void virtscsi_ctrl_done(struct virtqueue *vq) @@ -311,21 +312,22 @@ out: static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) { DECLARE_COMPLETION_ONSTACK(comp); - int ret; + int ret = FAILED; cmd->comp = ∁ - ret = virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd, - sizeof cmd->req.tmf, sizeof cmd->resp.tmf, - GFP_NOIO); - if (ret < 0) - return FAILED; + if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd, + sizeof cmd->req.tmf, sizeof cmd->resp.tmf, + GFP_NOIO) < 0) + goto out; wait_for_completion(&comp); - if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK && - cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED) - return FAILED; + if (cmd->resp.tmf.response == VIRTIO_SCSI_S_OK || + cmd->resp.tmf.response == VIRTIO_SCSI_S_FUNCTION_SUCCEEDED) + ret = SUCCESS; - return SUCCESS; +out: + mempool_free(cmd, virtscsi_cmd_pool); + return ret; } static int virtscsi_device_reset(struct scsi_cmnd *sc) -- cgit v1.2.3-70-g09d2 From 14e99b4a3f5323bb961754de5024daff79e59b98 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 10 Feb 2012 01:05:43 -0800 Subject: isci: improve 'invalid state' warnings Convert controller state machine warnings to emit the state number (it missed the number to string conversion, but since these error rarely happen not much motivation to go further). Fix up the rnc warnings to use the state name. Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 33 ++++++++++++++++----------------- drivers/scsi/isci/remote_node_context.c | 33 ++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 30 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d4bf9c12ecd..3822ffb71ca 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -987,9 +987,8 @@ static enum sci_status sci_controller_start(struct isci_host *ihost, u16 index; if (ihost->sm.current_state_id != SCIC_INITIALIZED) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller start operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -1213,9 +1212,8 @@ static void isci_host_completion_routine(unsigned long data) static enum sci_status sci_controller_stop(struct isci_host *ihost, u32 timeout) { if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller stop operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -1250,9 +1248,8 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) sci_change_state(&ihost->sm, SCIC_RESETTING); return SCI_SUCCESS; default: - dev_warn(&ihost->pdev->dev, - "SCIC Controller reset operation requested in " - "invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } } @@ -2279,9 +2276,8 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost) unsigned long i, state, val; if (ihost->sm.current_state_id != SCIC_RESET) { - dev_warn(&ihost->pdev->dev, - "SCIC Controller initialize operation requested " - "in invalid state\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2842,7 +2838,8 @@ enum sci_status sci_controller_start_io(struct isci_host *ihost, enum sci_status status; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, "invalid state to start I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2866,8 +2863,8 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, enum sci_status status; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, - "invalid state to terminate request\n"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2915,7 +2912,8 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost, clear_bit(IREQ_ACTIVE, &ireq->flags); return SCI_SUCCESS; default: - dev_warn(&ihost->pdev->dev, "invalid state to complete I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } @@ -2926,7 +2924,8 @@ enum sci_status sci_controller_continue_io(struct isci_request *ireq) struct isci_host *ihost = ireq->owning_controller; if (ihost->sm.current_state_id != SCIC_READY) { - dev_warn(&ihost->pdev->dev, "invalid state to continue I/O"); + dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", + __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 3a9463481f3..994ec0c25a7 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -443,14 +443,16 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con break; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state: %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } return SCI_SUCCESS; out: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: code: %#x state: %d\n", __func__, event_code, state); + "%s: code: %#x state: %s\n", __func__, event_code, + rnc_state_name(state)); return SCI_FAILURE; } @@ -477,7 +479,8 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context return SCI_SUCCESS; case SCI_RNC_INITIAL: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state: %s\n", __func__, + rnc_state_name(state)); /* We have decided that the destruct request on the remote node context * can not fail since it is either in the initial/destroyed state or is * can be destroyed. @@ -485,7 +488,8 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } } @@ -500,7 +504,8 @@ enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context * state = sci_rnc->sm.current_state_id; if (state != SCI_RNC_READY) { dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } @@ -571,7 +576,8 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } } @@ -590,15 +596,15 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context case SCI_RNC_TX_RX_SUSPENDED: case SCI_RNC_AWAIT_SUSPENSION: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; default: - break; + dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); + return SCI_FAILURE_INVALID_STATE; } - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: requested to start IO while still resuming, %d\n", - __func__, state); - return SCI_FAILURE_INVALID_STATE; } enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc, @@ -618,7 +624,8 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %d\n", __func__, state); + "%s: invalid state %s\n", __func__, + rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } } -- cgit v1.2.3-70-g09d2 From 11cc51835af0e6fbb2da9cb012bdaaa036497b7f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 1 Feb 2012 00:23:10 -0800 Subject: isci: kill ->is_direct_attached domain_device ->parent conveys the same information. Occurrences of ->is_direct_attached appear next to incomplete open-coded versions of dev_is_sata(), clean those up as well. Signed-off-by: Dan Williams --- drivers/scsi/isci/host.h | 4 ++-- drivers/scsi/isci/remote_device.c | 29 +++++------------------------ drivers/scsi/isci/remote_device.h | 1 - drivers/scsi/isci/remote_node_context.c | 27 ++++++++------------------- drivers/scsi/isci/request.c | 5 ++--- 5 files changed, 17 insertions(+), 49 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index adbad69d106..a9679ee7084 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -55,6 +55,7 @@ #ifndef _SCI_HOST_H_ #define _SCI_HOST_H_ +#include #include "remote_device.h" #include "phy.h" #include "isci.h" @@ -378,8 +379,7 @@ static inline int sci_remote_device_node_count(struct isci_remote_device *idev) { struct domain_device *dev = idev->domain_dev; - if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) && - !idev->is_direct_attached) + if (dev_is_sata(dev) && dev->parent) return SCU_STP_REMOTE_NODE_COUNT; return SCU_SSP_REMOTE_NODE_COUNT; } diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 8f501b0a81d..71f50906473 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1113,33 +1113,20 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport, { enum sci_status status; struct sci_port_properties properties; - struct domain_device *dev = idev->domain_dev; sci_remote_device_construct(iport, idev); - /* - * This information is request to determine how many remote node context - * entries will be needed to store the remote node. - */ - idev->is_direct_attached = true; - sci_port_get_properties(iport, &properties); /* Get accurate port width from port's phy mask for a DA device. */ idev->device_port_width = hweight32(properties.phy_mask); status = sci_controller_allocate_remote_node_context(iport->owning_controller, - idev, - &idev->rnc.remote_node_index); + idev, + &idev->rnc.remote_node_index); if (status != SCI_SUCCESS) return status; - if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || - (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) - /* pass */; - else - return SCI_FAILURE_UNSUPPORTED_PROTOCOL; - idev->connection_rate = sci_port_get_max_allowed_speed(iport); return SCI_SUCCESS; @@ -1171,19 +1158,13 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, if (status != SCI_SUCCESS) return status; - if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || - (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) - /* pass */; - else - return SCI_FAILURE_UNSUPPORTED_PROTOCOL; - - /* - * For SAS-2 the physical link rate is actually a logical link + /* For SAS-2 the physical link rate is actually a logical link * rate that incorporates multiplexing. The SCU doesn't * incorporate multiplexing and for the purposes of the * connection the logical link rate is that same as the * physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay - * one another, so this code works for both situations. */ + * one another, so this code works for both situations. + */ idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport), dev->linkrate); diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 58637ee08f5..4a67ff0eb94 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -94,7 +94,6 @@ struct isci_remote_device { struct sci_base_state_machine sm; u32 device_port_width; enum sas_linkrate connection_rate; - bool is_direct_attached; struct isci_port *owning_port; struct sci_remote_node_context rnc; /* XXX unify with device reference counting and delete */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 994ec0c25a7..8ce5a35891e 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -131,7 +131,7 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont rnc->ssp.arbitration_wait_time = 0; - if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { + if (dev_is_sata(dev)) { rnc->ssp.connection_occupancy_timeout = ihost->user_parameters.stp_max_occupancy_timeout; rnc->ssp.connection_inactivity_timeout = @@ -219,13 +219,12 @@ static void sci_remote_node_context_validate_context_buffer(struct sci_remote_no rnc_buffer->ssp.is_valid = true; - if (!idev->is_direct_attached && - (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP))) { + if (dev_is_sata(dev) && dev->parent) { sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_96); } else { sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_32); - if (idev->is_direct_attached) + if (!dev->parent) sci_port_setup_transports(idev->owning_port, sci_rnc->remote_node_index); } @@ -287,10 +286,8 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m * resume because of a target reset we also need to update * the STPTLDARNI register with the RNi of the device */ - if ((dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) && - idev->is_direct_attached) - sci_port_setup_transports(idev->owning_port, - rnc->remote_node_index); + if (dev_is_sata(dev) && !dev->parent) + sci_port_setup_transports(idev->owning_port, rnc->remote_node_index); sci_remote_device_post_request(idev, SCU_CONTEXT_COMMAND_POST_RNC_RESUME); } @@ -553,18 +550,10 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); - /* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */ - if (dev->dev_type == SAS_END_DEV || dev_is_expander(dev)) + if (dev_is_sata(dev) && dev->parent) + sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); + else sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { - if (idev->is_direct_attached) { - /* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */ - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - } else { - sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); - } - } else - return SCI_FAILURE; return SCI_SUCCESS; } case SCI_RNC_TX_RX_SUSPENDED: diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 2def1e3960f..01844ef1965 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3193,7 +3193,7 @@ sci_io_request_construct(struct isci_host *ihost, if (dev->dev_type == SAS_END_DEV) /* pass */; - else if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) + else if (dev_is_sata(dev)) memset(&ireq->stp.cmd, 0, sizeof(ireq->stp.cmd)); else if (dev_is_expander(dev)) /* pass */; @@ -3215,8 +3215,7 @@ enum sci_status sci_task_request_construct(struct isci_host *ihost, /* Build the common part of the request */ sci_general_request_construct(ihost, idev, ireq); - if (dev->dev_type == SAS_END_DEV || - dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) { + if (dev->dev_type == SAS_END_DEV || dev_is_sata(dev)) { set_bit(IREQ_TMF, &ireq->flags); memset(ireq->tc, 0, sizeof(struct scu_task_context)); } else -- cgit v1.2.3-70-g09d2 From c79dd80d73017a88a2c2ae46e7d5303cba6a32e0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 1 Feb 2012 00:44:14 -0800 Subject: isci: kill sci_phy_protocol and sci_request_protocol Holdovers from the initial driver cleanup, replace with enum sas_protocol. Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 4 ++-- drivers/scsi/isci/phy.c | 12 ++++++------ drivers/scsi/isci/phy.h | 9 +-------- drivers/scsi/isci/port.c | 14 ++++++-------- drivers/scsi/isci/request.c | 10 +++++----- drivers/scsi/isci/request.h | 9 +-------- include/scsi/sas.h | 1 + 7 files changed, 22 insertions(+), 37 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 3822ffb71ca..83886a27e84 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1911,7 +1911,7 @@ static void power_control_timeout(unsigned long data) ihost->power_control.phys_granted_power++; sci_phy_consume_power_handler(iphy); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + if (iphy->protocol == SAS_PROTOCOL_SSP) { u8 j; for (j = 0; j < SCI_MAX_PHYS; j++) { @@ -1985,7 +1985,7 @@ void sci_controller_power_control_queue_insert(struct isci_host *ihost, sizeof(current_phy->frame_rcvd.iaf.sas_addr)); if (current_phy->sm.current_state_id == SCI_PHY_READY && - current_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS && + current_phy->protocol == SAS_PROTOCOL_SSP && other == 0) { sci_phy_consume_power_handler(iphy); break; diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index fab3586840b..c685ab04532 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -580,7 +580,7 @@ static void sci_phy_start_sas_link_training(struct isci_phy *iphy) sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SAS_SPEED_EN); - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SAS; + iphy->protocol = SAS_PROTOCOL_SSP; } static void sci_phy_start_sata_link_training(struct isci_phy *iphy) @@ -591,7 +591,7 @@ static void sci_phy_start_sata_link_training(struct isci_phy *iphy) */ sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_POWER); - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; + iphy->protocol = SAS_PROTOCOL_SATA; } /** @@ -797,7 +797,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) */ break; case SCU_EVENT_SATA_PHY_DETECTED: - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; + iphy->protocol = SAS_PROTOCOL_SATA; /* We have received the SATA PHY notification change state */ sci_change_state(&iphy->sm, SCI_PHY_SUB_AWAIT_SATA_SPEED_EN); @@ -1215,7 +1215,7 @@ static void sci_phy_starting_state_enter(struct sci_base_state_machine *sm) scu_link_layer_start_oob(iphy); /* We don't know what kind of phy we are going to be just yet */ - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + iphy->protocol = SAS_PROTOCOL_NONE; iphy->bcn_received_while_port_unassigned = false; if (iphy->sm.previous_state_id == SCI_PHY_READY) @@ -1250,7 +1250,7 @@ static void sci_phy_resetting_state_enter(struct sci_base_state_machine *sm) */ sci_port_deactivate_phy(iphy->owning_port, iphy, false); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + if (iphy->protocol == SAS_PROTOCOL_SSP) { scu_link_layer_tx_hard_reset(iphy); } else { /* The SCU does not need to have a discrete reset state so @@ -1316,7 +1316,7 @@ void sci_phy_construct(struct isci_phy *iphy, iphy->owning_port = iport; iphy->phy_index = phy_index; iphy->bcn_received_while_port_unassigned = false; - iphy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + iphy->protocol = SAS_PROTOCOL_NONE; iphy->link_layer_registers = NULL; iphy->max_negotiated_speed = SAS_LINK_RATE_UNKNOWN; diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h index 0e45833ba06..45fecfa36a9 100644 --- a/drivers/scsi/isci/phy.h +++ b/drivers/scsi/isci/phy.h @@ -76,13 +76,6 @@ */ #define SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT 250 -enum sci_phy_protocol { - SCIC_SDS_PHY_PROTOCOL_UNKNOWN, - SCIC_SDS_PHY_PROTOCOL_SAS, - SCIC_SDS_PHY_PROTOCOL_SATA, - SCIC_SDS_MAX_PHY_PROTOCOLS -}; - /** * isci_phy - hba local phy infrastructure * @sm: @@ -95,7 +88,7 @@ struct isci_phy { struct sci_base_state_machine sm; struct isci_port *owning_port; enum sas_linkrate max_negotiated_speed; - enum sci_phy_protocol protocol; + enum sas_protocol protocol; u8 phy_index; bool bcn_received_while_port_unassigned; bool is_in_link_training; diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 5fada73b71f..923579fa029 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -184,7 +184,7 @@ static void isci_port_link_up(struct isci_host *isci_host, sci_port_get_properties(iport, &properties); - if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) { + if (iphy->protocol == SAS_PROTOCOL_SATA) { u64 attached_sas_address; iphy->sas_phy.oob_mode = SATA_OOB_MODE; @@ -204,7 +204,7 @@ static void isci_port_link_up(struct isci_host *isci_host, memcpy(&iphy->sas_phy.attached_sas_addr, &attached_sas_address, sizeof(attached_sas_address)); - } else if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + } else if (iphy->protocol == SAS_PROTOCOL_SSP) { iphy->sas_phy.oob_mode = SAS_OOB_MODE; iphy->sas_phy.frame_rcvd_size = sizeof(struct sas_identify_frame); @@ -517,7 +517,7 @@ void sci_port_get_attached_sas_address(struct isci_port *iport, struct sci_sas_a */ iphy = sci_port_get_a_connected_phy(iport); if (iphy) { - if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA) { + if (iphy->protocol != SAS_PROTOCOL_SATA) { sci_phy_get_attached_sas_address(iphy, sas); } else { sci_phy_get_sas_address(iphy, sas); @@ -624,7 +624,7 @@ static void sci_port_activate_phy(struct isci_port *iport, { struct isci_host *ihost = iport->owning_controller; - if (iphy->protocol != SCIC_SDS_PHY_PROTOCOL_SATA && (flags & PF_RESUME)) + if (iphy->protocol != SAS_PROTOCOL_SATA && (flags & PF_RESUME)) sci_phy_resume(iphy); iport->active_phy_mask |= 1 << iphy->phy_index; @@ -751,12 +751,10 @@ static bool sci_port_is_wide(struct isci_port *iport) * wide ports and direct attached phys. Since there are no wide ported SATA * devices this could become an invalid port configuration. */ -bool sci_port_link_detected( - struct isci_port *iport, - struct isci_phy *iphy) +bool sci_port_link_detected(struct isci_port *iport, struct isci_phy *iphy) { if ((iport->logical_port_index != SCIC_SDS_DUMMY_PORT) && - (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA)) { + (iphy->protocol == SAS_PROTOCOL_SATA)) { if (sci_port_is_wide(iport)) { sci_port_invalid_link_up(iport, iphy); return false; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 01844ef1965..835ede84887 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -730,7 +730,7 @@ static enum sci_status sci_io_request_construct_basic_ssp(struct isci_request *i { struct sas_task *task = isci_request_access_task(ireq); - ireq->protocol = SCIC_SSP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_SSP; scu_ssp_io_request_construct_task_context(ireq, task->data_dir, @@ -763,7 +763,7 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request * bool copy = false; struct sas_task *task = isci_request_access_task(ireq); - ireq->protocol = SCIC_STP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_STP; copy = (task->data_dir == DMA_NONE) ? false : true; @@ -1070,7 +1070,7 @@ request_started_state_tc_event(struct isci_request *ireq, case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_SDBFIS): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_REG_ERR): case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SDB_ERR): - if (ireq->protocol == SCIC_STP_PROTOCOL) { + if (ireq->protocol == SAS_PROTOCOL_STP) { ireq->scu_status = SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT; ireq->sci_status = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; @@ -3169,7 +3169,7 @@ sci_general_request_construct(struct isci_host *ihost, sci_init_sm(&ireq->sm, sci_request_state_table, SCI_REQ_INIT); ireq->target_device = idev; - ireq->protocol = SCIC_NO_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_NONE; ireq->saved_rx_frame_index = SCU_INVALID_FRAME_INDEX; ireq->sci_status = SCI_SUCCESS; @@ -3310,7 +3310,7 @@ sci_io_request_construct_smp(struct device *dev, if (!dma_map_sg(dev, sg, 1, DMA_TO_DEVICE)) return SCI_FAILURE; - ireq->protocol = SCIC_SMP_PROTOCOL; + ireq->protocol = SAS_PROTOCOL_SMP; /* byte swap the smp request. */ diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 057f2378452..4961f9fbf70 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -77,13 +77,6 @@ enum isci_request_status { dead = 0x07 }; -enum sci_request_protocol { - SCIC_NO_PROTOCOL, - SCIC_SMP_PROTOCOL, - SCIC_SSP_PROTOCOL, - SCIC_STP_PROTOCOL -}; /* XXX remove me, use sas_task.{dev|task_proto} instead */; - /** * isci_stp_request - extra request infrastructure to handle pio/atapi protocol * @pio_len - number of bytes requested at PIO setup @@ -140,7 +133,7 @@ struct isci_request { struct isci_host *owning_controller; struct isci_remote_device *target_device; u16 io_tag; - enum sci_request_protocol protocol; + enum sas_protocol protocol; u32 scu_status; /* hardware result */ u32 sci_status; /* upper layer disposition */ u32 post_context; diff --git a/include/scsi/sas.h b/include/scsi/sas.h index a577a833603..be3eb0bf1ac 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -103,6 +103,7 @@ enum sas_dev_type { }; enum sas_protocol { + SAS_PROTOCOL_NONE = 0, SAS_PROTOCOL_SATA = 0x01, SAS_PROTOCOL_SMP = 0x02, SAS_PROTOCOL_STP = 0x04, -- cgit v1.2.3-70-g09d2 From 944b787d0a469a376f4d6699eb01138823197513 Mon Sep 17 00:00:00 2001 From: Tom Jackson Date: Fri, 24 Feb 2012 09:38:49 +0000 Subject: isci: Don't filter BROADCAST CHANGE primitives Per the SAS spec, several types of BROADCAST CHANGE primitives must cause re-discovery of the originating expander. Only the standard BROADCAST CHANGE primitive was being sent to the LIBSAS layer. The other BC primitives have been added to the sci_phy_event_handler() Signed-off-by: Tom Jackson Signed-off-by: Dan Williams --- drivers/scsi/isci/phy.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index c685ab04532..474330fdbe1 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -875,12 +875,19 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; case SCU_EVENT_BROADCAST_CHANGE: + case SCU_EVENT_BROADCAST_SES: + case SCU_EVENT_BROADCAST_RESERVED0: + case SCU_EVENT_BROADCAST_RESERVED1: + case SCU_EVENT_BROADCAST_EXPANDER: + case SCU_EVENT_BROADCAST_AEN: /* Broadcast change received. Notify the port. */ if (phy_get_non_dummy_port(iphy) != NULL) sci_port_broadcast_change_received(iphy->owning_port, iphy); else iphy->bcn_received_while_port_unassigned = true; break; + case SCU_EVENT_BROADCAST_RESERVED3: + case SCU_EVENT_BROADCAST_RESERVED4: default: phy_event_warn(iphy, state, event_code); return SCI_FAILURE_INVALID_STATE; -- cgit v1.2.3-70-g09d2 From 1844e4789fe5c97a9ff3bb82628111abbe7cc846 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 15 Feb 2012 13:20:31 -0800 Subject: isci: kill ->status, and ->state_lock in isci_host They serve no incremental purpose over the existing sas_ha state. Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 11 ----------- drivers/scsi/isci/host.h | 23 ----------------------- 2 files changed, 34 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 83886a27e84..d647b07ba1a 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -642,7 +642,6 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co if (completion_status != SCI_SUCCESS) dev_info(&ihost->pdev->dev, "controller start timed out, continuing...\n"); - isci_host_change_state(ihost, isci_ready); clear_bit(IHOST_START_PENDING, &ihost->flags); wake_up(&ihost->eventq); } @@ -657,12 +656,7 @@ int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) sas_drain_work(ha); - dev_dbg(&ihost->pdev->dev, - "%s: ihost->status = %d, time = %ld\n", - __func__, isci_host_get_state(ihost), time); - return 1; - } /** @@ -1054,7 +1048,6 @@ void isci_host_scan_start(struct Scsi_Host *shost) static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status) { - isci_host_change_state(ihost, isci_stopped); sci_controller_disable_interrupts(ihost); clear_bit(IHOST_STOP_PENDING, &ihost->flags); wake_up(&ihost->eventq); @@ -1262,7 +1255,6 @@ void isci_host_deinit(struct isci_host *ihost) for (i = 0; i < isci_gpio_count(ihost); i++) writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); - isci_host_change_state(ihost, isci_stopping); for (i = 0; i < SCI_MAX_PORTS; i++) { struct isci_port *iport = &ihost->ports[i]; struct isci_remote_device *idev, *d; @@ -2494,12 +2486,9 @@ int isci_host_init(struct isci_host *ihost) struct sci_user_parameters sci_user_params; struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); - spin_lock_init(&ihost->state_lock); spin_lock_init(&ihost->scic_lock); init_waitqueue_head(&ihost->eventq); - isci_host_change_state(ihost, isci_starting); - status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index a9679ee7084..81485b4a1a5 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -191,9 +191,7 @@ struct isci_host { struct asd_sas_port sas_ports[SCI_MAX_PORTS]; struct sas_ha_struct sas_ha; - spinlock_t state_lock; struct pci_dev *pdev; - enum isci_status status; #define IHOST_START_PENDING 0 #define IHOST_STOP_PENDING 1 unsigned long flags; @@ -315,27 +313,6 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \ ihost = to_pci_info(pdev)->hosts[++id]) -static inline enum isci_status isci_host_get_state(struct isci_host *isci_host) -{ - return isci_host->status; -} - -static inline void isci_host_change_state(struct isci_host *isci_host, - enum isci_status status) -{ - unsigned long flags; - - dev_dbg(&isci_host->pdev->dev, - "%s: isci_host = %p, state = 0x%x", - __func__, - isci_host, - status); - spin_lock_irqsave(&isci_host->state_lock, flags); - isci_host->status = status; - spin_unlock_irqrestore(&isci_host->state_lock, flags); - -} - static inline void wait_for_start(struct isci_host *ihost) { wait_event(ihost->eventq, !test_bit(IHOST_START_PENDING, &ihost->flags)); -- cgit v1.2.3-70-g09d2 From ae904d15cf344bcb426f63982016f6bacc45825b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 17 Feb 2012 16:30:47 -0800 Subject: isci: kill isci_port.domain_dev_list Another unused field, and isci_port_init is overkill. Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 8 ++++++-- drivers/scsi/isci/port.c | 7 ------- drivers/scsi/isci/port.h | 6 ------ 3 files changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d647b07ba1a..bbec1982d07 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -2557,8 +2557,12 @@ int isci_host_init(struct isci_host *ihost) if (err) return err; - for (i = 0; i < SCI_MAX_PORTS; i++) - isci_port_init(&ihost->ports[i], ihost, i); + for (i = 0; i < SCI_MAX_PORTS; i++) { + struct isci_port *iport = &ihost->ports[i]; + + INIT_LIST_HEAD(&iport->remote_dev_list); + iport->isci_host = ihost; + } for (i = 0; i < SCI_MAX_PHYS; i++) isci_phy_init(&ihost->phys[i], ihost, i); diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 923579fa029..6ef4bd910f3 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -1606,13 +1606,6 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->phy_table[index] = NULL; } -void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index) -{ - INIT_LIST_HEAD(&iport->remote_dev_list); - INIT_LIST_HEAD(&iport->domain_dev_list); - iport->isci_host = ihost; -} - void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy *iphy) { struct isci_host *ihost = iport->owning_controller; diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index 6b56240c205..f8bd1e8dbfe 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -97,7 +97,6 @@ enum isci_status { struct isci_port { struct isci_host *isci_host; struct list_head remote_dev_list; - struct list_head domain_dev_list; #define IPORT_RESET_PENDING 0 unsigned long state; enum sci_status hard_reset_status; @@ -273,11 +272,6 @@ void sci_port_get_attached_sas_address( void isci_port_formed(struct asd_sas_phy *); void isci_port_deformed(struct asd_sas_phy *); -void isci_port_init( - struct isci_port *port, - struct isci_host *host, - int index); - int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport, struct isci_phy *iphy); int isci_ata_check_ready(struct domain_device *dev); -- cgit v1.2.3-70-g09d2 From abec912d71c44bbd642ce12ad98aab76f5a53163 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 15 Feb 2012 13:58:42 -0800 Subject: isci: refactor initialization for S3/S4 Based on an original implementation by Ed Nadolski and Artur Wojcik In preparation for S3/S4 support refactor initialization so that driver-load and resume-from-suspend can share the common init path of isci_host_init(). Organize the initialization into objects that are self-contained to the driver (initialized by isci_host_init) versus those that have some upward registration (initialized at allocation time asd_sas_phy, asd_sas_port, dma allocations). The largest change is moving the the validation of the oem and module parameters from isci_host_init() to isci_host_alloc(). The S3/S4 approach being taken is that libsas will be tasked with remembering the state of the domain and the lldd is free to be forgetful. In the case of isci we'll just re-init using a subset of the normal driver load path. [clean up some unused / mis-indented function definitions in host.h] Signed-off-by: Ed Nadolski Signed-off-by: Artur Wojcik Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 313 +++++--------------------- drivers/scsi/isci/host.h | 71 ++---- drivers/scsi/isci/init.c | 199 ++++++++++++++-- drivers/scsi/isci/probe_roms.c | 12 - drivers/scsi/isci/probe_roms.h | 2 - drivers/scsi/isci/request.c | 4 +- drivers/scsi/isci/unsolicited_frame_control.c | 30 +-- drivers/scsi/isci/unsolicited_frame_control.h | 6 +- 8 files changed, 277 insertions(+), 360 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index bbec1982d07..0fe372f9328 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1074,7 +1074,7 @@ static void sci_controller_completion_handler(struct isci_host *ihost) * @data: This parameter specifies the ISCI host object * */ -static void isci_host_completion_routine(unsigned long data) +void isci_host_completion_routine(unsigned long data) { struct isci_host *ihost = (struct isci_host *)data; struct list_head completed_request_list; @@ -1317,29 +1317,6 @@ static void __iomem *smu_base(struct isci_host *isci_host) return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id; } -static void isci_user_parameters_get(struct sci_user_parameters *u) -{ - int i; - - for (i = 0; i < SCI_MAX_PHYS; i++) { - struct sci_phy_user_params *u_phy = &u->phys[i]; - - u_phy->max_speed_generation = phy_gen; - - /* we are not exporting these for now */ - u_phy->align_insertion_frequency = 0x7f; - u_phy->in_connection_align_insertion_frequency = 0xff; - u_phy->notify_enable_spin_up_insertion_frequency = 0x33; - } - - u->stp_inactivity_timeout = stp_inactive_to; - u->ssp_inactivity_timeout = ssp_inactive_to; - u->stp_max_occupancy_timeout = stp_max_occ_to; - u->ssp_max_occupancy_timeout = ssp_max_occ_to; - u->no_outbound_task_timeout = no_outbound_task_to; - u->max_concurr_spinup = max_concurr_spinup; -} - static void sci_controller_initial_state_enter(struct sci_base_state_machine *sm) { struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); @@ -1648,55 +1625,6 @@ static const struct sci_base_state sci_controller_state_table[] = { [SCIC_FAILED] = {} }; -static void sci_controller_set_default_config_parameters(struct isci_host *ihost) -{ - /* these defaults are overridden by the platform / firmware */ - u16 index; - - /* Default to APC mode. */ - ihost->oem_parameters.controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; - - /* Default to APC mode. */ - ihost->oem_parameters.controller.max_concurr_spin_up = 1; - - /* Default to no SSC operation. */ - ihost->oem_parameters.controller.do_enable_ssc = false; - - /* Default to short cables on all phys. */ - ihost->oem_parameters.controller.cable_selection_mask = 0; - - /* Initialize all of the port parameter information to narrow ports. */ - for (index = 0; index < SCI_MAX_PORTS; index++) { - ihost->oem_parameters.ports[index].phy_mask = 0; - } - - /* Initialize all of the phy parameter information. */ - for (index = 0; index < SCI_MAX_PHYS; index++) { - /* Default to 3G (i.e. Gen 2). */ - ihost->user_parameters.phys[index].max_speed_generation = - SCIC_SDS_PARM_GEN2_SPEED; - - /* the frequencies cannot be 0 */ - ihost->user_parameters.phys[index].align_insertion_frequency = 0x7f; - ihost->user_parameters.phys[index].in_connection_align_insertion_frequency = 0xff; - ihost->user_parameters.phys[index].notify_enable_spin_up_insertion_frequency = 0x33; - - /* - * Previous Vitesse based expanders had a arbitration issue that - * is worked around by having the upper 32-bits of SAS address - * with a value greater then the Vitesse company identifier. - * Hence, usage of 0x5FCFFFFF. */ - ihost->oem_parameters.phys[index].sas_address.low = 0x1 + ihost->id; - ihost->oem_parameters.phys[index].sas_address.high = 0x5FCFFFFF; - } - - ihost->user_parameters.stp_inactivity_timeout = 5; - ihost->user_parameters.ssp_inactivity_timeout = 5; - ihost->user_parameters.stp_max_occupancy_timeout = 5; - ihost->user_parameters.ssp_max_occupancy_timeout = 20; - ihost->user_parameters.no_outbound_task_timeout = 2; -} - static void controller_timeout(unsigned long data) { struct sci_timer *tmr = (struct sci_timer *)data; @@ -1753,9 +1681,6 @@ static enum sci_status sci_controller_construct(struct isci_host *ihost, sci_init_timer(&ihost->timer, controller_timeout); - /* Initialize the User and OEM parameters to default values. */ - sci_controller_set_default_config_parameters(ihost); - return sci_controller_reset(ihost); } @@ -1835,27 +1760,6 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version) return 0; } -static enum sci_status sci_oem_parameters_set(struct isci_host *ihost) -{ - u32 state = ihost->sm.current_state_id; - struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); - - if (state == SCIC_RESET || - state == SCIC_INITIALIZING || - state == SCIC_INITIALIZED) { - u8 oem_version = pci_info->orom ? pci_info->orom->hdr.version : - ISCI_ROM_VER_1_0; - - if (sci_oem_parameters_validate(&ihost->oem_parameters, - oem_version)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - return SCI_SUCCESS; - } - - return SCI_FAILURE_INVALID_STATE; -} - static u8 max_spin_up(struct isci_host *ihost) { if (ihost->user_parameters.max_concurr_spinup) @@ -2372,96 +2276,77 @@ static enum sci_status sci_controller_initialize(struct isci_host *ihost) return result; } -static enum sci_status sci_user_parameters_set(struct isci_host *ihost, - struct sci_user_parameters *sci_parms) -{ - u32 state = ihost->sm.current_state_id; - - if (state == SCIC_RESET || - state == SCIC_INITIALIZING || - state == SCIC_INITIALIZED) { - u16 index; - - /* - * Validate the user parameters. If they are not legal, then - * return a failure. - */ - for (index = 0; index < SCI_MAX_PHYS; index++) { - struct sci_phy_user_params *user_phy; - - user_phy = &sci_parms->phys[index]; - - if (!((user_phy->max_speed_generation <= - SCIC_SDS_PARM_MAX_SPEED) && - (user_phy->max_speed_generation > - SCIC_SDS_PARM_NO_SPEED))) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - if (user_phy->in_connection_align_insertion_frequency < - 3) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - if ((user_phy->in_connection_align_insertion_frequency < - 3) || - (user_phy->align_insertion_frequency == 0) || - (user_phy-> - notify_enable_spin_up_insertion_frequency == - 0)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - } - - if ((sci_parms->stp_inactivity_timeout == 0) || - (sci_parms->ssp_inactivity_timeout == 0) || - (sci_parms->stp_max_occupancy_timeout == 0) || - (sci_parms->ssp_max_occupancy_timeout == 0) || - (sci_parms->no_outbound_task_timeout == 0)) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - - memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms)); - - return SCI_SUCCESS; - } - - return SCI_FAILURE_INVALID_STATE; -} - -static int sci_controller_mem_init(struct isci_host *ihost) +static int sci_controller_dma_alloc(struct isci_host *ihost) { struct device *dev = &ihost->pdev->dev; - dma_addr_t dma; size_t size; - int err; + int i; + + /* detect re-initialization */ + if (ihost->completion_queue) + return 0; size = SCU_MAX_COMPLETION_QUEUE_ENTRIES * sizeof(u32); - ihost->completion_queue = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL); + ihost->completion_queue = dmam_alloc_coherent(dev, size, &ihost->cq_dma, + GFP_KERNEL); if (!ihost->completion_queue) return -ENOMEM; - writel(lower_32_bits(dma), &ihost->smu_registers->completion_queue_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->completion_queue_upper); - size = ihost->remote_node_entries * sizeof(union scu_remote_node_context); - ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &dma, + ihost->remote_node_context_table = dmam_alloc_coherent(dev, size, &ihost->rnc_dma, GFP_KERNEL); + if (!ihost->remote_node_context_table) return -ENOMEM; - writel(lower_32_bits(dma), &ihost->smu_registers->remote_node_context_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->remote_node_context_upper); - size = ihost->task_context_entries * sizeof(struct scu_task_context), - ihost->task_context_table = dmam_alloc_coherent(dev, size, &dma, GFP_KERNEL); + ihost->task_context_table = dmam_alloc_coherent(dev, size, &ihost->tc_dma, + GFP_KERNEL); if (!ihost->task_context_table) return -ENOMEM; - ihost->task_context_dma = dma; - writel(lower_32_bits(dma), &ihost->smu_registers->host_task_table_lower); - writel(upper_32_bits(dma), &ihost->smu_registers->host_task_table_upper); + size = SCI_UFI_TOTAL_SIZE; + ihost->ufi_buf = dmam_alloc_coherent(dev, size, &ihost->ufi_dma, GFP_KERNEL); + if (!ihost->ufi_buf) + return -ENOMEM; + + for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { + struct isci_request *ireq; + dma_addr_t dma; + + ireq = dmam_alloc_coherent(dev, sizeof(*ireq), &dma, GFP_KERNEL); + if (!ireq) + return -ENOMEM; + + ireq->tc = &ihost->task_context_table[i]; + ireq->owning_controller = ihost; + spin_lock_init(&ireq->state_lock); + ireq->request_daddr = dma; + ireq->isci_host = ihost; + ihost->reqs[i] = ireq; + } + + return 0; +} + +static int sci_controller_mem_init(struct isci_host *ihost) +{ + int err = sci_controller_dma_alloc(ihost); - err = sci_unsolicited_frame_control_construct(ihost); if (err) return err; + writel(lower_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_lower); + writel(upper_32_bits(ihost->cq_dma), &ihost->smu_registers->completion_queue_upper); + + writel(lower_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_lower); + writel(upper_32_bits(ihost->rnc_dma), &ihost->smu_registers->remote_node_context_upper); + + writel(lower_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_lower); + writel(upper_32_bits(ihost->tc_dma), &ihost->smu_registers->host_task_table_upper); + + sci_unsolicited_frame_control_construct(ihost); + /* * Inform the silicon as to the location of the UF headers and * address table. @@ -2479,19 +2364,20 @@ static int sci_controller_mem_init(struct isci_host *ihost) return 0; } +/** + * isci_host_init - (re-)initialize hardware and internal (private) state + * @ihost: host to init + * + * Any public facing objects (like asd_sas_port, and asd_sas_phys), or + * one-time initialization objects like locks and waitqueues, are + * not touched (they are initialized in isci_host_alloc) + */ int isci_host_init(struct isci_host *ihost) { - int err = 0, i; + int i, err; enum sci_status status; - struct sci_user_parameters sci_user_params; - struct isci_pci_info *pci_info = to_pci_info(ihost->pdev); - - spin_lock_init(&ihost->scic_lock); - init_waitqueue_head(&ihost->eventq); - - status = sci_controller_construct(ihost, scu_base(ihost), - smu_base(ihost)); + status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); if (status != SCI_SUCCESS) { dev_err(&ihost->pdev->dev, "%s: sci_controller_construct failed - status = %x\n", @@ -2500,48 +2386,6 @@ int isci_host_init(struct isci_host *ihost) return -ENODEV; } - ihost->sas_ha.dev = &ihost->pdev->dev; - ihost->sas_ha.lldd_ha = ihost; - - /* - * grab initial values stored in the controller object for OEM and USER - * parameters - */ - isci_user_parameters_get(&sci_user_params); - status = sci_user_parameters_set(ihost, &sci_user_params); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "%s: sci_user_parameters_set failed\n", - __func__); - return -ENODEV; - } - - /* grab any OEM parameters specified in orom */ - if (pci_info->orom) { - status = isci_parse_oem_parameters(&ihost->oem_parameters, - pci_info->orom, - ihost->id); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "parsing firmware oem parameters failed\n"); - return -EINVAL; - } - } - - status = sci_oem_parameters_set(ihost); - if (status != SCI_SUCCESS) { - dev_warn(&ihost->pdev->dev, - "%s: sci_oem_parameters_set failed\n", - __func__); - return -ENODEV; - } - - tasklet_init(&ihost->completion_tasklet, - isci_host_completion_routine, (unsigned long)ihost); - - INIT_LIST_HEAD(&ihost->requests_to_complete); - INIT_LIST_HEAD(&ihost->requests_to_errorback); - spin_lock_irq(&ihost->scic_lock); status = sci_controller_initialize(ihost); spin_unlock_irq(&ihost->scic_lock); @@ -2557,47 +2401,12 @@ int isci_host_init(struct isci_host *ihost) if (err) return err; - for (i = 0; i < SCI_MAX_PORTS; i++) { - struct isci_port *iport = &ihost->ports[i]; - - INIT_LIST_HEAD(&iport->remote_dev_list); - iport->isci_host = ihost; - } - - for (i = 0; i < SCI_MAX_PHYS; i++) - isci_phy_init(&ihost->phys[i], ihost, i); - /* enable sgpio */ writel(1, &ihost->scu_registers->peg0.sgpio.interface_control); for (i = 0; i < isci_gpio_count(ihost); i++) writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); writel(0, &ihost->scu_registers->peg0.sgpio.vendor_specific_code); - for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { - struct isci_remote_device *idev = &ihost->devices[i]; - - INIT_LIST_HEAD(&idev->reqs_in_process); - INIT_LIST_HEAD(&idev->node); - } - - for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { - struct isci_request *ireq; - dma_addr_t dma; - - ireq = dmam_alloc_coherent(&ihost->pdev->dev, - sizeof(struct isci_request), &dma, - GFP_KERNEL); - if (!ireq) - return -ENOMEM; - - ireq->tc = &ihost->task_context_table[i]; - ireq->owning_controller = ihost; - spin_lock_init(&ireq->state_lock); - ireq->request_daddr = dma; - ireq->isci_host = ihost; - ihost->reqs[i] = ireq; - } - return 0; } diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 81485b4a1a5..4695162f406 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -158,13 +158,17 @@ struct isci_host { struct sci_power_control power_control; u8 io_request_sequence[SCI_MAX_IO_REQUESTS]; struct scu_task_context *task_context_table; - dma_addr_t task_context_dma; + dma_addr_t tc_dma; union scu_remote_node_context *remote_node_context_table; + dma_addr_t rnc_dma; u32 *completion_queue; + dma_addr_t cq_dma; u32 completion_queue_get; u32 logical_port_entries; u32 remote_node_entries; u32 task_context_entries; + void *ufi_buf; + dma_addr_t ufi_dma; struct sci_unsolicited_frame_control uf_control; /* phy startup */ @@ -452,36 +456,17 @@ void sci_controller_free_remote_node_context( struct isci_remote_device *idev, u16 node_id); -struct isci_request *sci_request_by_tag(struct isci_host *ihost, - u16 io_tag); - -void sci_controller_power_control_queue_insert( - struct isci_host *ihost, - struct isci_phy *iphy); - -void sci_controller_power_control_queue_remove( - struct isci_host *ihost, - struct isci_phy *iphy); - -void sci_controller_link_up( - struct isci_host *ihost, - struct isci_port *iport, - struct isci_phy *iphy); - -void sci_controller_link_down( - struct isci_host *ihost, - struct isci_port *iport, - struct isci_phy *iphy); - -void sci_controller_remote_device_stopped( - struct isci_host *ihost, - struct isci_remote_device *idev); - -void sci_controller_copy_task_context( - struct isci_host *ihost, - struct isci_request *ireq); - -void sci_controller_register_setup(struct isci_host *ihost); +struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag); +void sci_controller_power_control_queue_insert(struct isci_host *ihost, + struct isci_phy *iphy); +void sci_controller_power_control_queue_remove(struct isci_host *ihost, + struct isci_phy *iphy); +void sci_controller_link_up(struct isci_host *ihost, struct isci_port *iport, + struct isci_phy *iphy); +void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport, + struct isci_phy *iphy); +void sci_controller_remote_device_stopped(struct isci_host *ihost, + struct isci_remote_device *idev); enum sci_status sci_controller_continue_io(struct isci_request *ireq); int isci_host_scan_finished(struct Scsi_Host *, unsigned long); @@ -491,27 +476,9 @@ enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag); void isci_tci_free(struct isci_host *ihost, u16 tci); int isci_host_init(struct isci_host *); - -void isci_host_init_controller_names( - struct isci_host *isci_host, - unsigned int controller_idx); - -void isci_host_deinit( - struct isci_host *); - -void isci_host_port_link_up( - struct isci_host *, - struct isci_port *, - struct isci_phy *); -int isci_host_dev_found(struct domain_device *); - -void isci_host_remote_device_start_complete( - struct isci_host *, - struct isci_remote_device *, - enum sci_status); - -void sci_controller_disable_interrupts( - struct isci_host *ihost); +void isci_host_completion_routine(unsigned long data); +void isci_host_deinit(struct isci_host *); +void sci_controller_disable_interrupts(struct isci_host *ihost); enum sci_status sci_controller_start_io( struct isci_host *ihost, diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 5137db5a5d8..eda43851cc9 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -397,38 +397,203 @@ static int isci_setup_interrupts(struct pci_dev *pdev) return err; } +static void isci_user_parameters_get(struct sci_user_parameters *u) +{ + int i; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + struct sci_phy_user_params *u_phy = &u->phys[i]; + + u_phy->max_speed_generation = phy_gen; + + /* we are not exporting these for now */ + u_phy->align_insertion_frequency = 0x7f; + u_phy->in_connection_align_insertion_frequency = 0xff; + u_phy->notify_enable_spin_up_insertion_frequency = 0x33; + } + + u->stp_inactivity_timeout = stp_inactive_to; + u->ssp_inactivity_timeout = ssp_inactive_to; + u->stp_max_occupancy_timeout = stp_max_occ_to; + u->ssp_max_occupancy_timeout = ssp_max_occ_to; + u->no_outbound_task_timeout = no_outbound_task_to; + u->max_concurr_spinup = max_concurr_spinup; +} + +static enum sci_status sci_user_parameters_set(struct isci_host *ihost, + struct sci_user_parameters *sci_parms) +{ + u16 index; + + /* + * Validate the user parameters. If they are not legal, then + * return a failure. + */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + struct sci_phy_user_params *u; + + u = &sci_parms->phys[index]; + + if (!((u->max_speed_generation <= SCIC_SDS_PARM_MAX_SPEED) && + (u->max_speed_generation > SCIC_SDS_PARM_NO_SPEED))) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + if (u->in_connection_align_insertion_frequency < 3) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + if ((u->in_connection_align_insertion_frequency < 3) || + (u->align_insertion_frequency == 0) || + (u->notify_enable_spin_up_insertion_frequency == 0)) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + + if ((sci_parms->stp_inactivity_timeout == 0) || + (sci_parms->ssp_inactivity_timeout == 0) || + (sci_parms->stp_max_occupancy_timeout == 0) || + (sci_parms->ssp_max_occupancy_timeout == 0) || + (sci_parms->no_outbound_task_timeout == 0)) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + memcpy(&ihost->user_parameters, sci_parms, sizeof(*sci_parms)); + + return SCI_SUCCESS; +} + +static void sci_oem_defaults(struct isci_host *ihost) +{ + /* these defaults are overridden by the platform / firmware */ + struct sci_user_parameters *user = &ihost->user_parameters; + struct sci_oem_params *oem = &ihost->oem_parameters; + int i; + + /* Default to APC mode. */ + oem->controller.mode_type = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; + + /* Default to APC mode. */ + oem->controller.max_concurr_spin_up = 1; + + /* Default to no SSC operation. */ + oem->controller.do_enable_ssc = false; + + /* Default to short cables on all phys. */ + oem->controller.cable_selection_mask = 0; + + /* Initialize all of the port parameter information to narrow ports. */ + for (i = 0; i < SCI_MAX_PORTS; i++) + oem->ports[i].phy_mask = 0; + + /* Initialize all of the phy parameter information. */ + for (i = 0; i < SCI_MAX_PHYS; i++) { + /* Default to 3G (i.e. Gen 2). */ + user->phys[i].max_speed_generation = SCIC_SDS_PARM_GEN2_SPEED; + + /* the frequencies cannot be 0 */ + user->phys[i].align_insertion_frequency = 0x7f; + user->phys[i].in_connection_align_insertion_frequency = 0xff; + user->phys[i].notify_enable_spin_up_insertion_frequency = 0x33; + + /* Previous Vitesse based expanders had a arbitration issue that + * is worked around by having the upper 32-bits of SAS address + * with a value greater then the Vitesse company identifier. + * Hence, usage of 0x5FCFFFFF. + */ + oem->phys[i].sas_address.low = 0x1 + ihost->id; + oem->phys[i].sas_address.high = 0x5FCFFFFF; + } + + user->stp_inactivity_timeout = 5; + user->ssp_inactivity_timeout = 5; + user->stp_max_occupancy_timeout = 5; + user->ssp_max_occupancy_timeout = 20; + user->no_outbound_task_timeout = 2; +} + static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) { - struct isci_host *isci_host; + struct isci_orom *orom = to_pci_info(pdev)->orom; + struct sci_user_parameters sci_user_params; + u8 oem_version = ISCI_ROM_VER_1_0; + struct isci_host *ihost; struct Scsi_Host *shost; - int err; + int err, i; - isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL); - if (!isci_host) + ihost = devm_kzalloc(&pdev->dev, sizeof(*ihost), GFP_KERNEL); + if (!ihost) + return NULL; + + ihost->pdev = pdev; + ihost->id = id; + spin_lock_init(&ihost->scic_lock); + init_waitqueue_head(&ihost->eventq); + ihost->sas_ha.dev = &ihost->pdev->dev; + ihost->sas_ha.lldd_ha = ihost; + tasklet_init(&ihost->completion_tasklet, + isci_host_completion_routine, (unsigned long)ihost); + + /* validate module parameters */ + /* TODO: kill struct sci_user_parameters and reference directly */ + sci_oem_defaults(ihost); + isci_user_parameters_get(&sci_user_params); + if (sci_user_parameters_set(ihost, &sci_user_params)) { + dev_warn(&pdev->dev, + "%s: sci_user_parameters_set failed\n", __func__); + return NULL; + } + + /* sanity check platform (or 'firmware') oem parameters */ + if (orom) { + if (id < 0 || id >= SCI_MAX_CONTROLLERS || id > orom->hdr.num_elements) { + dev_warn(&pdev->dev, "parsing firmware oem parameters failed\n"); + return NULL; + } + ihost->oem_parameters = orom->ctrl[id]; + oem_version = orom->hdr.version; + } + + /* validate oem parameters (platform, firmware, or built-in defaults) */ + if (sci_oem_parameters_validate(&ihost->oem_parameters, oem_version)) { + dev_warn(&pdev->dev, "oem parameter validation failed\n"); return NULL; + } + + INIT_LIST_HEAD(&ihost->requests_to_complete); + INIT_LIST_HEAD(&ihost->requests_to_errorback); + for (i = 0; i < SCI_MAX_PORTS; i++) { + struct isci_port *iport = &ihost->ports[i]; + + INIT_LIST_HEAD(&iport->remote_dev_list); + iport->isci_host = ihost; + } - isci_host->pdev = pdev; - isci_host->id = id; + for (i = 0; i < SCI_MAX_PHYS; i++) + isci_phy_init(&ihost->phys[i], ihost, i); + + for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { + struct isci_remote_device *idev = &ihost->devices[i]; + + INIT_LIST_HEAD(&idev->reqs_in_process); + INIT_LIST_HEAD(&idev->node); + } shost = scsi_host_alloc(&isci_sht, sizeof(void *)); if (!shost) return NULL; - isci_host->shost = shost; + ihost->shost = shost; dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: " "{%s, %s, %s, %s}\n", - (is_cable_select_overridden() ? "* " : ""), isci_host->id, - lookup_cable_names(decode_cable_selection(isci_host, 3)), - lookup_cable_names(decode_cable_selection(isci_host, 2)), - lookup_cable_names(decode_cable_selection(isci_host, 1)), - lookup_cable_names(decode_cable_selection(isci_host, 0))); + (is_cable_select_overridden() ? "* " : ""), ihost->id, + lookup_cable_names(decode_cable_selection(ihost, 3)), + lookup_cable_names(decode_cable_selection(ihost, 2)), + lookup_cable_names(decode_cable_selection(ihost, 1)), + lookup_cable_names(decode_cable_selection(ihost, 0))); - err = isci_host_init(isci_host); + err = isci_host_init(ihost); if (err) goto err_shost; - SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha; - isci_host->sas_ha.core.shost = shost; + SHOST_TO_SAS_HA(shost) = &ihost->sas_ha; + ihost->sas_ha.core.shost = shost; shost->transportt = isci_transport_template; shost->max_id = ~0; @@ -439,11 +604,11 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) if (err) goto err_shost; - err = isci_register_sas_ha(isci_host); + err = isci_register_sas_ha(ihost); if (err) goto err_shost_remove; - return isci_host; + return ihost; err_shost_remove: scsi_remove_host(shost); diff --git a/drivers/scsi/isci/probe_roms.c b/drivers/scsi/isci/probe_roms.c index 9b8117b9d75..4d95654c3fd 100644 --- a/drivers/scsi/isci/probe_roms.c +++ b/drivers/scsi/isci/probe_roms.c @@ -112,18 +112,6 @@ struct isci_orom *isci_request_oprom(struct pci_dev *pdev) return rom; } -enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem, - struct isci_orom *orom, int scu_index) -{ - /* check for valid inputs */ - if (scu_index < 0 || scu_index >= SCI_MAX_CONTROLLERS || - scu_index > orom->hdr.num_elements || !oem) - return -EINVAL; - - *oem = orom->ctrl[scu_index]; - return 0; -} - struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw) { struct isci_orom *orom = NULL, *data; diff --git a/drivers/scsi/isci/probe_roms.h b/drivers/scsi/isci/probe_roms.h index bb0e9d4d97c..e08b578241f 100644 --- a/drivers/scsi/isci/probe_roms.h +++ b/drivers/scsi/isci/probe_roms.h @@ -156,8 +156,6 @@ int sci_oem_parameters_validate(struct sci_oem_params *oem, u8 version); struct isci_orom; struct isci_orom *isci_request_oprom(struct pci_dev *pdev); -enum sci_status isci_parse_oem_parameters(struct sci_oem_params *oem, - struct isci_orom *orom, int scu_index); struct isci_orom *isci_request_firmware(struct pci_dev *pdev, const struct firmware *fw); struct isci_orom *isci_get_efi_var(struct pci_dev *pdev); diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 835ede84887..dcd26eadf86 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -92,11 +92,11 @@ static dma_addr_t to_sgl_element_pair_dma(struct isci_host *ihost, if (idx == 0) { offset = (void *) &ireq->tc->sgl_pair_ab - (void *) &ihost->task_context_table[0]; - return ihost->task_context_dma + offset; + return ihost->tc_dma + offset; } else if (idx == 1) { offset = (void *) &ireq->tc->sgl_pair_cd - (void *) &ihost->task_context_table[0]; - return ihost->task_context_dma + offset; + return ihost->tc_dma + offset; } return sci_io_request_get_dma_addr(ireq, &ireq->sg_table[idx - 2]); diff --git a/drivers/scsi/isci/unsolicited_frame_control.c b/drivers/scsi/isci/unsolicited_frame_control.c index 16f88ab939c..04a6d0d59a2 100644 --- a/drivers/scsi/isci/unsolicited_frame_control.c +++ b/drivers/scsi/isci/unsolicited_frame_control.c @@ -57,31 +57,19 @@ #include "unsolicited_frame_control.h" #include "registers.h" -int sci_unsolicited_frame_control_construct(struct isci_host *ihost) +void sci_unsolicited_frame_control_construct(struct isci_host *ihost) { struct sci_unsolicited_frame_control *uf_control = &ihost->uf_control; struct sci_unsolicited_frame *uf; - u32 buf_len, header_len, i; - dma_addr_t dma; - size_t size; - void *virt; - - /* - * Prepare all of the memory sizes for the UF headers, UF address - * table, and UF buffers themselves. - */ - buf_len = SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE; - header_len = SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header); - size = buf_len + header_len + SCU_MAX_UNSOLICITED_FRAMES * sizeof(uf_control->address_table.array[0]); + dma_addr_t dma = ihost->ufi_dma; + void *virt = ihost->ufi_buf; + int i; /* * The Unsolicited Frame buffers are set at the start of the UF * memory descriptor entry. The headers and address table will be * placed after the buffers. */ - virt = dmam_alloc_coherent(&ihost->pdev->dev, size, &dma, GFP_KERNEL); - if (!virt) - return -ENOMEM; /* * Program the location of the UF header table into the SCU. @@ -93,8 +81,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) * headers, since we program the UF address table pointers to * NULL. */ - uf_control->headers.physical_address = dma + buf_len; - uf_control->headers.array = virt + buf_len; + uf_control->headers.physical_address = dma + SCI_UFI_BUF_SIZE; + uf_control->headers.array = virt + SCI_UFI_BUF_SIZE; /* * Program the location of the UF address table into the SCU. @@ -103,8 +91,8 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) * byte boundary already due to above programming headers being on a * 64-bit boundary and headers are on a 64-bytes in size. */ - uf_control->address_table.physical_address = dma + buf_len + header_len; - uf_control->address_table.array = virt + buf_len + header_len; + uf_control->address_table.physical_address = dma + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE; + uf_control->address_table.array = virt + SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE; uf_control->get = 0; /* @@ -135,8 +123,6 @@ int sci_unsolicited_frame_control_construct(struct isci_host *ihost) virt += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; dma += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; } - - return 0; } enum sci_status sci_unsolicited_frame_control_get_header(struct sci_unsolicited_frame_control *uf_control, diff --git a/drivers/scsi/isci/unsolicited_frame_control.h b/drivers/scsi/isci/unsolicited_frame_control.h index 75d896686f5..1bc551ec611 100644 --- a/drivers/scsi/isci/unsolicited_frame_control.h +++ b/drivers/scsi/isci/unsolicited_frame_control.h @@ -257,9 +257,13 @@ struct sci_unsolicited_frame_control { }; +#define SCI_UFI_BUF_SIZE (SCU_MAX_UNSOLICITED_FRAMES * SCU_UNSOLICITED_FRAME_BUFFER_SIZE) +#define SCI_UFI_HDR_SIZE (SCU_MAX_UNSOLICITED_FRAMES * sizeof(struct scu_unsolicited_frame_header)) +#define SCI_UFI_TOTAL_SIZE (SCI_UFI_BUF_SIZE + SCI_UFI_HDR_SIZE + SCU_MAX_UNSOLICITED_FRAMES * sizeof(u64)) + struct isci_host; -int sci_unsolicited_frame_control_construct(struct isci_host *ihost); +void sci_unsolicited_frame_control_construct(struct isci_host *ihost); enum sci_status sci_unsolicited_frame_control_get_header( struct sci_unsolicited_frame_control *uf_control, -- cgit v1.2.3-70-g09d2 From eb608c3cb3f0a6b99252ea6a69fc0d2bbecf1f4f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 23 Feb 2012 01:12:10 -0800 Subject: isci: fix controller stop 1/ notify waiters when controller stop completes (fixes 10 second stall unloading the driver) 2/ make sure phy stop is after port and device stop Cc: Richard Boyd Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 99 ++++++++++++++++++++++++++---------------------- drivers/scsi/isci/host.h | 8 +--- 2 files changed, 55 insertions(+), 52 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 0fe372f9328..95c3da66ea4 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1046,7 +1046,7 @@ void isci_host_scan_start(struct Scsi_Host *shost) spin_unlock_irq(&ihost->scic_lock); } -static void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status) +static void isci_host_stop_complete(struct isci_host *ihost) { sci_controller_disable_interrupts(ihost); clear_bit(IHOST_STOP_PENDING, &ihost->flags); @@ -1232,7 +1232,7 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) switch (ihost->sm.current_state_id) { case SCIC_RESET: case SCIC_READY: - case SCIC_STOPPED: + case SCIC_STOPPING: case SCIC_FAILED: /* * The reset operation is not a graceful cleanup, just @@ -1247,6 +1247,44 @@ static enum sci_status sci_controller_reset(struct isci_host *ihost) } } +static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) +{ + u32 index; + enum sci_status status; + enum sci_status phy_status; + + status = SCI_SUCCESS; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + phy_status = sci_phy_stop(&ihost->phys[index]); + + if (phy_status != SCI_SUCCESS && + phy_status != SCI_FAILURE_INVALID_STATE) { + status = SCI_FAILURE; + + dev_warn(&ihost->pdev->dev, + "%s: Controller stop operation failed to stop " + "phy %d because of status %d.\n", + __func__, + ihost->phys[index].phy_index, phy_status); + } + } + + return status; +} + + +/** + * isci_host_deinit - shutdown frame reception and dma + * @ihost: host to take down + * + * This is called in either the driver shutdown or the suspend path. In + * the shutdown case libsas went through port teardown and normal device + * removal (i.e. physical links stayed up to service scsi_device removal + * commands). In the suspend case we disable the hardware without + * notifying libsas of the link down events since we want libsas to + * remember the domain across the suspend/resume cycle + */ void isci_host_deinit(struct isci_host *ihost) { int i; @@ -1255,16 +1293,6 @@ void isci_host_deinit(struct isci_host *ihost) for (i = 0; i < isci_gpio_count(ihost); i++) writel(SGPIO_HW_CONTROL, &ihost->scu_registers->peg0.sgpio.output_data_select[i]); - for (i = 0; i < SCI_MAX_PORTS; i++) { - struct isci_port *iport = &ihost->ports[i]; - struct isci_remote_device *idev, *d; - - list_for_each_entry_safe(idev, d, &iport->remote_dev_list, node) { - if (test_bit(IDEV_ALLOCATED, &idev->flags)) - isci_remote_device_stop(ihost, idev); - } - } - set_bit(IHOST_STOP_PENDING, &ihost->flags); spin_lock_irq(&ihost->scic_lock); @@ -1273,6 +1301,13 @@ void isci_host_deinit(struct isci_host *ihost) wait_for_stop(ihost); + /* phy stop is after controller stop to allow port and device to + * go idle before shutting down the phys, but the expectation is + * that i/o has been shut off well before we reach this + * function. + */ + sci_controller_stop_phys(ihost); + /* disable sgpio: where the above wait should give time for the * enclosure to sample the gpios going inactive */ @@ -1476,32 +1511,6 @@ static void sci_controller_ready_state_exit(struct sci_base_state_machine *sm) sci_controller_set_interrupt_coalescence(ihost, 0, 0); } -static enum sci_status sci_controller_stop_phys(struct isci_host *ihost) -{ - u32 index; - enum sci_status status; - enum sci_status phy_status; - - status = SCI_SUCCESS; - - for (index = 0; index < SCI_MAX_PHYS; index++) { - phy_status = sci_phy_stop(&ihost->phys[index]); - - if (phy_status != SCI_SUCCESS && - phy_status != SCI_FAILURE_INVALID_STATE) { - status = SCI_FAILURE; - - dev_warn(&ihost->pdev->dev, - "%s: Controller stop operation failed to stop " - "phy %d because of status %d.\n", - __func__, - ihost->phys[index].phy_index, phy_status); - } - } - - return status; -} - static enum sci_status sci_controller_stop_ports(struct isci_host *ihost) { u32 index; @@ -1561,10 +1570,11 @@ static void sci_controller_stopping_state_enter(struct sci_base_state_machine *s { struct isci_host *ihost = container_of(sm, typeof(*ihost), sm); - /* Stop all of the components for this controller */ - sci_controller_stop_phys(ihost); - sci_controller_stop_ports(ihost); sci_controller_stop_devices(ihost); + sci_controller_stop_ports(ihost); + + if (!sci_controller_has_remote_devices_stopping(ihost)) + isci_host_stop_complete(ihost); } static void sci_controller_stopping_state_exit(struct sci_base_state_machine *sm) @@ -1621,7 +1631,6 @@ static const struct sci_base_state sci_controller_state_table[] = { .enter_state = sci_controller_stopping_state_enter, .exit_state = sci_controller_stopping_state_exit, }, - [SCIC_STOPPED] = {}, [SCIC_FAILED] = {} }; @@ -1641,7 +1650,7 @@ static void controller_timeout(unsigned long data) sci_controller_transition_to_ready(ihost, SCI_FAILURE_TIMEOUT); else if (sm->current_state_id == SCIC_STOPPING) { sci_change_state(sm, SCIC_FAILED); - isci_host_stop_complete(ihost, SCI_FAILURE_TIMEOUT); + isci_host_stop_complete(ihost); } else /* / @todo Now what do we want to do in this case? */ dev_err(&ihost->pdev->dev, "%s: Controller timer fired when controller was not " @@ -2452,7 +2461,7 @@ void sci_controller_link_down(struct isci_host *ihost, struct isci_port *iport, } } -static bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) +bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost) { u32 index; @@ -2478,7 +2487,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost, } if (!sci_controller_has_remote_devices_stopping(ihost)) - sci_change_state(&ihost->sm, SCIC_STOPPED); + isci_host_stop_complete(ihost); } void sci_controller_post_request(struct isci_host *ihost, u32 request) diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 4695162f406..a89c0e3c5a1 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -276,13 +276,6 @@ enum sci_controller_states { */ SCIC_STOPPING, - /** - * This state indicates that the controller has successfully been stopped. - * In this state no new IO operations are permitted. - * This state is entered from the STOPPING state. - */ - SCIC_STOPPED, - /** * This state indicates that the controller could not successfully be * initialized. In this state no new IO operations are permitted. @@ -479,6 +472,7 @@ int isci_host_init(struct isci_host *); void isci_host_completion_routine(unsigned long data); void isci_host_deinit(struct isci_host *); void sci_controller_disable_interrupts(struct isci_host *ihost); +bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost); enum sci_status sci_controller_start_io( struct isci_host *ihost, -- cgit v1.2.3-70-g09d2 From 50a92d93148ec073efd2456b007e04ecae452086 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Feb 2012 01:07:56 -0800 Subject: isci: fix 'link-up' events occur after 'start-complete' The call to wait_for_start() is meant to ensure that all links have been given a chance to come up before letting the kernel proceed with probing. However, the implementation is not correctly syncing with the port configuration agent. In the MPC case the ports are hard-coded, in the APC case we need to wait for the port-configuration to form ports from the started phys. Towards that end increase the timeout for the APC agent to form ports, and delay start complete until all phys are out of link-training. Cc: Cc: Richard Boyd Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 67 ++++++++++++++++++++++------------------- drivers/scsi/isci/host.h | 3 ++ drivers/scsi/isci/port_config.c | 18 ++++++----- 3 files changed, 50 insertions(+), 38 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 95c3da66ea4..577a8369274 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -816,7 +816,7 @@ static void sci_controller_initialize_unsolicited_frame_queue(struct isci_host * &ihost->scu_registers->sdma.unsolicited_frame_put_pointer); } -static void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status) +void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status) { if (ihost->sm.current_state_id == SCIC_STARTING) { /* @@ -843,6 +843,7 @@ static bool is_phy_starting(struct isci_phy *iphy) case SCI_PHY_SUB_AWAIT_SATA_POWER: case SCI_PHY_SUB_AWAIT_SATA_PHY_EN: case SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: + case SCI_PHY_SUB_AWAIT_OSSP_EN: case SCI_PHY_SUB_AWAIT_SIG_FIS_UF: case SCI_PHY_SUB_FINAL: return true; @@ -851,6 +852,39 @@ static bool is_phy_starting(struct isci_phy *iphy) } } +bool is_controller_start_complete(struct isci_host *ihost) +{ + int i; + + for (i = 0; i < SCI_MAX_PHYS; i++) { + struct isci_phy *iphy = &ihost->phys[i]; + u32 state = iphy->sm.current_state_id; + + /* in apc mode we need to check every phy, in + * mpc mode we only need to check phys that have + * been configured into a port + */ + if (is_port_config_apc(ihost)) + /* pass */; + else if (!phy_get_non_dummy_port(iphy)) + continue; + + /* The controller start operation is complete iff: + * - all links have been given an opportunity to start + * - have no indication of a connected device + * - have an indication of a connected device and it has + * finished the link training process. + */ + if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) || + (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) || + (iphy->is_in_link_training == true && is_phy_starting(iphy)) || + (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask)) + return false; + } + + return true; +} + /** * sci_controller_start_next_phy - start phy * @scic: controller @@ -871,36 +905,7 @@ static enum sci_status sci_controller_start_next_phy(struct isci_host *ihost) return status; if (ihost->next_phy_to_start >= SCI_MAX_PHYS) { - bool is_controller_start_complete = true; - u32 state; - u8 index; - - for (index = 0; index < SCI_MAX_PHYS; index++) { - iphy = &ihost->phys[index]; - state = iphy->sm.current_state_id; - - if (!phy_get_non_dummy_port(iphy)) - continue; - - /* The controller start operation is complete iff: - * - all links have been given an opportunity to start - * - have no indication of a connected device - * - have an indication of a connected device and it has - * finished the link training process. - */ - if ((iphy->is_in_link_training == false && state == SCI_PHY_INITIAL) || - (iphy->is_in_link_training == false && state == SCI_PHY_STOPPED) || - (iphy->is_in_link_training == true && is_phy_starting(iphy)) || - (ihost->port_agent.phy_ready_mask != ihost->port_agent.phy_configured_mask)) { - is_controller_start_complete = false; - break; - } - } - - /* - * The controller has successfully finished the start process. - * Inform the SCI Core user and transition to the READY state. */ - if (is_controller_start_complete == true) { + if (is_controller_start_complete(ihost)) { sci_controller_transition_to_ready(ihost, SCI_SUCCESS); sci_del_timer(&ihost->phy_timer); ihost->phy_startup_timer_pending = false; diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index a89c0e3c5a1..9dc910b9d92 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -109,6 +109,8 @@ struct sci_port_configuration_agent; typedef void (*port_config_fn)(struct isci_host *, struct sci_port_configuration_agent *, struct isci_port *, struct isci_phy *); +bool is_port_config_apc(struct isci_host *ihost); +bool is_controller_start_complete(struct isci_host *ihost); struct sci_port_configuration_agent { u16 phy_configured_mask; @@ -473,6 +475,7 @@ void isci_host_completion_routine(unsigned long data); void isci_host_deinit(struct isci_host *); void sci_controller_disable_interrupts(struct isci_host *ihost); bool sci_controller_has_remote_devices_stopping(struct isci_host *ihost); +void sci_controller_transition_to_ready(struct isci_host *ihost, enum sci_status status); enum sci_status sci_controller_start_io( struct isci_host *ihost, diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 6d1e9544cbe..cd962da4a57 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -57,7 +57,7 @@ #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10) #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10) -#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (250) +#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000) enum SCIC_SDS_APC_ACTIVITY { SCIC_SDS_APC_SKIP_PHY, @@ -472,13 +472,9 @@ sci_apc_agent_validate_phy_configuration(struct isci_host *ihost, * down event or a link up event where we can not yet tell to which a phy * belongs. */ -static void sci_apc_agent_start_timer( - struct sci_port_configuration_agent *port_agent, - u32 timeout) +static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent, + u32 timeout) { - if (port_agent->timer_pending) - sci_del_timer(&port_agent->timer); - port_agent->timer_pending = true; sci_mod_timer(&port_agent->timer, timeout); } @@ -697,6 +693,9 @@ static void apc_agent_timeout(unsigned long data) &ihost->phys[index], false); } + if (is_controller_start_complete(ihost)) + sci_controller_transition_to_ready(ihost, SCI_SUCCESS); + done: spin_unlock_irqrestore(&ihost->scic_lock, flags); } @@ -732,6 +731,11 @@ void sci_port_configuration_agent_construct( } } +bool is_port_config_apc(struct isci_host *ihost) +{ + return ihost->port_agent.link_up_handler == sci_apc_agent_link_up; +} + enum sci_status sci_port_configuration_agent_initialize( struct isci_host *ihost, struct sci_port_configuration_agent *port_agent) -- cgit v1.2.3-70-g09d2 From 2396a2650a5a39634e3ad6b29e1104944e5ab88f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 1 Mar 2012 17:06:24 -0800 Subject: isci: fix interrupt disable There is a (dubious?) lost irq workaround in sci_controller_isr() that effectively nullifies attempts to disable interrupts. Until the workaround can be re-evaluated add some infrastructure to prevent the interrupt handler from inadvertantly re-enabling interrupts. The failure mode was interrupts continuing to run after the driver had been removed and its iomappings torn down. Reported-by: Jacek Danecki Tested-by: Jacek Danecki [richard: clear remaining interrupts at the end of reset] Acked-by: Richard Boyd Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 39 ++++++++++++++++++++++++++------------- drivers/scsi/isci/host.h | 1 + 2 files changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 577a8369274..5832b13e7b0 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -192,22 +192,27 @@ static bool sci_controller_completion_queue_has_entries(struct isci_host *ihost) static bool sci_controller_isr(struct isci_host *ihost) { - if (sci_controller_completion_queue_has_entries(ihost)) { + if (sci_controller_completion_queue_has_entries(ihost)) return true; - } else { - /* - * we have a spurious interrupt it could be that we have already - * emptied the completion queue from a previous interrupt */ - writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); - /* - * There is a race in the hardware that could cause us not to be notified - * of an interrupt completion if we do not take this step. We will mask - * then unmask the interrupts so if there is another interrupt pending - * the clearing of the interrupt source we get the next interrupt message. */ + /* we have a spurious interrupt it could be that we have already + * emptied the completion queue from a previous interrupt + * FIXME: really!? + */ + writel(SMU_ISR_COMPLETION, &ihost->smu_registers->interrupt_status); + + /* There is a race in the hardware that could cause us not to be + * notified of an interrupt completion if we do not take this + * step. We will mask then unmask the interrupts so if there is + * another interrupt pending the clearing of the interrupt + * source we get the next interrupt message. + */ + spin_lock(&ihost->scic_lock); + if (test_bit(IHOST_IRQ_ENABLED, &ihost->flags)) { writel(0xFF000000, &ihost->smu_registers->interrupt_mask); writel(0, &ihost->smu_registers->interrupt_mask); } + spin_unlock(&ihost->scic_lock); return false; } @@ -698,14 +703,15 @@ static u32 sci_controller_get_suggested_start_timeout(struct isci_host *ihost) static void sci_controller_enable_interrupts(struct isci_host *ihost) { - BUG_ON(ihost->smu_registers == NULL); + set_bit(IHOST_IRQ_ENABLED, &ihost->flags); writel(0, &ihost->smu_registers->interrupt_mask); } void sci_controller_disable_interrupts(struct isci_host *ihost) { - BUG_ON(ihost->smu_registers == NULL); + clear_bit(IHOST_IRQ_ENABLED, &ihost->flags); writel(0xffffffff, &ihost->smu_registers->interrupt_mask); + readl(&ihost->smu_registers->interrupt_mask); /* flush */ } static void sci_controller_enable_port_task_scheduler(struct isci_host *ihost) @@ -1318,7 +1324,9 @@ void isci_host_deinit(struct isci_host *ihost) */ writel(0, &ihost->scu_registers->peg0.sgpio.interface_control); + spin_lock_irq(&ihost->scic_lock); sci_controller_reset(ihost); + spin_unlock_irq(&ihost->scic_lock); /* Cancel any/all outstanding port timers */ for (i = 0; i < ihost->logical_port_entries; i++) { @@ -1605,6 +1613,9 @@ static void sci_controller_reset_hardware(struct isci_host *ihost) /* The write to the UFQGP clears the UFQPR */ writel(0, &ihost->scu_registers->sdma.unsolicited_frame_get_pointer); + + /* clear all interrupts */ + writel(~SMU_INTERRUPT_STATUS_RESERVED_MASK, &ihost->smu_registers->interrupt_status); } static void sci_controller_resetting_state_enter(struct sci_base_state_machine *sm) @@ -2391,7 +2402,9 @@ int isci_host_init(struct isci_host *ihost) int i, err; enum sci_status status; + spin_lock_irq(&ihost->scic_lock); status = sci_controller_construct(ihost, scu_base(ihost), smu_base(ihost)); + spin_unlock_irq(&ihost->scic_lock); if (status != SCI_SUCCESS) { dev_err(&ihost->pdev->dev, "%s: sci_controller_construct failed - status = %x\n", diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 9dc910b9d92..9701c1d673b 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -200,6 +200,7 @@ struct isci_host { struct pci_dev *pdev; #define IHOST_START_PENDING 0 #define IHOST_STOP_PENDING 1 + #define IHOST_IRQ_ENABLED 2 unsigned long flags; wait_queue_head_t eventq; struct Scsi_Host *shost; -- cgit v1.2.3-70-g09d2 From d1dc5e2d21a55538167d7ce82aa147c91c5e6788 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 25 Feb 2012 14:29:49 -0800 Subject: isci: kill isci_host.shost We can retrieve the shost from the sas_ha like the rest of libsas and drop this out of our local data structure. Acked-by: Jacek Danecki Signed-off-by: Dan Williams --- drivers/scsi/isci/host.h | 6 +++++- drivers/scsi/isci/init.c | 16 +++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 9701c1d673b..7272a0a375f 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -203,7 +203,6 @@ struct isci_host { #define IHOST_IRQ_ENABLED 2 unsigned long flags; wait_queue_head_t eventq; - struct Scsi_Host *shost; struct tasklet_struct completion_tasklet; struct list_head requests_to_complete; struct list_head requests_to_errorback; @@ -308,6 +307,11 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) return pci_get_drvdata(pdev); } +static inline struct Scsi_Host *to_shost(struct isci_host *ihost) +{ + return ihost->sas_ha.core.shost; +} + #define for_each_isci_host(id, ihost, pdev) \ for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \ id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \ diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index eda43851cc9..fdae42f572b 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -271,13 +271,12 @@ static void isci_unregister(struct isci_host *isci_host) if (!isci_host) return; - shost = isci_host->shost; - sas_unregister_ha(&isci_host->sas_ha); - sas_remove_host(isci_host->shost); - scsi_remove_host(isci_host->shost); - scsi_host_put(isci_host->shost); + shost = to_shost(isci_host); + sas_remove_host(shost); + scsi_remove_host(shost); + scsi_host_put(shost); } static int __devinit isci_pci_init(struct pci_dev *pdev) @@ -578,7 +577,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) shost = scsi_host_alloc(&isci_sht, sizeof(void *)); if (!shost) return NULL; - ihost->shost = shost; dev_info(&pdev->dev, "%sSCU controller %d: phy 3-0 cables: " "{%s, %s, %s, %s}\n", @@ -690,11 +688,11 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic pci_info->hosts[i] = h; /* turn on DIF support */ - scsi_host_set_prot(h->shost, + scsi_host_set_prot(to_shost(h), SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | SHOST_DIF_TYPE3_PROTECTION); - scsi_host_set_guard(h->shost, SHOST_DIX_GUARD_CRC); + scsi_host_set_guard(to_shost(h), SHOST_DIX_GUARD_CRC); } err = isci_setup_interrupts(pdev); @@ -702,7 +700,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic goto err_host_alloc; for_each_isci_host(i, isci_host, pdev) - scsi_scan_host(isci_host->shost); + scsi_scan_host(to_shost(isci_host)); return 0; -- cgit v1.2.3-70-g09d2 From 6119908f0fe3737bba2f64eff70599c3e41d522e Mon Sep 17 00:00:00 2001 From: Andrzej Jakowski Date: Thu, 8 Mar 2012 19:38:50 +0000 Subject: isci: Changes in COMSAS timings enabling ISCI to detect buggy disc drives. This patch extends timings in COMSAS signaling, so ISCI can detect disc drives having issues to send COMSAS in correct time frame. Signed-off-by: Andrzej Jakowski Signed-off-by: Dan Williams --- drivers/scsi/isci/phy.c | 55 +++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/isci/registers.h | 8 +++++++ 2 files changed, 63 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 474330fdbe1..85b26ac9074 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -668,6 +668,19 @@ static const char *phy_event_name(u32 event_code) phy_to_host(iphy)->id, iphy->phy_index, \ phy_state_name(state), phy_event_name(code), code) + +void scu_link_layer_set_txcomsas_timeout(struct isci_phy *iphy, u32 timeout) +{ + u32 val; + + /* Extend timeout */ + val = readl(&iphy->link_layer_registers->transmit_comsas_signal); + val &= ~SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK); + val |= SCU_SAS_LLTXCOMSAS_GEN_VAL(NEGTIME, timeout); + + writel(val, &iphy->link_layer_registers->transmit_comsas_signal); +} + enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) { enum sci_phy_states state = iphy->sm.current_state_id; @@ -683,6 +696,13 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); iphy->is_in_link_training = true; break; + case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; default: phy_event_dbg(iphy, state, event_code); return SCI_FAILURE; @@ -717,9 +737,19 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; + case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend the timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; default: phy_event_warn(iphy, state, event_code); return SCI_FAILURE; @@ -740,7 +770,14 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) sci_phy_start_sata_link_training(iphy); break; case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + /* Extend the timeout value */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED); + + /* Start the oob/sn state machine over again */ + sci_change_state(&iphy->sm, SCI_PHY_STARTING); + break; case SCU_EVENT_LINK_FAILURE: + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); case SCU_EVENT_HARD_RESET_RECEIVED: /* Start the oob/sn state machine over again */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); @@ -753,6 +790,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SAS_POWER: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -764,6 +804,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SATA_POWER: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -788,6 +831,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_SUB_AWAIT_SATA_PHY_EN: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -836,6 +882,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) SCI_PHY_SUB_AWAIT_SIG_FIS_UF); break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -859,6 +908,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) break; case SCU_EVENT_LINK_FAILURE: + /* Change the timeout value to default */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; @@ -871,6 +923,9 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code) case SCI_PHY_READY: switch (scu_get_event_code(event_code)) { case SCU_EVENT_LINK_FAILURE: + /* Set default timeout */ + scu_link_layer_set_txcomsas_timeout(iphy, SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT); + /* Link failure change state back to the starting state */ sci_change_state(&iphy->sm, SCI_PHY_STARTING); break; diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h index 7eb0ccd45fe..97f3ceb8d72 100644 --- a/drivers/scsi/isci/registers.h +++ b/drivers/scsi/isci/registers.h @@ -1239,6 +1239,14 @@ struct scu_transport_layer_registers { #define SCU_SAS_LLCTL_GEN_BIT(name) \ SCU_GEN_BIT(SCU_SAS_LINK_LAYER_CONTROL_ ## name) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_DEFAULT (0xF0) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_EXTENDED (0x1FF) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_SHIFT (0) +#define SCU_SAS_LINK_LAYER_TXCOMSAS_NEGTIME_MASK (0x3FF) + +#define SCU_SAS_LLTXCOMSAS_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SAS_LINK_LAYER_TXCOMSAS_ ## name, value) + /* #define SCU_FRXHECR_DCNT_OFFSET 0x00B0 */ #define SCU_PSZGCR_OFFSET 0x00E4 -- cgit v1.2.3-70-g09d2 From 08e73be56b6b2e5459638481a54b755ed562ada8 Mon Sep 17 00:00:00 2001 From: Maciej Trela Date: Mon, 12 Mar 2012 23:29:30 +0000 Subject: isci: enable BCN in sci_port_add_phy() Ensure we enable receiving BCN's from the hardware when adding phy to isci_port. Otherwise if we get BCN before the port is created we won't see any BCN Signed-off-by: Maciej Trela Reported-by: Richard Boyd Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 6ef4bd910f3..0a3aec11809 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -1199,6 +1199,8 @@ enum sci_status sci_port_add_phy(struct isci_port *iport, enum sci_status status; enum sci_port_states state; + sci_port_bcn_enable(iport); + state = iport->sm.current_state_id; switch (state) { case SCI_PORT_STOPPED: { -- cgit v1.2.3-70-g09d2 From fc25f79af321c01a739150ba2c09435cf977a63d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 30 Apr 2012 11:57:44 -0700 Subject: isci: fix oem parameter validation on single controller skus OEM parameters [1] are parsed from the platform option-rom / efi driver. By default the driver was validating the parameters for the dual-controller case, but in single-controller case only the first set of parameters may be valid. Limit the validation to the number of actual controllers detected otherwise the driver may fail to parse the valid parameters leading to driver-load or runtime failures. [1] the platform specific set of phy address, configuration,and analog tuning values [stable v3.0+] Cc: Reported-by: Dave Jiang Tested-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/scsi/isci/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index fdae42f572b..9e1c83e425e 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -639,7 +639,7 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic if (!orom) orom = isci_request_oprom(pdev); - for (i = 0; orom && i < ARRAY_SIZE(orom->ctrl); i++) { + for (i = 0; orom && i < num_controllers(pdev); i++) { if (sci_oem_parameters_validate(&orom->ctrl[i], orom->hdr.version)) { dev_warn(&pdev->dev, -- cgit v1.2.3-70-g09d2 From 6f48844e3f16b7d8a1f9a1a11bd9a11089a5292f Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:48 -0800 Subject: isci: Manage the link layer hang detect timer for RNC suspensions. For STP devices under certain protocol conditions, an RNC will not suspend until the current transfer state is broken with a SYNC/ESC sequence from the SCU. The SYNC/ESC driven by expiration of the SCU link layer hang detect timer, which has too small a dynamic range to support slow SATA devices, so normally it is disabled. This change enables the timer with the minimum period at the point when the suspension is requested. Note that there is potential collateral damage to other open connections to slow SATA devices on the same port, since there is no alternative but to enable the LLHANG timer on every phy in the port for the current suspension request - there is no way to tell on which phy the RNC in question is currently active. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 26 +++++++++++++++++++++++++- drivers/scsi/isci/port.h | 5 +++++ drivers/scsi/isci/remote_device.h | 7 +++++++ drivers/scsi/isci/remote_node_context.c | 15 ++++++++++++++- 4 files changed, 51 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 0a3aec11809..ed206c5a00a 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -1548,6 +1548,29 @@ static void sci_port_failed_state_enter(struct sci_base_state_machine *sm) isci_port_hard_reset_complete(iport, SCI_FAILURE_TIMEOUT); } +void sci_port_set_hang_detection_timeout(struct isci_port *iport, u32 timeout) +{ + int phy_index; + u32 phy_mask = iport->active_phy_mask; + + if (timeout) + ++iport->hang_detect_users; + else if (iport->hang_detect_users > 1) + --iport->hang_detect_users; + else + iport->hang_detect_users = 0; + + if (timeout || (iport->hang_detect_users == 0)) { + for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) { + if ((phy_mask >> phy_index) & 1) { + writel(timeout, + &iport->phy_table[phy_index] + ->link_layer_registers + ->link_layer_hang_detection_timeout); + } + } + } +} /* --------------------------------------------------------------------------- */ static const struct sci_base_state sci_port_state_table[] = { @@ -1596,6 +1619,7 @@ void sci_port_construct(struct isci_port *iport, u8 index, iport->started_request_count = 0; iport->assigned_device_count = 0; + iport->hang_detect_users = 0; iport->reserved_rni = SCU_DUMMY_INDEX; iport->reserved_tag = SCI_CONTROLLER_INVALID_IO_TAG; @@ -1733,7 +1757,7 @@ void isci_port_formed(struct asd_sas_phy *phy) struct isci_host *ihost = phy->ha->lldd_ha; struct isci_phy *iphy = to_iphy(phy); struct asd_sas_port *port = phy->port; - struct isci_port *iport; + struct isci_port *iport = NULL; unsigned long flags; int i; diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h index f8bd1e8dbfe..861e8f72811 100644 --- a/drivers/scsi/isci/port.h +++ b/drivers/scsi/isci/port.h @@ -111,6 +111,7 @@ struct isci_port { u16 reserved_tag; u32 started_request_count; u32 assigned_device_count; + u32 hang_detect_users; u32 not_ready_reason; struct isci_phy *phy_table[SCI_MAX_PHYS]; struct isci_host *owning_controller; @@ -269,6 +270,10 @@ void sci_port_get_attached_sas_address( struct isci_port *iport, struct sci_sas_address *sas_address); +void sci_port_set_hang_detection_timeout( + struct isci_port *isci_port, + u32 timeout); + void isci_port_formed(struct asd_sas_phy *); void isci_port_deformed(struct asd_sas_phy *); diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 4a67ff0eb94..4850b58edbe 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -301,6 +301,13 @@ static inline void sci_remote_device_decrement_request_count(struct isci_remote_ idev->started_request_count--; } +static inline void isci_dev_set_hang_detection_timeout( + struct isci_remote_device *idev, + u32 timeout) +{ + sci_port_set_hang_detection_timeout(idev->owning_port, timeout); +} + enum sci_status sci_remote_device_frame_handler( struct isci_remote_device *idev, u32 frame_index); diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 8ce5a35891e..3a55ba66b8a 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -316,6 +316,15 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ sci_remote_node_context_continue_state_transitions(rnc); } +static void sci_remote_node_context_await_suspend_state_exit( + struct sci_base_state_machine *sm) +{ + struct sci_remote_node_context *rnc + = container_of(sm, typeof(*rnc), sm); + + isci_dev_set_hang_detection_timeout(rnc_to_dev(rnc), 0); +} + static const struct sci_base_state sci_remote_node_context_state_table[] = { [SCI_RNC_INITIAL] = { .enter_state = sci_remote_node_context_initial_state_enter, @@ -338,7 +347,9 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = { [SCI_RNC_TX_RX_SUSPENDED] = { .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, }, - [SCI_RNC_AWAIT_SUSPENSION] = { }, + [SCI_RNC_AWAIT_SUSPENSION] = { + .exit_state = sci_remote_node_context_await_suspend_state_exit, + }, }; void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, @@ -513,6 +524,8 @@ enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context * if (suspend_type == SCI_SOFTWARE_SUSPENSION) { sci_remote_device_post_request(rnc_to_dev(sci_rnc), SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX); + isci_dev_set_hang_detection_timeout(rnc_to_dev(sci_rnc), + 0x00000001); } sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION); -- cgit v1.2.3-70-g09d2 From 56d7c013e714c6feab2ab5ac854808e29048b069 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:49 -0800 Subject: isci: Fixed bug in resumption from RNC Tx/Rx suspend state. The resumption from the Tx/Rx suspended state should work the same as the Tx suspended state. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_node_context.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 3a55ba66b8a..3e849752bff 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -557,10 +557,16 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s sci_rnc->user_callback = cb_fn; sci_rnc->user_cookie = cb_p; return SCI_SUCCESS; - case SCI_RNC_TX_SUSPENDED: { + case SCI_RNC_TX_SUSPENDED: + case SCI_RNC_TX_RX_SUSPENDED: { struct isci_remote_device *idev = rnc_to_dev(sci_rnc); struct domain_device *dev = idev->domain_dev; + /* If this is an expander attached SATA device we must + * invalidate and repost the RNC since this is the only way + * to clear the TCi to NCQ tag mapping table for the RNi. + * All other device types we can just resume. + */ sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); if (dev_is_sata(dev) && dev->parent) @@ -569,10 +575,6 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); return SCI_SUCCESS; } - case SCI_RNC_TX_RX_SUSPENDED: - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); - return SCI_FAILURE_INVALID_STATE; case SCI_RNC_AWAIT_SUSPENSION: sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); return SCI_SUCCESS; -- cgit v1.2.3-70-g09d2 From ac78ed0f78eae5c3c918e132b5e2029cdc4fdedc Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:50 -0800 Subject: isci: Handle all suspending TC completions Add comprehensive decode for all TC completions that generate RNC suspensions. Note that this commit also removes unconditional resumptions of ATAPI devices when in the SCI_STP_DEV_ATAPI_ERROR state, and STP devices when in the SCI_STP_DEV_IDLE state. This is because the SCI_STP_DEV_IDLE and SCI_STP_DEV_ATAPI state entry functions manage the RNC resumption. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 86 +++++++++--------- drivers/scsi/isci/remote_device.h | 4 - drivers/scsi/isci/remote_node_context.c | 74 ++++++++++----- drivers/scsi/isci/remote_node_context.h | 13 ++- drivers/scsi/isci/request.c | 149 ++++++++++++++++++++++++++++--- drivers/scsi/isci/request.h | 1 - drivers/scsi/isci/scu_completion_codes.h | 2 + 7 files changed, 242 insertions(+), 87 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 71f50906473..b1a8000a5ef 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -265,20 +265,12 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev return SCI_SUCCESS; } -enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, - u32 suspend_type) +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev) { - struct sci_base_state_machine *sm = &idev->sm; - enum sci_remote_device_states state = sm->current_state_id; - - if (state != SCI_STP_DEV_CMD) { - dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n", - __func__, dev_state_name(state)); - return SCI_FAILURE_INVALID_STATE; - } - return sci_remote_node_context_suspend(&idev->rnc, - suspend_type, NULL, NULL); + SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, + NULL, NULL); } enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev, @@ -412,8 +404,6 @@ static void atapi_remote_device_resume_done(void *_dev) enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, u32 event_code) { - struct sci_base_state_machine *sm = &idev->sm; - enum sci_remote_device_states state = sm->current_state_id; enum sci_status status; switch (scu_get_event_type(event_code)) { @@ -427,9 +417,11 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, status = SCI_SUCCESS; /* Suspend the associated RNC */ - sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, - NULL, NULL); + sci_remote_node_context_suspend( + &idev->rnc, + SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, + NULL, NULL); dev_dbg(scirdev_to_dev(idev), "%s: device: %p event code: %x: %s\n", @@ -455,26 +447,6 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, if (status != SCI_SUCCESS) return status; - if (state == SCI_STP_DEV_ATAPI_ERROR) { - /* For ATAPI error state resume the RNC right away. */ - if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || - scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { - return sci_remote_node_context_resume(&idev->rnc, - atapi_remote_device_resume_done, - idev); - } - } - - if (state == SCI_STP_DEV_IDLE) { - - /* We pick up suspension events to handle specifically to this - * state. We resume the RNC right away. - */ - if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || - scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) - status = sci_remote_node_context_resume(&idev->rnc, NULL, NULL); - } - return status; } @@ -765,11 +737,11 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, * the correct action when the remote node context is suspended * and later resumed. */ - sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, NULL, NULL); - sci_remote_node_context_resume(&idev->rnc, - sci_remote_device_continue_request, - idev); + sci_remote_node_context_suspend( + &idev->rnc, SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); + sci_remote_node_context_resume( + &idev->rnc, sci_remote_device_continue_request, idev); out: sci_remote_device_start_request(idev, ireq, status); @@ -954,14 +926,23 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); + struct isci_host *ihost = idev->owning_port->owning_controller; + + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p\n", __func__, idev); sci_remote_node_context_suspend( - &idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL); + &idev->rnc, SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); } static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); + struct isci_host *ihost = idev->owning_port->owning_controller; + + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p\n", __func__, idev); sci_remote_node_context_resume(&idev->rnc, NULL, NULL); } @@ -1004,6 +985,21 @@ static void sci_stp_remote_device_ready_ncq_error_substate_enter(struct sci_base idev->not_ready_reason); } +static void sci_stp_remote_device_atapi_error_substate_enter( + struct sci_base_state_machine *sm) +{ + struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); + + /* This state is entered when an I/O is decoded with an error + * condition. By this point the RNC expected suspension state is set. + * The error conditions suspend the device, so unsuspend here if + * possible. + */ + sci_remote_node_context_resume(&idev->rnc, + atapi_remote_device_resume_done, + idev); +} + static void sci_smp_remote_device_ready_idle_substate_enter(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); @@ -1054,7 +1050,9 @@ static const struct sci_base_state sci_remote_device_state_table[] = { [SCI_STP_DEV_NCQ_ERROR] = { .enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter, }, - [SCI_STP_DEV_ATAPI_ERROR] = { }, + [SCI_STP_DEV_ATAPI_ERROR] = { + .enter_state = sci_stp_remote_device_atapi_error_substate_enter, + }, [SCI_STP_DEV_AWAIT_RESET] = { }, [SCI_SMP_DEV_IDLE] = { .enter_state = sci_smp_remote_device_ready_idle_substate_enter, diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 4850b58edbe..39159053b7f 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -331,10 +331,6 @@ enum sci_status sci_remote_device_complete_io( struct isci_remote_device *idev, struct isci_request *ireq); -enum sci_status sci_remote_device_suspend( - struct isci_remote_device *idev, - u32 suspend_type); - void sci_remote_device_post_request( struct isci_remote_device *idev, u32 request); diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 3e849752bff..f180c726c5b 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -367,6 +367,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con u32 event_code) { enum scis_sds_remote_node_context_states state; + u32 next_state; state = sci_rnc->sm.current_state_id; switch (state) { @@ -425,11 +426,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con switch (scu_get_event_type(event_code)) { case SCU_EVENT_TL_RNC_SUSPEND_TX: sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + sci_rnc->suspend_type = scu_get_event_type(event_code); break; case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + sci_rnc->suspend_type = scu_get_event_type(event_code); break; default: goto out; @@ -438,16 +439,16 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con case SCI_RNC_AWAIT_SUSPENSION: switch (scu_get_event_type(event_code)) { case SCU_EVENT_TL_RNC_SUSPEND_TX: - sci_change_state(&sci_rnc->sm, SCI_RNC_TX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + next_state = SCI_RNC_TX_SUSPENDED; break; case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: - sci_change_state(&sci_rnc->sm, SCI_RNC_TX_RX_SUSPENDED); - sci_rnc->suspension_code = scu_get_event_specifier(event_code); + next_state = SCI_RNC_TX_RX_SUSPENDED; break; default: goto out; } + if (sci_rnc->suspend_type == scu_get_event_type(event_code)) + sci_change_state(&sci_rnc->sm, next_state); break; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), @@ -502,33 +503,60 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context } } -enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc, - u32 suspend_type, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p) +enum sci_status sci_remote_node_context_suspend( + struct sci_remote_node_context *sci_rnc, + enum sci_remote_node_suspension_reasons suspend_reason, + u32 suspend_type, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) { - enum scis_sds_remote_node_context_states state; + enum scis_sds_remote_node_context_states state + = sci_rnc->sm.current_state_id; + struct isci_remote_device *idev = rnc_to_dev(sci_rnc); + enum sci_status status = SCI_FAILURE_INVALID_STATE; - state = sci_rnc->sm.current_state_id; - if (state != SCI_RNC_READY) { + /* Disable automatic state continuations if explicitly suspending. */ + if (suspend_reason == SCI_SOFTWARE_SUSPENSION) + sci_rnc->destination_state + = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + switch (state) { + case SCI_RNC_READY: + break; + case SCI_RNC_TX_SUSPENDED: + if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX) + status = SCI_SUCCESS; + break; + case SCI_RNC_TX_RX_SUSPENDED: + if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX) + status = SCI_SUCCESS; + break; + case SCI_RNC_AWAIT_SUSPENSION: + if ((sci_rnc->suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX_RX) + || (suspend_type == sci_rnc->suspend_type)) + return SCI_SUCCESS; + break; + default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: invalid state %s\n", __func__, rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } + sci_rnc->user_callback = cb_fn; + sci_rnc->user_cookie = cb_p; + sci_rnc->suspend_type = suspend_type; - sci_rnc->user_callback = cb_fn; - sci_rnc->user_cookie = cb_p; - sci_rnc->suspension_code = suspend_type; - - if (suspend_type == SCI_SOFTWARE_SUSPENSION) { - sci_remote_device_post_request(rnc_to_dev(sci_rnc), - SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX); - isci_dev_set_hang_detection_timeout(rnc_to_dev(sci_rnc), - 0x00000001); + if (status == SCI_SUCCESS) { /* Already in the destination state? */ + sci_remote_node_context_notify_user(sci_rnc); + return SCI_SUCCESS; + } + if (suspend_reason == SCI_SOFTWARE_SUSPENSION) { + isci_dev_set_hang_detection_timeout(idev, 0x00000001); + sci_remote_device_post_request( + idev, SCI_SOFTWARE_SUSPEND_CMD); } + if (state != SCI_RNC_AWAIT_SUSPENSION) + sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION); - sci_change_state(&sci_rnc->sm, SCI_RNC_AWAIT_SUSPENSION); return SCI_SUCCESS; } diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index a241e0f4c86..276fc491e8f 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -75,8 +75,12 @@ */ #define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF -#define SCU_HARDWARE_SUSPENSION (0) -#define SCI_SOFTWARE_SUSPENSION (1) +enum sci_remote_node_suspension_reasons { + SCU_HARDWARE_SUSPENSION, + SCI_SOFTWARE_SUSPENSION +}; +#define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX +#define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX struct isci_request; struct isci_remote_device; @@ -156,10 +160,10 @@ struct sci_remote_node_context { u16 remote_node_index; /** - * This field is the recored suspension code or the reason for the remote node + * This field is the recored suspension type of the remote node * context suspension. */ - u32 suspension_code; + u32 suspend_type; /** * This field is true if the remote node context is resuming from its current @@ -200,6 +204,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context void *callback_parameter); enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc, u32 suspend_type, + u32 suspension_code, scics_sds_remote_node_context_callback cb_fn, void *cb_p); enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc, diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index dcd26eadf86..605dc68cbf7 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2116,9 +2116,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq * completion. */ if (ireq->stp.rsp.fis_type == FIS_REGD2H) { - sci_remote_device_suspend(ireq->target_device, - SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code))); - ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); @@ -2138,13 +2135,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq /* TODO We can retry the command for SCU_TASK_DONE_CMD_LL_R_ERR * - this comes only for B0 */ - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_INV_FIS_LEN): - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR): - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR): - case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CMD_LL_R_ERR): - sci_remote_device_suspend(ireq->target_device, - SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code))); - /* Fall through to the default case */ default: /* All other completion status cause the IO to be complete. */ ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code); @@ -2262,15 +2252,152 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire return status; } +static int sci_request_smp_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + return 1; + } + return 0; +} + +static int sci_request_smp_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + return 0; /* There are no Tx/Rx SMP suspend conditions. */ +} + +static int sci_request_ssp_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_TX_RAW_CMD_ERR: + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + return 1; + } + return 0; +} + +static int sci_request_ssp_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + return 0; /* There are no Tx/Rx SSP suspend conditions. */ +} + +static int sci_request_stpsata_completion_status_is_tx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_TX_RAW_CMD_ERR: + case SCU_TASK_DONE_LL_R_ERR: + case SCU_TASK_DONE_LL_PERR: + case SCU_TASK_DONE_REG_ERR: + case SCU_TASK_DONE_SDB_ERR: + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + return 1; + } + return 0; +} + + +static int sci_request_stpsata_completion_status_is_tx_rx_suspend( + unsigned int completion_status) +{ + switch (completion_status) { + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_DONE_LL_SY_TERM: + case SCU_TASK_DONE_LL_LF_TERM: + case SCU_TASK_DONE_BREAK_RCVD: + case SCU_TASK_DONE_INV_FIS_LEN: + case SCU_TASK_DONE_UNEXP_FIS: + case SCU_TASK_DONE_UNEXP_SDBFIS: + case SCU_TASK_DONE_MAX_PLD_ERR: + return 1; + } + return 0; +} + +static void sci_request_handle_suspending_completions( + struct isci_request *ireq, + u32 completion_code) +{ + int is_tx = 0; + int is_tx_rx = 0; + + switch (ireq->protocol) { + case SAS_PROTOCOL_SMP: + is_tx = sci_request_smp_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = sci_request_smp_completion_status_is_tx_rx_suspend( + completion_code); + break; + case SAS_PROTOCOL_SSP: + is_tx = sci_request_ssp_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = sci_request_ssp_completion_status_is_tx_rx_suspend( + completion_code); + break; + case SAS_PROTOCOL_STP: + is_tx = sci_request_stpsata_completion_status_is_tx_suspend( + completion_code); + is_tx_rx = + sci_request_stpsata_completion_status_is_tx_rx_suspend( + completion_code); + break; + default: + dev_warn(&ireq->isci_host->pdev->dev, + "%s: request %p has no valid protocol\n", + __func__, ireq); + break; + } + if (is_tx || is_tx_rx) { + BUG_ON(is_tx && is_tx_rx); + + sci_remote_node_context_suspend( + &ireq->target_device->rnc, + SCU_HARDWARE_SUSPENSION, + (is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX + : SCU_EVENT_TL_RNC_SUSPEND_TX, + NULL, NULL); + } +} + enum sci_status sci_io_request_tc_completion(struct isci_request *ireq, - u32 completion_code) + u32 completion_code) { enum sci_base_request_states state; struct isci_host *ihost = ireq->owning_controller; state = ireq->sm.current_state_id; + /* Decode those completions that signal upcoming suspension events. */ + sci_request_handle_suspending_completions( + ireq, SCU_GET_COMPLETION_TL_STATUS(completion_code)); + switch (state) { case SCI_REQ_STARTED: return request_started_state_tc_event(ireq, completion_code); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 4961f9fbf70..e845a31eceb 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -414,5 +414,4 @@ static inline int isci_task_is_ncq_recovery(struct sas_task *task) task->ata_task.fis.lbal == ATA_LOG_SATA_NCQ); } - #endif /* !defined(_ISCI_REQUEST_H_) */ diff --git a/drivers/scsi/isci/scu_completion_codes.h b/drivers/scsi/isci/scu_completion_codes.h index c8b329c695f..071cb74a211 100644 --- a/drivers/scsi/isci/scu_completion_codes.h +++ b/drivers/scsi/isci/scu_completion_codes.h @@ -224,6 +224,7 @@ * 32-bit value like we want, each immediate value must be cast to a u32. */ #define SCU_TASK_DONE_GOOD ((u32)0x00) +#define SCU_TASK_DONE_TX_RAW_CMD_ERR ((u32)0x08) #define SCU_TASK_DONE_CRC_ERR ((u32)0x14) #define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14) #define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15) @@ -237,6 +238,7 @@ #define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A) #define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A) #define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B) +#define SCU_TASK_DONE_BREAK_RCVD ((u32)0x1B) #define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B) #define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C) #define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C) -- cgit v1.2.3-70-g09d2 From 726980d56908f2e230624394f03743689db3110c Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:50 -0800 Subject: isci: Terminate outstanding TCs on TX/RX RNC suspensions. TCs must only be terminated when RNCs are suspended. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 22 +++---- drivers/scsi/isci/remote_device.c | 106 ++++++++++++++++++++++++++++---- drivers/scsi/isci/remote_device.h | 10 +++ drivers/scsi/isci/remote_node_context.c | 56 +++++++++++++++++ drivers/scsi/isci/remote_node_context.h | 2 + drivers/scsi/isci/request.c | 22 ++++--- drivers/scsi/isci/request.h | 2 + 7 files changed, 188 insertions(+), 32 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 5832b13e7b0..d241b5722eb 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -2696,18 +2696,18 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, __func__, ihost->sm.current_state_id); return SCI_FAILURE_INVALID_STATE; } - status = sci_io_request_terminate(ireq); - if (status != SCI_SUCCESS) - return status; - - /* - * Utilize the original post context command and or in the POST_TC_ABORT - * request sub-type. - */ - sci_controller_post_request(ihost, - ireq->post_context | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); - return SCI_SUCCESS; + if ((status == SCI_SUCCESS) && + !test_bit(IREQ_PENDING_ABORT, &ireq->flags) && + !test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) { + /* Utilize the original post context command and or in the + * POST_TC_ABORT request sub-type. + */ + sci_controller_post_request( + ihost, ireq->post_context | + SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT); + } + return status; } /** diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b1a8000a5ef..9f03877534d 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -133,6 +133,50 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote wake_up(&ihost->eventq); } +static enum sci_status sci_remote_device_suspend( + struct isci_remote_device *idev) +{ + return sci_remote_node_context_suspend( + &idev->rnc, + SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, + NULL, NULL); +} + +enum sci_status isci_remote_device_suspend( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + enum sci_status status; + unsigned long flags; + + spin_lock_irqsave(&ihost->scic_lock, flags); + + if (isci_lookup_device(idev->domain_dev) == NULL) { + spin_unlock_irqrestore(&ihost->scic_lock, flags); + status = SCI_FAILURE; + } else { + status = sci_remote_device_suspend(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (status == SCI_SUCCESS) { + wait_event(ihost->eventq, + test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) + || !test_bit(IDEV_ALLOCATED, &idev->flags)); + + status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) + ? SCI_SUCCESS : SCI_FAILURE; + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p, wait done, device is %s\n", + __func__, idev, + test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) + ? "" : ""); + + } + isci_put_device(idev); + } + return status; +} + /* called once the remote node context is ready to be freed. * The remote device can now report that its stop operation is complete. none */ @@ -144,7 +188,9 @@ static void rnc_destruct_done(void *_dev) sci_change_state(&idev->sm, SCI_DEV_STOPPED); } -static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev) +static enum sci_status sci_remote_device_terminate_requests_checkabort( + struct isci_remote_device *idev, + int check_abort_pending) { struct isci_host *ihost = idev->owning_port->owning_controller; enum sci_status status = SCI_SUCCESS; @@ -155,7 +201,9 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d enum sci_status s; if (!test_bit(IREQ_ACTIVE, &ireq->flags) || - ireq->target_device != idev) + (ireq->target_device != idev) || + (check_abort_pending && !test_bit(IREQ_PENDING_ABORT, + &ireq->flags))) continue; s = sci_controller_terminate_request(ihost, idev, ireq); @@ -166,6 +214,12 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d return status; } +enum sci_status sci_remote_device_terminate_requests( + struct isci_remote_device *idev) +{ + return sci_remote_device_terminate_requests_checkabort(idev, 0); +} + enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, u32 timeout) { @@ -265,14 +319,6 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev return SCI_SUCCESS; } -enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev) -{ - return sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, - NULL, NULL); -} - enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev, u32 frame_index) { @@ -1186,7 +1232,7 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, * the device when there have been no phys added to it. */ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, - u32 timeout) + u32 timeout) { struct sci_base_state_machine *sm = &idev->sm; enum sci_remote_device_states state = sm->current_state_id; @@ -1413,3 +1459,41 @@ int isci_remote_device_found(struct domain_device *dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } + +enum sci_status isci_remote_device_reset( + struct isci_remote_device *idev) +{ + struct isci_host *ihost = dev_to_ihost(idev->domain_dev); + unsigned long flags; + enum sci_status status; + + /* Wait for the device suspend. */ + status = isci_remote_device_suspend(ihost, idev); + if (status != SCI_SUCCESS) { + dev_dbg(&ihost->pdev->dev, + "%s: isci_remote_device_suspend(%p) returned %d!\n", + __func__, idev, status); + return status; + } + spin_lock_irqsave(&ihost->scic_lock, flags); + status = sci_remote_device_reset(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + if (status != SCI_SUCCESS) { + dev_dbg(&ihost->pdev->dev, + "%s: sci_remote_device_reset(%p) returned %d!\n", + __func__, idev, status); + } + return status; +} + +int isci_remote_device_is_safe_to_abort( + struct isci_remote_device *idev) +{ + return sci_remote_node_context_is_safe_to_abort(&idev->rnc); +} + +enum sci_status sci_remote_device_abort_requests_pending_abort( + struct isci_remote_device *idev) +{ + return sci_remote_device_terminate_requests_checkabort(idev, 1); +} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 39159053b7f..ae508ee3366 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -85,6 +85,7 @@ struct isci_remote_device { #define IDEV_GONE 3 #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 + #define IDEV_TXRX_SUSPENDED 6 unsigned long flags; struct kref kref; struct isci_port *isci_port; @@ -335,4 +336,13 @@ void sci_remote_device_post_request( struct isci_remote_device *idev, u32 request); +enum sci_status sci_remote_device_terminate_requests( + struct isci_remote_device *idev); + +int isci_remote_device_is_safe_to_abort( + struct isci_remote_device *idev); + +enum sci_status +sci_remote_device_abort_requests_pending_abort( + struct isci_remote_device *idev); #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index f180c726c5b..7a8347e5176 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -268,6 +268,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + /* Terminate outstanding requests pending abort. */ + sci_remote_device_abort_requests_pending_abort(rnc_to_dev(rnc)); sci_remote_node_context_invalidate_context_buffer(rnc); } @@ -312,10 +314,28 @@ static void sci_remote_node_context_tx_suspended_state_enter(struct sci_base_sta static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + struct isci_host *ihost = idev->owning_port->owning_controller; + + set_bit(IDEV_TXRX_SUSPENDED, &idev->flags); + /* Terminate outstanding requests pending abort. */ + sci_remote_device_abort_requests_pending_abort(idev); + + wake_up(&ihost->eventq); sci_remote_node_context_continue_state_transitions(rnc); } +static void sci_remote_node_context_tx_rx_suspended_state_exit( + struct sci_base_state_machine *sm) +{ + struct sci_remote_node_context *rnc + = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + + clear_bit(IDEV_TXRX_SUSPENDED, &idev->flags); +} + static void sci_remote_node_context_await_suspend_state_exit( struct sci_base_state_machine *sm) { @@ -346,6 +366,8 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = { }, [SCI_RNC_TX_RX_SUSPENDED] = { .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, + .exit_state + = sci_remote_node_context_tx_rx_suspended_state_exit, }, [SCI_RNC_AWAIT_SUSPENSION] = { .exit_state = sci_remote_node_context_await_suspend_state_exit, @@ -515,6 +537,13 @@ enum sci_status sci_remote_node_context_suspend( struct isci_remote_device *idev = rnc_to_dev(sci_rnc); enum sci_status status = SCI_FAILURE_INVALID_STATE; + dev_dbg(scirdev_to_dev(idev), + "%s: current state %d, current suspend_type %x dest state %d," + " arg suspend_reason %d, arg suspend_type %x", + __func__, state, sci_rnc->suspend_type, + sci_rnc->destination_state, suspend_reason, + suspend_type); + /* Disable automatic state continuations if explicitly suspending. */ if (suspend_reason == SCI_SOFTWARE_SUSPENSION) sci_rnc->destination_state @@ -546,7 +575,10 @@ enum sci_status sci_remote_node_context_suspend( sci_rnc->suspend_type = suspend_type; if (status == SCI_SUCCESS) { /* Already in the destination state? */ + struct isci_host *ihost = idev->owning_port->owning_controller; + sci_remote_node_context_notify_user(sci_rnc); + wake_up_all(&ihost->eventq); /* Let observers look. */ return SCI_SUCCESS; } if (suspend_reason == SCI_SOFTWARE_SUSPENSION) { @@ -661,3 +693,27 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex return SCI_FAILURE_INVALID_STATE; } } + +int sci_remote_node_context_is_safe_to_abort( + struct sci_remote_node_context *sci_rnc) +{ + enum scis_sds_remote_node_context_states state; + + state = sci_rnc->sm.current_state_id; + switch (state) { + case SCI_RNC_INVALIDATING: + case SCI_RNC_TX_RX_SUSPENDED: + return 1; + case SCI_RNC_POSTING: + case SCI_RNC_RESUMING: + case SCI_RNC_READY: + case SCI_RNC_TX_SUSPENDED: + case SCI_RNC_AWAIT_SUSPENSION: + case SCI_RNC_INITIAL: + return 0; + default: + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: invalid state %d\n", __func__, state); + return 0; + } +} diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 276fc491e8f..5ddf88b5313 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -214,5 +214,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex struct isci_request *ireq); enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc, struct isci_request *ireq); +int sci_remote_node_context_is_safe_to_abort( + struct sci_remote_node_context *sci_rnc); #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 605dc68cbf7..1f314d0d71d 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -863,6 +863,8 @@ sci_io_request_terminate(struct isci_request *ireq) switch (state) { case SCI_REQ_CONSTRUCTED: + /* Set to make sure no HW terminate posting is done: */ + set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags); ireq->scu_status = SCU_TASK_DONE_TASK_ABORT; ireq->sci_status = SCI_FAILURE_IO_TERMINATED; sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); @@ -883,8 +885,7 @@ sci_io_request_terminate(struct isci_request *ireq) case SCI_REQ_ATAPI_WAIT_PIO_SETUP: case SCI_REQ_ATAPI_WAIT_D2H: case SCI_REQ_ATAPI_WAIT_TC_COMP: - sci_change_state(&ireq->sm, SCI_REQ_ABORTING); - return SCI_SUCCESS; + /* Fall through and change state to ABORTING... */ case SCI_REQ_TASK_WAIT_TC_RESP: /* The task frame was already confirmed to have been * sent by the SCU HW. Since the state machine is @@ -893,20 +894,21 @@ sci_io_request_terminate(struct isci_request *ireq) * and don't wait for the task response. */ sci_change_state(&ireq->sm, SCI_REQ_ABORTING); - sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); - return SCI_SUCCESS; + /* Fall through and handle like ABORTING... */ case SCI_REQ_ABORTING: - /* If a request has a termination requested twice, return - * a failure indication, since HW confirmation of the first - * abort is still outstanding. + if (!isci_remote_device_is_safe_to_abort(ireq->target_device)) + set_bit(IREQ_PENDING_ABORT, &ireq->flags); + else + clear_bit(IREQ_PENDING_ABORT, &ireq->flags); + /* If the request is only waiting on the remote device + * suspension, return SUCCESS so the caller will wait too. */ + return SCI_SUCCESS; case SCI_REQ_COMPLETED: default: dev_warn(&ireq->owning_controller->pdev->dev, "%s: SCIC IO Request requested to abort while in wrong " - "state %d\n", - __func__, - ireq->sm.current_state_id); + "state %d\n", __func__, ireq->sm.current_state_id); break; } diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index e845a31eceb..8d55f78010a 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -102,6 +102,8 @@ struct isci_request { #define IREQ_TERMINATED 1 #define IREQ_TMF 2 #define IREQ_ACTIVE 3 + #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ + #define IREQ_TC_ABORT_POSTED 5 unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { -- cgit v1.2.3-70-g09d2 From 5b6bf225e7fc249c703e19bf2c983d1a59178874 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:51 -0800 Subject: isci: Manage device suspensions during TC terminations. TCs must be terminated only while the RNC is suspended. This commit adds remote device suspensions and resumptions in the abort, reset and termination paths. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 90 +++++++++++++++++++++++++++++++-------- drivers/scsi/isci/remote_device.h | 33 ++++++++++++++ drivers/scsi/isci/task.c | 80 ++++++++++++++++++++-------------- 3 files changed, 154 insertions(+), 49 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 9f03877534d..4f76dcd1cec 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -143,6 +143,12 @@ static enum sci_status sci_remote_device_suspend( NULL, NULL); } +static int isci_remote_device_suspendcheck(struct isci_remote_device *idev) +{ + return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) + || !test_bit(IDEV_ALLOCATED, &idev->flags); +} + enum sci_status isci_remote_device_suspend( struct isci_host *ihost, struct isci_remote_device *idev) @@ -151,18 +157,18 @@ enum sci_status isci_remote_device_suspend( unsigned long flags; spin_lock_irqsave(&ihost->scic_lock, flags); - - if (isci_lookup_device(idev->domain_dev) == NULL) { + if (isci_get_device(idev->domain_dev) == NULL) { spin_unlock_irqrestore(&ihost->scic_lock, flags); status = SCI_FAILURE; } else { status = sci_remote_device_suspend(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); if (status == SCI_SUCCESS) { + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p, about to wait\n", + __func__, idev); wait_event(ihost->eventq, - test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) - || !test_bit(IDEV_ALLOCATED, &idev->flags)); - + isci_remote_device_suspendcheck(idev)); status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) ? SCI_SUCCESS : SCI_FAILURE; dev_dbg(&ihost->pdev->dev, @@ -171,7 +177,10 @@ enum sci_status isci_remote_device_suspend( test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) ? "" : ""); - } + } else + dev_dbg(scirdev_to_dev(idev), + "%s: sci_remote_device_suspend failed, " + "status = %d\n", __func__, status); isci_put_device(idev); } return status; @@ -1218,6 +1227,35 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport, return SCI_SUCCESS; } +enum sci_status sci_remote_device_resume( + struct isci_remote_device *idev, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) +{ + enum sci_status status; + + status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p); + if (status != SCI_SUCCESS) + dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n", + __func__, status); + return status; +} + +enum sci_status isci_remote_device_resume( + struct isci_host *ihost, + struct isci_remote_device *idev, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) +{ + unsigned long flags; + enum sci_status status; + + spin_lock_irqsave(&ihost->scic_lock, flags); + status = sci_remote_device_resume(idev, cb_fn, cb_p); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return status; +} /** * sci_remote_device_start() - This method will start the supplied remote * device. This method enables normal IO requests to flow through to the @@ -1244,9 +1282,8 @@ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, return SCI_FAILURE_INVALID_STATE; } - status = sci_remote_node_context_resume(&idev->rnc, - remote_device_resume_done, - idev); + status = sci_remote_device_resume(idev, remote_device_resume_done, + idev); if (status != SCI_SUCCESS) return status; @@ -1461,26 +1498,29 @@ int isci_remote_device_found(struct domain_device *dev) } enum sci_status isci_remote_device_reset( + struct isci_host *ihost, struct isci_remote_device *idev) { - struct isci_host *ihost = dev_to_ihost(idev->domain_dev); unsigned long flags; enum sci_status status; - /* Wait for the device suspend. */ - status = isci_remote_device_suspend(ihost, idev); + /* Put the device into a reset state so the suspension will not + * automatically resume. + */ + spin_lock_irqsave(&ihost->scic_lock, flags); + status = sci_remote_device_reset(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); if (status != SCI_SUCCESS) { dev_dbg(&ihost->pdev->dev, - "%s: isci_remote_device_suspend(%p) returned %d!\n", + "%s: sci_remote_device_reset(%p) returned %d!\n", __func__, idev, status); return status; } - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* Wait for the device suspend. */ + status = isci_remote_device_suspend(ihost, idev); if (status != SCI_SUCCESS) { dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset(%p) returned %d!\n", + "%s: isci_remote_device_suspend(%p) returned %d!\n", __func__, idev, status); } return status; @@ -1497,3 +1537,19 @@ enum sci_status sci_remote_device_abort_requests_pending_abort( { return sci_remote_device_terminate_requests_checkabort(idev, 1); } + +enum sci_status isci_remote_device_reset_complete( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + unsigned long flags; + enum sci_status status; + + spin_lock_irqsave(&ihost->scic_lock, flags); + status = sci_remote_device_reset_complete(idev); + sci_remote_device_resume(idev, NULL, NULL); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return status; +} + diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index ae508ee3366..a6a376e200e 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -106,6 +106,16 @@ struct isci_remote_device { #define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000 /* device reference routines must be called under sci_lock */ +static inline struct isci_remote_device *isci_get_device( + struct domain_device *dev) +{ + struct isci_remote_device *idev = dev->lldd_dev; + + if (idev) + kref_get(&idev->kref); + return idev; +} + static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev) { struct isci_remote_device *idev = dev->lldd_dev; @@ -345,4 +355,27 @@ int isci_remote_device_is_safe_to_abort( enum sci_status sci_remote_device_abort_requests_pending_abort( struct isci_remote_device *idev); + +enum sci_status isci_remote_device_suspend( + struct isci_host *ihost, + struct isci_remote_device *idev); + +enum sci_status sci_remote_device_resume( + struct isci_remote_device *idev, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p); + +enum sci_status isci_remote_device_resume( + struct isci_host *ihost, + struct isci_remote_device *idev, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p); + +enum sci_status isci_remote_device_reset( + struct isci_host *ihost, + struct isci_remote_device *idev); + +enum sci_status isci_remote_device_reset_complete( + struct isci_host *ihost, + struct isci_remote_device *idev); #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 374254ede9d..9b8632f33dd 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -716,6 +716,8 @@ void isci_terminate_pending_requests(struct isci_host *ihost, unsigned long flags; LIST_HEAD(list); + isci_remote_device_suspend(ihost, idev); + spin_lock_irqsave(&ihost->scic_lock, flags); list_splice_init(&idev->reqs_in_process, &list); @@ -826,40 +828,47 @@ static int isci_task_send_lu_reset_sas( int isci_task_lu_reset(struct domain_device *dev, u8 *lun) { - struct isci_host *isci_host = dev_to_ihost(dev); + struct isci_host *ihost = dev_to_ihost(dev); struct isci_remote_device *isci_device; unsigned long flags; int ret; - spin_lock_irqsave(&isci_host->scic_lock, flags); + spin_lock_irqsave(&ihost->scic_lock, flags); isci_device = isci_lookup_device(dev); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_dbg(&ihost->pdev->dev, "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", - __func__, dev, isci_host, isci_device); + __func__, dev, ihost, isci_device); if (!isci_device) { /* If the device is gone, stop the escalations. */ - dev_dbg(&isci_host->pdev->dev, "%s: No dev\n", __func__); + dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); ret = TMF_RESP_FUNC_COMPLETE; goto out; } + if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) { + dev_dbg(&ihost->pdev->dev, + "%s: device = %p; failed to suspend\n", + __func__, isci_device); + ret = TMF_RESP_FUNC_FAILED; + goto out; + } /* Send the task management part of the reset. */ 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); + ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun); /* If the LUN reset worked, all the I/O can now be terminated. */ - if (ret == TMF_RESP_FUNC_COMPLETE) + if (ret == TMF_RESP_FUNC_COMPLETE) { /* Terminate all I/O now. */ - isci_terminate_pending_requests(isci_host, - isci_device); - + isci_terminate_pending_requests(ihost, isci_device); + isci_remote_device_resume(ihost, isci_device, NULL, NULL); + } out: isci_put_device(isci_device); return ret; @@ -976,7 +985,7 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock(&task->task_state_lock); spin_unlock_irqrestore(&isci_host->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: dev = %p, task = %p, old_request == %p\n", __func__, isci_device, task, old_request); @@ -998,7 +1007,7 @@ int isci_task_abort_task(struct sas_task *task) ret = TMF_RESP_FUNC_COMPLETE; - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: abort task not needed for %p\n", __func__, task); goto out; @@ -1022,7 +1031,7 @@ int isci_task_abort_task(struct sas_task *task) /* The request was already being handled by someone else (because * they got to set the state away from started). */ - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: device = %p; old_request %p already being aborted\n", __func__, isci_device, old_request); @@ -1035,7 +1044,7 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: %s request" " or complete_in_target (%d), thus no TMF\n", __func__, @@ -1068,6 +1077,15 @@ int isci_task_abort_task(struct sas_task *task) */ perform_termination = 1; + if (isci_device && !test_bit(IDEV_GONE, &isci_device->flags) && + (isci_remote_device_suspend(isci_host, isci_device) + != SCI_SUCCESS)) { + dev_warn(&isci_host->pdev->dev, + "%s: device = %p; failed to suspend\n", + __func__, isci_device); + goto out; + } + } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, @@ -1076,6 +1094,14 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&isci_host->scic_lock, flags); + if (isci_remote_device_suspend(isci_host, isci_device) + != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: device = %p; failed to suspend\n", + __func__, isci_device); + goto out; + } + #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); @@ -1083,7 +1109,7 @@ int isci_task_abort_task(struct sas_task *task) if (ret == TMF_RESP_FUNC_COMPLETE) perform_termination = 1; else - dev_dbg(&isci_host->pdev->dev, + dev_warn(&isci_host->pdev->dev, "%s: isci_task_send_tmf failed\n", __func__); } if (perform_termination) { @@ -1094,6 +1120,7 @@ int isci_task_abort_task(struct sas_task *task) */ isci_terminate_request_core(isci_host, isci_device, old_request); + isci_remote_device_resume(isci_host, isci_device, NULL, NULL); } /* Make sure we do not leave a reference to aborted_io_completion */ @@ -1251,21 +1278,13 @@ static int isci_reset_device(struct isci_host *ihost, struct isci_remote_device *idev) { int rc; - unsigned long flags; enum sci_status status; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset(%p) returned %d!\n", - __func__, idev, status); + if (isci_remote_device_reset(ihost, idev) != SCI_SUCCESS) { rc = TMF_RESP_FUNC_FAILED; goto out; } @@ -1281,15 +1300,12 @@ static int isci_reset_device(struct isci_host *ihost, isci_remote_device_nuke_requests(ihost, idev); /* Since all pending TCs have been cleaned, resume the RNC. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset_complete(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + status = isci_remote_device_reset_complete(ihost, idev); - if (status != SCI_SUCCESS) { + if (status != SCI_SUCCESS) dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset_complete(%p) " + "%s: isci_remote_device_reset_complete(%p) " "returned %d!\n", __func__, idev, status); - } dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); out: @@ -1305,7 +1321,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(dev); + idev = isci_get_device(dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!idev) { -- cgit v1.2.3-70-g09d2 From 23ec2aa947e83d0a172220f361166b8224875221 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:52 -0800 Subject: isci: Remote device must be suspended for NCQ cleanup. When the remote device enters the NCQ error state, the device must be suspended so that the I/O terminations can take place. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 4f76dcd1cec..f40d429d2cc 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -72,6 +72,14 @@ const char *dev_state_name(enum sci_remote_device_states state) } #undef C +static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev) +{ + return sci_remote_node_context_suspend(&idev->rnc, + SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, + NULL, NULL); +} + /** * isci_remote_device_not_ready() - This function is called by the ihost when * the remote device is not ready. We mark the isci device as ready (not @@ -96,6 +104,9 @@ static void isci_remote_device_not_ready(struct isci_host *ihost, case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: set_bit(IDEV_IO_NCQERROR, &idev->flags); + /* Suspend the remote device so the I/O can be terminated. */ + sci_remote_device_suspend(idev); + /* Kill all outstanding requests for the device. */ list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) { @@ -103,9 +114,7 @@ static void isci_remote_device_not_ready(struct isci_host *ihost, "%s: isci_device = %p request = %p\n", __func__, idev, ireq); - sci_controller_terminate_request(ihost, - idev, - ireq); + sci_controller_terminate_request(ihost, idev, ireq); } /* Fall through into the default case... */ default: @@ -133,16 +142,6 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote wake_up(&ihost->eventq); } -static enum sci_status sci_remote_device_suspend( - struct isci_remote_device *idev) -{ - return sci_remote_node_context_suspend( - &idev->rnc, - SCI_SOFTWARE_SUSPENSION, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, - NULL, NULL); -} - static int isci_remote_device_suspendcheck(struct isci_remote_device *idev) { return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) -- cgit v1.2.3-70-g09d2 From 83884014eaaa68834ced39d1c75f1bc20d618ec0 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:52 -0800 Subject: isci: Remote device stop also suspends the RNC and terminates I/O. Fixing the remote device state machine to suspend and terminate all outstanding I/O before the device stopped state is reached. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index f40d429d2cc..3048e02aeb7 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -263,13 +263,15 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, case SCI_SMP_DEV_IDLE: case SCI_SMP_DEV_CMD: sci_change_state(sm, SCI_DEV_STOPPING); - if (idev->started_request_count == 0) { + if (idev->started_request_count == 0) sci_remote_node_context_destruct(&idev->rnc, - rnc_destruct_done, idev); - return SCI_SUCCESS; - } else - return sci_remote_device_terminate_requests(idev); - break; + rnc_destruct_done, + idev); + else { + sci_remote_device_suspend(idev); + sci_remote_device_terminate_requests(idev); + } + return SCI_SUCCESS; case SCI_DEV_STOPPING: /* All requests should have been terminated, but if there is an * attempt to stop a device already in the stopping state, then @@ -1403,14 +1405,8 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem spin_lock_irqsave(&ihost->scic_lock, flags); idev->domain_dev->lldd_dev = NULL; /* disable new lookups */ set_bit(IDEV_GONE, &idev->flags); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - /* Kill all outstanding requests. */ - isci_remote_device_nuke_requests(ihost, idev); set_bit(IDEV_STOP_PENDING, &idev->flags); - - spin_lock_irqsave(&ihost->scic_lock, flags); status = sci_remote_device_stop(idev, 50); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1420,6 +1416,9 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem else wait_for_device_stop(ihost, idev); + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p, waiting done.\n", __func__, idev); + return status; } -- cgit v1.2.3-70-g09d2 From d80ecd5726ce49b5da457d562804b40f0183e8f7 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:53 -0800 Subject: isci: Escalate to I_T_Nexus_Reset when the device is gone. If LUN reset sees that the device is gone, it returns TMF_RESP_FUNC_FAILED to cause libsas to escalate to an I_T_Nexus_Reset. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 9b8632f33dd..26de06ef688 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -842,10 +842,10 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) __func__, dev, ihost, isci_device); if (!isci_device) { - /* If the device is gone, stop the escalations. */ + /* If the device is gone, escalate to I_T_Nexus_Reset. */ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); - ret = TMF_RESP_FUNC_COMPLETE; + ret = TMF_RESP_FUNC_FAILED; goto out; } if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) { -- cgit v1.2.3-70-g09d2 From 14aaa9f0a318bd04cbb9d822524b817e95d8b343 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:54 -0800 Subject: isci: Redesign device suspension, abort, cleanup. This commit changes the means by which outstanding I/Os are handled for cleanup. The likelihood is that this commit will be broken into smaller pieces, however that will be a later revision. Among the changes: - All completion structures have been removed from the tmf and abort paths. - Now using one completed I/O list, with the I/O completed in host bit being used to select error or normal callback paths. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 86 ++-- drivers/scsi/isci/host.h | 1 - drivers/scsi/isci/init.c | 1 - drivers/scsi/isci/remote_device.c | 255 +++++------ drivers/scsi/isci/remote_device.h | 15 +- drivers/scsi/isci/remote_node_context.c | 23 +- drivers/scsi/isci/remote_node_context.h | 4 +- drivers/scsi/isci/request.c | 509 ++++++--------------- drivers/scsi/isci/request.h | 108 +---- drivers/scsi/isci/task.c | 758 +++++--------------------------- drivers/scsi/isci/task.h | 132 +----- 11 files changed, 418 insertions(+), 1474 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index d241b5722eb..25d537e2f5c 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1089,33 +1089,25 @@ void isci_host_completion_routine(unsigned long data) { struct isci_host *ihost = (struct isci_host *)data; struct list_head completed_request_list; - struct list_head errored_request_list; struct list_head *current_position; struct list_head *next_position; struct isci_request *request; - struct isci_request *next_request; struct sas_task *task; u16 active; INIT_LIST_HEAD(&completed_request_list); - INIT_LIST_HEAD(&errored_request_list); spin_lock_irq(&ihost->scic_lock); sci_controller_completion_handler(ihost); /* Take the lists of completed I/Os from the host. */ - list_splice_init(&ihost->requests_to_complete, &completed_request_list); - /* Take the list of errored I/Os from the host. */ - list_splice_init(&ihost->requests_to_errorback, - &errored_request_list); - spin_unlock_irq(&ihost->scic_lock); - /* Process any completions in the lists. */ + /* Process any completions in the list. */ list_for_each_safe(current_position, next_position, &completed_request_list) { @@ -1123,23 +1115,30 @@ void isci_host_completion_routine(unsigned long data) completed_node); task = isci_request_access_task(request); - /* Normal notification (task_done) */ - dev_dbg(&ihost->pdev->dev, - "%s: Normal - request/task = %p/%p\n", - __func__, - request, - task); /* Return the task to libsas */ if (task != NULL) { task->lldd_task = NULL; - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - - /* If the task is already in the abort path, - * the task_done callback cannot be called. - */ - task->task_done(task); + if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags) && + !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (test_bit(IREQ_COMPLETE_IN_TARGET, + &request->flags)) { + + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, "%s: Normal" + " - request/task = %p/%p\n", + __func__, request, task); + + task->task_done(task); + } else { + dev_warn(&ihost->pdev->dev, + "%s: Error - request/task" + " = %p/%p\n", + __func__, request, task); + + sas_task_abort(task); + } } } @@ -1147,44 +1146,6 @@ void isci_host_completion_routine(unsigned long data) isci_free_tag(ihost, request->io_tag); spin_unlock_irq(&ihost->scic_lock); } - list_for_each_entry_safe(request, next_request, &errored_request_list, - completed_node) { - - task = isci_request_access_task(request); - - /* Use sas_task_abort */ - dev_warn(&ihost->pdev->dev, - "%s: Error - request/task = %p/%p\n", - __func__, - request, - task); - - if (task != NULL) { - - /* Put the task into the abort path if it's not there - * already. - */ - if (!(task->task_state_flags & SAS_TASK_STATE_ABORTED)) - sas_task_abort(task); - - } else { - /* This is a case where the request has completed with a - * status such that it needed further target servicing, - * but the sas_task reference has already been removed - * from the request. Since it was errored, it was not - * being aborted, so there is nothing to do except free - * it. - */ - - spin_lock_irq(&ihost->scic_lock); - /* Remove the request from the remote device's list - * of pending requests. - */ - list_del_init(&request->dev_node); - isci_free_tag(ihost, request->io_tag); - spin_unlock_irq(&ihost->scic_lock); - } - } /* the coalesence timeout doubles at each encoding step, so * update it based on the ilog2 value of the outstanding requests @@ -2345,7 +2306,6 @@ static int sci_controller_dma_alloc(struct isci_host *ihost) ireq->tc = &ihost->task_context_table[i]; ireq->owning_controller = ihost; - spin_lock_init(&ireq->state_lock); ireq->request_daddr = dma; ireq->isci_host = ihost; ihost->reqs[i] = ireq; @@ -2697,6 +2657,10 @@ enum sci_status sci_controller_terminate_request(struct isci_host *ihost, return SCI_FAILURE_INVALID_STATE; } status = sci_io_request_terminate(ireq); + + dev_dbg(&ihost->pdev->dev, "%s: status=%d; ireq=%p; flags=%lx\n", + __func__, status, ireq, ireq->flags); + if ((status == SCI_SUCCESS) && !test_bit(IREQ_PENDING_ABORT, &ireq->flags) && !test_and_set_bit(IREQ_TC_ABORT_POSTED, &ireq->flags)) { @@ -2739,6 +2703,8 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost, index = ISCI_TAG_TCI(ireq->io_tag); clear_bit(IREQ_ACTIVE, &ireq->flags); + if (test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); return SCI_SUCCESS; default: dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 7272a0a375f..eaa13c0be09 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -205,7 +205,6 @@ struct isci_host { wait_queue_head_t eventq; struct tasklet_struct completion_tasklet; struct list_head requests_to_complete; - struct list_head requests_to_errorback; spinlock_t scic_lock; struct isci_request *reqs[SCI_MAX_IO_REQUESTS]; struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES]; diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 9e1c83e425e..39f12703b89 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -556,7 +556,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) } INIT_LIST_HEAD(&ihost->requests_to_complete); - INIT_LIST_HEAD(&ihost->requests_to_errorback); for (i = 0; i < SCI_MAX_PORTS; i++) { struct isci_port *iport = &ihost->ports[i]; diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 3048e02aeb7..c47304cea45 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -80,49 +80,6 @@ static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev NULL, NULL); } -/** - * isci_remote_device_not_ready() - This function is called by the ihost when - * the remote device is not ready. We mark the isci device as ready (not - * "ready_for_io") and signal the waiting proccess. - * @isci_host: This parameter specifies the isci host object. - * @isci_device: This parameter specifies the remote device - * - * sci_lock is held on entrance to this function. - */ -static void isci_remote_device_not_ready(struct isci_host *ihost, - struct isci_remote_device *idev, u32 reason) -{ - struct isci_request *ireq; - - dev_dbg(&ihost->pdev->dev, - "%s: isci_device = %p\n", __func__, idev); - - switch (reason) { - case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: - set_bit(IDEV_GONE, &idev->flags); - break; - case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: - set_bit(IDEV_IO_NCQERROR, &idev->flags); - - /* Suspend the remote device so the I/O can be terminated. */ - sci_remote_device_suspend(idev); - - /* Kill all outstanding requests for the device. */ - list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) { - - dev_dbg(&ihost->pdev->dev, - "%s: isci_device = %p request = %p\n", - __func__, idev, ireq); - - sci_controller_terminate_request(ihost, idev, ireq); - } - /* Fall through into the default case... */ - default: - clear_bit(IDEV_IO_READY, &idev->flags); - break; - } -} - /** * isci_remote_device_ready() - This function is called by the ihost when the * remote device is ready. We mark the isci device as ready and signal the @@ -142,49 +99,121 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote wake_up(&ihost->eventq); } -static int isci_remote_device_suspendcheck(struct isci_remote_device *idev) +static enum sci_status sci_remote_device_terminate_req( + struct isci_host *ihost, + struct isci_remote_device *idev, + int check_abort, + struct isci_request *ireq) +{ + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p; flags=%lx; req=%p; req target=%p\n", + __func__, idev, idev->flags, ireq, ireq->target_device); + + if (!test_bit(IREQ_ACTIVE, &ireq->flags) || + (ireq->target_device != idev) || + (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags))) + return SCI_SUCCESS; + + set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); + + return sci_controller_terminate_request(ihost, idev, ireq); +} + +static enum sci_status sci_remote_device_terminate_reqs_checkabort( + struct isci_remote_device *idev, + int chk) { - return test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) - || !test_bit(IDEV_ALLOCATED, &idev->flags); + struct isci_host *ihost = idev->owning_port->owning_controller; + enum sci_status status = SCI_SUCCESS; + u32 i; + + for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { + struct isci_request *ireq = ihost->reqs[i]; + enum sci_status s; + + s = sci_remote_device_terminate_req(ihost, idev, chk, ireq); + if (s != SCI_SUCCESS) + status = s; + } + return status; } -enum sci_status isci_remote_device_suspend( +enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, - struct isci_remote_device *idev) + struct isci_remote_device *idev, + struct isci_request *ireq) { - enum sci_status status; + enum sci_status status = SCI_SUCCESS; unsigned long flags; spin_lock_irqsave(&ihost->scic_lock, flags); - if (isci_get_device(idev->domain_dev) == NULL) { + if (isci_get_device(idev) == NULL) { + dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", + __func__, idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); status = SCI_FAILURE; } else { - status = sci_remote_device_suspend(idev); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - if (status == SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: idev=%p, about to wait\n", - __func__, idev); - wait_event(ihost->eventq, - isci_remote_device_suspendcheck(idev)); - status = test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) - ? SCI_SUCCESS : SCI_FAILURE; - dev_dbg(&ihost->pdev->dev, - "%s: idev=%p, wait done, device is %s\n", - __func__, idev, - test_bit(IDEV_TXRX_SUSPENDED, &idev->flags) - ? "" : ""); + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p, ireq=%p; started_request_count=%d, " + "about to wait\n", + __func__, idev, ireq, idev->started_request_count); + if (ireq) { + /* Terminate a specific TC. */ + sci_remote_device_terminate_req(ihost, idev, 0, ireq); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE, + &ireq->flags)); - } else - dev_dbg(scirdev_to_dev(idev), - "%s: sci_remote_device_suspend failed, " - "status = %d\n", __func__, status); + } else { + /* Terminate all TCs. */ + sci_remote_device_terminate_requests(idev); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + wait_event(ihost->eventq, + idev->started_request_count == 0); + } + dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", + __func__, idev); isci_put_device(idev); } return status; } +/** +* isci_remote_device_not_ready() - This function is called by the ihost when +* the remote device is not ready. We mark the isci device as ready (not +* "ready_for_io") and signal the waiting proccess. +* @isci_host: This parameter specifies the isci host object. +* @isci_device: This parameter specifies the remote device +* +* sci_lock is held on entrance to this function. +*/ +static void isci_remote_device_not_ready(struct isci_host *ihost, + struct isci_remote_device *idev, + u32 reason) +{ + dev_dbg(&ihost->pdev->dev, + "%s: isci_device = %p\n", __func__, idev); + + switch (reason) { + case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: + set_bit(IDEV_GONE, &idev->flags); + break; + case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: + set_bit(IDEV_IO_NCQERROR, &idev->flags); + + /* Suspend the remote device so the I/O can be terminated. */ + sci_remote_device_suspend(idev); + + /* Kill all outstanding requests for the device. */ + sci_remote_device_terminate_requests(idev); + + /* Fall through into the default case... */ + default: + clear_bit(IDEV_IO_READY, &idev->flags); + break; + } +} + /* called once the remote node context is ready to be freed. * The remote device can now report that its stop operation is complete. none */ @@ -196,36 +225,10 @@ static void rnc_destruct_done(void *_dev) sci_change_state(&idev->sm, SCI_DEV_STOPPED); } -static enum sci_status sci_remote_device_terminate_requests_checkabort( - struct isci_remote_device *idev, - int check_abort_pending) -{ - struct isci_host *ihost = idev->owning_port->owning_controller; - enum sci_status status = SCI_SUCCESS; - u32 i; - - for (i = 0; i < SCI_MAX_IO_REQUESTS; i++) { - struct isci_request *ireq = ihost->reqs[i]; - enum sci_status s; - - if (!test_bit(IREQ_ACTIVE, &ireq->flags) || - (ireq->target_device != idev) || - (check_abort_pending && !test_bit(IREQ_PENDING_ABORT, - &ireq->flags))) - continue; - - s = sci_controller_terminate_request(ihost, idev, ireq); - if (s != SCI_SUCCESS) - status = s; - } - - return status; -} - enum sci_status sci_remote_device_terminate_requests( struct isci_remote_device *idev) { - return sci_remote_device_terminate_requests_checkabort(idev, 0); + return sci_remote_device_terminate_reqs_checkabort(idev, 0); } enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, @@ -771,10 +774,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, if (status != SCI_SUCCESS) return status; - status = sci_remote_node_context_start_task(&idev->rnc, ireq); - if (status != SCI_SUCCESS) - goto out; - status = sci_request_start(ireq); if (status != SCI_SUCCESS) goto out; @@ -796,8 +795,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, sci_remote_node_context_suspend( &idev->rnc, SCI_SOFTWARE_SUSPENSION, SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); - sci_remote_node_context_resume( - &idev->rnc, sci_remote_device_continue_request, idev); + + status = sci_remote_node_context_start_task(&idev->rnc, ireq, + sci_remote_device_continue_request, idev); out: sci_remote_device_start_request(idev, ireq, status); @@ -811,7 +811,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, if (status != SCI_SUCCESS) return status; - status = sci_remote_node_context_start_task(&idev->rnc, ireq); + /* Resume the RNC as needed: */ + status = sci_remote_node_context_start_task(&idev->rnc, ireq, + NULL, NULL); if (status != SCI_SUCCESS) break; @@ -1322,20 +1324,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport, return status; } -void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev) -{ - DECLARE_COMPLETION_ONSTACK(aborted_task_completion); - - dev_dbg(&ihost->pdev->dev, - "%s: idev = %p\n", __func__, idev); - - /* Cleanup all requests pending for this device. */ - isci_terminate_pending_requests(ihost, idev); - - dev_dbg(&ihost->pdev->dev, - "%s: idev = %p, done\n", __func__, idev); -} - /** * This function builds the isci_remote_device when a libsas dev_found message * is received. @@ -1495,32 +1483,28 @@ int isci_remote_device_found(struct domain_device *dev) return status == SCI_SUCCESS ? 0 : -ENODEV; } -enum sci_status isci_remote_device_reset( +enum sci_status isci_remote_device_suspend_terminate( struct isci_host *ihost, - struct isci_remote_device *idev) + struct isci_remote_device *idev, + struct isci_request *ireq) { unsigned long flags; enum sci_status status; - /* Put the device into a reset state so the suspension will not - * automatically resume. - */ + /* Put the device into suspension. */ spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_reset(idev); + sci_remote_device_suspend(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_remote_device_reset(%p) returned %d!\n", - __func__, idev, status); - return status; - } - /* Wait for the device suspend. */ - status = isci_remote_device_suspend(ihost, idev); - if (status != SCI_SUCCESS) { + + /* Terminate and wait for the completions. */ + status = isci_remote_device_terminate_requests(ihost, idev, ireq); + if (status != SCI_SUCCESS) dev_dbg(&ihost->pdev->dev, - "%s: isci_remote_device_suspend(%p) returned %d!\n", + "%s: isci_remote_device_terminate_requests(%p) " + "returned %d!\n", __func__, idev, status); - } + + /* NOTE: RNC resumption is left to the caller! */ return status; } @@ -1533,7 +1517,7 @@ int isci_remote_device_is_safe_to_abort( enum sci_status sci_remote_device_abort_requests_pending_abort( struct isci_remote_device *idev) { - return sci_remote_device_terminate_requests_checkabort(idev, 1); + return sci_remote_device_terminate_reqs_checkabort(idev, 1); } enum sci_status isci_remote_device_reset_complete( @@ -1545,7 +1529,6 @@ enum sci_status isci_remote_device_reset_complete( spin_lock_irqsave(&ihost->scic_lock, flags); status = sci_remote_device_reset_complete(idev); - sci_remote_device_resume(idev, NULL, NULL); spin_unlock_irqrestore(&ihost->scic_lock, flags); return status; diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index a6a376e200e..da43698e9eb 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -85,7 +85,6 @@ struct isci_remote_device { #define IDEV_GONE 3 #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 - #define IDEV_TXRX_SUSPENDED 6 unsigned long flags; struct kref kref; struct isci_port *isci_port; @@ -107,10 +106,8 @@ struct isci_remote_device { /* device reference routines must be called under sci_lock */ static inline struct isci_remote_device *isci_get_device( - struct domain_device *dev) + struct isci_remote_device *idev) { - struct isci_remote_device *idev = dev->lldd_dev; - if (idev) kref_get(&idev->kref); return idev; @@ -378,4 +375,14 @@ enum sci_status isci_remote_device_reset( enum sci_status isci_remote_device_reset_complete( struct isci_host *ihost, struct isci_remote_device *idev); + +enum sci_status isci_remote_device_suspend_terminate( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq); + +enum sci_status isci_remote_device_terminate_requests( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq); #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 7a8347e5176..feeca17f0f1 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -317,8 +317,6 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ struct isci_remote_device *idev = rnc_to_dev(rnc); struct isci_host *ihost = idev->owning_port->owning_controller; - set_bit(IDEV_TXRX_SUSPENDED, &idev->flags); - /* Terminate outstanding requests pending abort. */ sci_remote_device_abort_requests_pending_abort(idev); @@ -326,16 +324,6 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ sci_remote_node_context_continue_state_transitions(rnc); } -static void sci_remote_node_context_tx_rx_suspended_state_exit( - struct sci_base_state_machine *sm) -{ - struct sci_remote_node_context *rnc - = container_of(sm, typeof(*rnc), sm); - struct isci_remote_device *idev = rnc_to_dev(rnc); - - clear_bit(IDEV_TXRX_SUSPENDED, &idev->flags); -} - static void sci_remote_node_context_await_suspend_state_exit( struct sci_base_state_machine *sm) { @@ -366,8 +354,6 @@ static const struct sci_base_state sci_remote_node_context_state_table[] = { }, [SCI_RNC_TX_RX_SUSPENDED] = { .enter_state = sci_remote_node_context_tx_rx_suspended_state_enter, - .exit_state - = sci_remote_node_context_tx_rx_suspended_state_exit, }, [SCI_RNC_AWAIT_SUSPENSION] = { .exit_state = sci_remote_node_context_await_suspend_state_exit, @@ -671,8 +657,11 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context } } -enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc, - struct isci_request *ireq) +enum sci_status sci_remote_node_context_start_task( + struct sci_remote_node_context *sci_rnc, + struct isci_request *ireq, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p) { enum scis_sds_remote_node_context_states state; @@ -684,7 +673,7 @@ enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_contex return SCI_SUCCESS; case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: - sci_remote_node_context_resume(sci_rnc, NULL, NULL); + sci_remote_node_context_resume(sci_rnc, cb_fn, cb_p); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 5ddf88b5313..2870af14eda 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -211,7 +211,9 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s scics_sds_remote_node_context_callback cb_fn, void *cb_p); enum sci_status sci_remote_node_context_start_task(struct sci_remote_node_context *sci_rnc, - struct isci_request *ireq); + struct isci_request *ireq, + scics_sds_remote_node_context_callback cb_fn, + void *cb_p); enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context *sci_rnc, struct isci_request *ireq); int sci_remote_node_context_is_safe_to_abort( diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 1f314d0d71d..f4e80f31423 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2491,9 +2491,6 @@ static void isci_request_process_response_iu( * @request: This parameter is the completed isci_request object. * @response_ptr: This parameter specifies the service response for the I/O. * @status_ptr: This parameter specifies the exec status for the I/O. - * @complete_to_host_ptr: This parameter specifies the action to be taken by - * the LLDD with respect to completing this request or forcing an abort - * condition on the I/O. * @open_rej_reason: This parameter specifies the encoded reason for the * abandon-class reject. * @@ -2504,14 +2501,12 @@ static void isci_request_set_open_reject_status( struct sas_task *task, enum service_response *response_ptr, enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr, enum sas_open_rej_reason open_rej_reason) { /* Task in the target is done. */ set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); *response_ptr = SAS_TASK_UNDELIVERED; *status_ptr = SAS_OPEN_REJECT; - *complete_to_host_ptr = isci_perform_normal_io_completion; task->task_status.open_rej_reason = open_rej_reason; } @@ -2521,9 +2516,6 @@ static void isci_request_set_open_reject_status( * @request: This parameter is the completed isci_request object. * @response_ptr: This parameter specifies the service response for the I/O. * @status_ptr: This parameter specifies the exec status for the I/O. - * @complete_to_host_ptr: This parameter specifies the action to be taken by - * the LLDD with respect to completing this request or forcing an abort - * condition on the I/O. * * none. */ @@ -2532,8 +2524,7 @@ static void isci_request_handle_controller_specific_errors( struct isci_request *request, struct sas_task *task, enum service_response *response_ptr, - enum exec_status *status_ptr, - enum isci_completion_selection *complete_to_host_ptr) + enum exec_status *status_ptr) { unsigned int cstatus; @@ -2574,9 +2565,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_normal_io_completion; } else { /* Task in the target is not done. */ *response_ptr = SAS_TASK_UNDELIVERED; @@ -2587,9 +2575,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAM_STAT_TASK_ABORTED; clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = - isci_perform_error_io_completion; } break; @@ -2618,8 +2603,6 @@ static void isci_request_handle_controller_specific_errors( *status_ptr = SAS_ABORTED_TASK; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_normal_io_completion; break; @@ -2630,7 +2613,7 @@ static void isci_request_handle_controller_specific_errors( isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_WRONG_DEST); + SAS_OREJ_WRONG_DEST); break; case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: @@ -2640,56 +2623,56 @@ static void isci_request_handle_controller_specific_errors( */ isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB0); + SAS_OREJ_RESV_AB0); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB1); + SAS_OREJ_RESV_AB1); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB2); + SAS_OREJ_RESV_AB2); break; case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_RESV_AB3); + SAS_OREJ_RESV_AB3); break; case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_BAD_DEST); + SAS_OREJ_BAD_DEST); break; case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_STP_NORES); + SAS_OREJ_STP_NORES); break; case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_EPROTO); + SAS_OREJ_EPROTO); break; case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: isci_request_set_open_reject_status( request, task, response_ptr, status_ptr, - complete_to_host_ptr, SAS_OREJ_CONN_RATE); + SAS_OREJ_CONN_RATE); break; case SCU_TASK_DONE_LL_R_ERR: @@ -2721,95 +2704,12 @@ static void isci_request_handle_controller_specific_errors( *response_ptr = SAS_TASK_UNDELIVERED; *status_ptr = SAM_STAT_TASK_ABORTED; - if (task->task_proto == SAS_PROTOCOL_SMP) { + if (task->task_proto == SAS_PROTOCOL_SMP) set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_normal_io_completion; - } else { + else clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - *complete_to_host_ptr = isci_perform_error_io_completion; - } - break; - } -} - -/** - * isci_task_save_for_upper_layer_completion() - This function saves the - * request for later completion to the upper layer driver. - * @host: This parameter is a pointer to the host on which the the request - * should be queued (either as an error or success). - * @request: This parameter is the completed request. - * @response: This parameter is the response code for the completed task. - * @status: This parameter is the status code for the completed task. - * - * none. - */ -static void isci_task_save_for_upper_layer_completion( - struct isci_host *host, - struct isci_request *request, - enum service_response response, - enum exec_status status, - enum isci_completion_selection task_notification_selection) -{ - struct sas_task *task = isci_request_access_task(request); - - task_notification_selection - = isci_task_set_completion_status(task, response, status, - task_notification_selection); - - /* Tasks aborted specifically by a call to the lldd_abort_task - * function should not be completed to the host in the regular path. - */ - switch (task_notification_selection) { - - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - - /* Add to the completed list. */ - list_add(&request->completed_node, - &host->requests_to_complete); - - /* Take the request off the device's pending request list. */ - list_del_init(&request->dev_node); - break; - - case isci_perform_aborted_io_completion: - /* No notification to libsas because this request is - * already in the abort path. - */ - /* Wake up whatever process was waiting for this - * request to complete. - */ - WARN_ON(request->io_request_completion == NULL); - - if (request->io_request_completion != NULL) { - - /* Signal whoever is waiting that this - * request is complete. - */ - complete(request->io_request_completion); - } - break; - - case isci_perform_error_io_completion: - /* Use sas_task_abort */ - /* Add to the aborted list. */ - list_add(&request->completed_node, - &host->requests_to_errorback); - break; - - default: - /* Add to the error to libsas list. */ - list_add(&request->completed_node, - &host->requests_to_errorback); break; } - dev_dbg(&host->pdev->dev, - "%s: %d - task = %p, response=%d (%d), status=%d (%d)\n", - __func__, task_notification_selection, task, - (task) ? task->task_status.resp : 0, response, - (task) ? task->task_status.stat : 0, status); } static void isci_process_stp_response(struct sas_task *task, struct dev_to_host_fis *fis) @@ -2844,9 +2744,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, struct isci_remote_device *idev = request->target_device; enum service_response response = SAS_TASK_UNDELIVERED; enum exec_status status = SAS_ABORTED_TASK; - enum isci_request_status request_status; - enum isci_completion_selection complete_to_host - = isci_perform_normal_io_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, task = %p,\n" @@ -2857,282 +2754,158 @@ static void isci_request_io_request_complete(struct isci_host *ihost, task->data_dir, completion_status); - spin_lock(&request->state_lock); - request_status = request->status; - - /* Decode the request status. Note that if the request has been - * aborted by a task management function, we don't care - * what the status is. - */ - switch (request_status) { - - case aborted: - /* "aborted" indicates that the request was aborted by a task - * management function, since once a task management request is - * perfomed by the device, the request only completes because - * of the subsequent driver terminate. - * - * Aborted also means an external thread is explicitly managing - * this request, so that we do not complete it up the stack. - * - * The target is still there (since the TMF was successful). - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_COMPLETE; + /* The request is done from an SCU HW perspective. */ - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; + /* This is an active request being completed from the core. */ + switch (completion_status) { - complete_to_host = isci_perform_aborted_io_completion; - /* This was an aborted request. */ + case SCI_IO_FAILURE_RESPONSE_VALID: + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", + __func__, request, task); - spin_unlock(&request->state_lock); - break; + if (sas_protocol_ata(task->task_proto)) { + isci_process_stp_response(task, &request->stp.rsp); + } else if (SAS_PROTOCOL_SSP == task->task_proto) { - case aborting: - /* aborting means that the task management function tried and - * failed to abort the request. We need to note the request - * as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the - * target as down. - * - * Aborting also means an external thread is explicitly managing - * this request, so that we do not complete it up the stack. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; + /* crack the iu response buffer. */ + resp_iu = &request->ssp.rsp; + isci_request_process_response_iu(task, resp_iu, + &ihost->pdev->dev); - if (!idev) - /* The device has been /is being stopped. Note that - * we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_PHY_DOWN; + } else if (SAS_PROTOCOL_SMP == task->task_proto) { - complete_to_host = isci_perform_aborted_io_completion; + dev_err(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID: " + "SAS_PROTOCOL_SMP protocol\n", + __func__); - /* This was an aborted request. */ + } else + dev_err(&ihost->pdev->dev, + "%s: unknown protocol\n", __func__); - spin_unlock(&request->state_lock); + /* use the task status set in the task struct by the + * isci_request_process_response_iu call. + */ + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + response = task->task_status.resp; + status = task->task_status.stat; break; - case terminating: + case SCI_IO_SUCCESS: + case SCI_IO_SUCCESS_IO_DONE_EARLY: - /* This was an terminated request. This happens when - * the I/O is being terminated because of an action on - * the device (reset, tear down, etc.), and the I/O needs - * to be completed up the stack. - */ + response = SAS_TASK_COMPLETE; + status = SAM_STAT_GOOD; set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; - - complete_to_host = isci_perform_aborted_io_completion; - - /* This was a terminated request. */ - - spin_unlock(&request->state_lock); - break; + if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { - case dead: - /* This was a terminated request that timed-out during the - * termination process. There is no task to complete to - * libsas. - */ - complete_to_host = isci_perform_normal_io_completion; - spin_unlock(&request->state_lock); - break; - - default: - - /* The request is done from an SCU HW perspective. */ - request->status = completed; + /* This was an SSP / STP / SATA transfer. + * There is a possibility that less data than + * the maximum was transferred. + */ + u32 transferred_length = sci_req_tx_bytes(request); - spin_unlock(&request->state_lock); + task->task_status.residual + = task->total_xfer_len - transferred_length; - /* This is an active request being completed from the core. */ - switch (completion_status) { + /* If there were residual bytes, call this an + * underrun. + */ + if (task->task_status.residual != 0) + status = SAS_DATA_UNDERRUN; - case SCI_IO_FAILURE_RESPONSE_VALID: dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", - __func__, - request, - task); - - if (sas_protocol_ata(task->task_proto)) { - isci_process_stp_response(task, &request->stp.rsp); - } else if (SAS_PROTOCOL_SSP == task->task_proto) { - - /* crack the iu response buffer. */ - resp_iu = &request->ssp.rsp; - isci_request_process_response_iu(task, resp_iu, - &ihost->pdev->dev); - - } else if (SAS_PROTOCOL_SMP == task->task_proto) { + "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", + __func__, status); - dev_err(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_RESPONSE_VALID: " - "SAS_PROTOCOL_SMP protocol\n", - __func__); - - } else - dev_err(&ihost->pdev->dev, - "%s: unknown protocol\n", __func__); - - /* use the task status set in the task struct by the - * isci_request_process_response_iu call. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = task->task_status.resp; - status = task->task_status.stat; - break; - - case SCI_IO_SUCCESS: - case SCI_IO_SUCCESS_IO_DONE_EARLY: - - response = SAS_TASK_COMPLETE; - status = SAM_STAT_GOOD; - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - - if (completion_status == SCI_IO_SUCCESS_IO_DONE_EARLY) { - - /* This was an SSP / STP / SATA transfer. - * There is a possibility that less data than - * the maximum was transferred. - */ - u32 transferred_length = sci_req_tx_bytes(request); - - task->task_status.residual - = task->total_xfer_len - transferred_length; + } else + dev_dbg(&ihost->pdev->dev, "%s: SCI_IO_SUCCESS\n", + __func__); + break; - /* If there were residual bytes, call this an - * underrun. - */ - if (task->task_status.residual != 0) - status = SAS_DATA_UNDERRUN; + case SCI_IO_FAILURE_TERMINATED: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", - __func__, - status); + dev_dbg(&ihost->pdev->dev, + "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", + __func__, request, task); - } else - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_SUCCESS\n", - __func__); + /* The request was terminated explicitly. */ + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + response = SAS_TASK_UNDELIVERED; - break; + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + break; - case SCI_IO_FAILURE_TERMINATED: - dev_dbg(&ihost->pdev->dev, - "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", - __func__, - request, - task); + case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: - /* The request was terminated explicitly. No handling - * is needed in the SCSI error handler path. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - response = SAS_TASK_UNDELIVERED; + isci_request_handle_controller_specific_errors(idev, request, + task, &response, + &status); + break; - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; + case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: + /* This is a special case, in that the I/O completion + * is telling us that the device needs a reset. + * In order for the device reset condition to be + * noticed, the I/O has to be handled in the error + * handler. Set the reset flag and cause the + * SCSI error thread to be scheduled. + */ + spin_lock_irqsave(&task->task_state_lock, task_flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, task_flags); - complete_to_host = isci_perform_normal_io_completion; - break; + /* Fail the I/O. */ + response = SAS_TASK_UNDELIVERED; + status = SAM_STAT_TASK_ABORTED; - case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - isci_request_handle_controller_specific_errors( - idev, request, task, &response, &status, - &complete_to_host); + case SCI_FAILURE_RETRY_REQUIRED: - break; + /* Fail the I/O so it can be retried. */ + response = SAS_TASK_UNDELIVERED; + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; - case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: - /* This is a special case, in that the I/O completion - * is telling us that the device needs a reset. - * In order for the device reset condition to be - * noticed, the I/O has to be handled in the error - * handler. Set the reset flag and cause the - * SCSI error thread to be scheduled. - */ - spin_lock_irqsave(&task->task_state_lock, task_flags); - task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; - spin_unlock_irqrestore(&task->task_state_lock, task_flags); + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + break; - /* Fail the I/O. */ - response = SAS_TASK_UNDELIVERED; - status = SAM_STAT_TASK_ABORTED; - complete_to_host = isci_perform_error_io_completion; - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; + default: + /* Catch any otherwise unhandled error codes here. */ + dev_dbg(&ihost->pdev->dev, + "%s: invalid completion code: 0x%x - " + "isci_request = %p\n", + __func__, completion_status, request); - case SCI_FAILURE_RETRY_REQUIRED: + response = SAS_TASK_UNDELIVERED; - /* Fail the I/O so it can be retried. */ - response = SAS_TASK_UNDELIVERED; - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if (!idev) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; - complete_to_host = isci_perform_normal_io_completion; + if (SAS_PROTOCOL_SMP == task->task_proto) set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - break; - - - default: - /* Catch any otherwise unhandled error codes here. */ - dev_dbg(&ihost->pdev->dev, - "%s: invalid completion code: 0x%x - " - "isci_request = %p\n", - __func__, completion_status, request); - - response = SAS_TASK_UNDELIVERED; - - /* See if the device has been/is being stopped. Note - * that we ignore the quiesce state, since we are - * concerned about the actual device state. - */ - if (!idev) - status = SAS_DEVICE_UNKNOWN; - else - status = SAS_ABORTED_TASK; - - if (SAS_PROTOCOL_SMP == task->task_proto) { - set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_normal_io_completion; - } else { - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); - complete_to_host = isci_perform_error_io_completion; - } - break; - } + else + clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); break; } @@ -3167,10 +2940,24 @@ static void isci_request_io_request_complete(struct isci_host *ihost, break; } - /* Put the completed request on the correct list */ - isci_task_save_for_upper_layer_completion(ihost, request, response, - status, complete_to_host - ); + spin_lock_irqsave(&task->task_state_lock, task_flags); + + task->task_status.resp = response; + task->task_status.stat = status; + + if (test_bit(IREQ_COMPLETE_IN_TARGET, &request->flags)) { + /* Normal notification (task_done) */ + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + } + spin_unlock_irqrestore(&task->task_state_lock, task_flags); + + /* Add to the completed list. */ + list_add(&request->completed_node, &ihost->requests_to_complete); + + /* Take the request off the device's pending request list. */ + list_del_init(&request->dev_node); /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); @@ -3626,7 +3413,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t ireq->num_sg_entries = 0; INIT_LIST_HEAD(&ireq->completed_node); INIT_LIST_HEAD(&ireq->dev_node); - isci_request_change_state(ireq, allocated); return ireq; } @@ -3721,15 +3507,12 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide */ list_add(&ireq->dev_node, &idev->reqs_in_process); - if (status == SCI_SUCCESS) { - isci_request_change_state(ireq, started); - } else { + if (status != SCI_SUCCESS) { /* The request did not really start in the * hardware, so clear the request handle * here so no terminations will be done. */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_request_change_state(ireq, completed); } spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 8d55f78010a..f3116a51235 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -60,23 +60,6 @@ #include "host.h" #include "scu_task_context.h" -/** - * struct isci_request_status - This enum defines the possible states of an I/O - * request. - * - * - */ -enum isci_request_status { - unallocated = 0x00, - allocated = 0x01, - started = 0x02, - completed = 0x03, - aborting = 0x04, - aborted = 0x05, - terminating = 0x06, - dead = 0x07 -}; - /** * isci_stp_request - extra request infrastructure to handle pio/atapi protocol * @pio_len - number of bytes requested at PIO setup @@ -97,13 +80,13 @@ struct isci_stp_request { }; struct isci_request { - enum isci_request_status status; #define IREQ_COMPLETE_IN_TARGET 0 #define IREQ_TERMINATED 1 #define IREQ_TMF 2 #define IREQ_ACTIVE 3 #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ #define IREQ_TC_ABORT_POSTED 5 + #define IREQ_ABORT_PATH_ACTIVE 6 unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { @@ -115,7 +98,6 @@ struct isci_request { struct list_head completed_node; /* For use in the reqs_in_process list: */ struct list_head dev_node; - spinlock_t state_lock; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; unsigned int num_sg_entries; @@ -304,92 +286,6 @@ sci_io_request_get_dma_addr(struct isci_request *ireq, void *virt_addr) return ireq->request_daddr + (requested_addr - base_addr); } -/** - * isci_request_change_state() - This function sets the status of the request - * object. - * @request: This parameter points to the isci_request object - * @status: This Parameter is the new status of the object - * - */ -static inline enum isci_request_status -isci_request_change_state(struct isci_request *isci_request, - enum isci_request_status status) -{ - enum isci_request_status old_state; - unsigned long flags; - - dev_dbg(&isci_request->isci_host->pdev->dev, - "%s: isci_request = %p, state = 0x%x\n", - __func__, - isci_request, - status); - - BUG_ON(isci_request == NULL); - - spin_lock_irqsave(&isci_request->state_lock, flags); - old_state = isci_request->status; - isci_request->status = status; - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - return old_state; -} - -/** - * isci_request_change_started_to_newstate() - This function sets the status of - * the request object. - * @request: This parameter points to the isci_request object - * @status: This Parameter is the new status of the object - * - * state previous to any change. - */ -static inline enum isci_request_status -isci_request_change_started_to_newstate(struct isci_request *isci_request, - struct completion *completion_ptr, - enum isci_request_status newstate) -{ - enum isci_request_status old_state; - unsigned long flags; - - spin_lock_irqsave(&isci_request->state_lock, flags); - - old_state = isci_request->status; - - if (old_state == started || old_state == aborting) { - BUG_ON(isci_request->io_request_completion != NULL); - - isci_request->io_request_completion = completion_ptr; - isci_request->status = newstate; - } - - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - dev_dbg(&isci_request->isci_host->pdev->dev, - "%s: isci_request = %p, old_state = 0x%x\n", - __func__, - isci_request, - old_state); - - return old_state; -} - -/** - * isci_request_change_started_to_aborted() - This function sets the status of - * the request object. - * @request: This parameter points to the isci_request object - * @completion_ptr: This parameter is saved as the kernel completion structure - * signalled when the old request completes. - * - * state previous to any change. - */ -static inline enum isci_request_status -isci_request_change_started_to_aborted(struct isci_request *isci_request, - struct completion *completion_ptr) -{ - return isci_request_change_started_to_newstate(isci_request, - completion_ptr, - aborted); -} - #define isci_request_access_task(req) ((req)->ttype_ptr.io_task_ptr) #define isci_request_access_tmf(req) ((req)->ttype_ptr.tmf_task_ptr) @@ -399,8 +295,6 @@ struct isci_request *isci_tmf_request_from_tag(struct isci_host *ihost, u16 tag); int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *idev, struct sas_task *task, u16 tag); -void isci_terminate_pending_requests(struct isci_host *ihost, - struct isci_remote_device *idev); enum sci_status sci_task_request_construct(struct isci_host *ihost, struct isci_remote_device *idev, diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 26de06ef688..29ce8815e79 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -78,54 +78,25 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, enum exec_status status) { - enum isci_completion_selection disposition; + unsigned long flags; - disposition = isci_perform_normal_io_completion; - disposition = isci_task_set_completion_status(task, response, status, - disposition); + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n", + __func__, task, response, status); - /* Tasks aborted specifically by a call to the lldd_abort_task - * function should not be completed to the host in the regular path. - */ - switch (disposition) { - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - dev_dbg(&ihost->pdev->dev, - "%s: Normal - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - - task->lldd_task = NULL; - task->task_done(task); - break; - - case isci_perform_aborted_io_completion: - /* - * No notification because this request is already in the - * abort path. - */ - dev_dbg(&ihost->pdev->dev, - "%s: Aborted - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - break; + spin_lock_irqsave(&task->task_state_lock, flags); - case isci_perform_error_io_completion: - /* Use sas_task_abort */ - dev_dbg(&ihost->pdev->dev, - "%s: Error - task = %p, response=%d, " - "status=%d\n", - __func__, task, response, status); - sas_task_abort(task); - break; + task->task_status.resp = response; + task->task_status.stat = status; - default: - dev_dbg(&ihost->pdev->dev, - "%s: isci task notification default case!", - __func__); - sas_task_abort(task); - break; - } + /* Normal notification (task_done) */ + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + task->lldd_task = NULL; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + task->task_done(task); } #define for_each_sas_task(num, task) \ @@ -289,60 +260,6 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost, return ireq; } -/** -* isci_request_mark_zombie() - This function must be called with scic_lock held. -*/ -static void isci_request_mark_zombie(struct isci_host *ihost, struct isci_request *ireq) -{ - struct completion *tmf_completion = NULL; - struct completion *req_completion; - - /* Set the request state to "dead". */ - ireq->status = dead; - - req_completion = ireq->io_request_completion; - ireq->io_request_completion = NULL; - - if (test_bit(IREQ_TMF, &ireq->flags)) { - /* Break links with the TMF request. */ - struct isci_tmf *tmf = isci_request_access_tmf(ireq); - - /* In the case where a task request is dying, - * the thread waiting on the complete will sit and - * timeout unless we wake it now. Since the TMF - * has a default error status, complete it here - * to wake the waiting thread. - */ - if (tmf) { - tmf_completion = tmf->complete; - tmf->complete = NULL; - } - ireq->ttype_ptr.tmf_task_ptr = NULL; - dev_dbg(&ihost->pdev->dev, "%s: tmf_code %d, managed tag %#x\n", - __func__, tmf->tmf_code, tmf->io_tag); - } else { - /* Break links with the sas_task - the callback is done - * elsewhere. - */ - struct sas_task *task = isci_request_access_task(ireq); - - if (task) - task->lldd_task = NULL; - - ireq->ttype_ptr.io_task_ptr = NULL; - } - - dev_warn(&ihost->pdev->dev, "task context unrecoverable (tag: %#x)\n", - ireq->io_tag); - - /* Don't force waiting threads to timeout. */ - if (req_completion) - complete(req_completion); - - if (tmf_completion != NULL) - complete(tmf_completion); -} - static int isci_task_execute_tmf(struct isci_host *ihost, struct isci_remote_device *idev, struct isci_tmf *tmf, unsigned long timeout_ms) @@ -400,15 +317,12 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - - if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data); - - isci_request_change_state(ireq, started); - /* add the request to the remote device request list. */ list_add(&ireq->dev_node, &idev->reqs_in_process); + /* The RNC must be unsuspended before the TMF can get a response. */ + sci_remote_device_resume(idev, NULL, NULL); + spin_unlock_irqrestore(&ihost->scic_lock, flags); /* Wait for the TMF to complete, or a timeout. */ @@ -419,32 +333,7 @@ static int isci_task_execute_tmf(struct isci_host *ihost, /* The TMF did not complete - this could be because * of an unplug. Terminate the TMF request now. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - - if (tmf->cb_state_func != NULL) - tmf->cb_state_func(isci_tmf_timed_out, tmf, - tmf->cb_data); - - sci_controller_terminate_request(ihost, idev, ireq); - - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - timeleft = wait_for_completion_timeout( - &completion, - msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); - - if (!timeleft) { - /* Strange condition - the termination of the TMF - * request timed-out. - */ - spin_lock_irqsave(&ihost->scic_lock, flags); - - /* If the TMF status has not changed, kill it. */ - if (tmf->status == SCI_FAILURE_TIMEOUT) - isci_request_mark_zombie(ihost, ireq); - - spin_unlock_irqrestore(&ihost->scic_lock, flags); - } + isci_remote_device_suspend_terminate(ihost, idev, ireq); } isci_print_tmf(ihost, tmf); @@ -476,316 +365,20 @@ static int isci_task_execute_tmf(struct isci_host *ihost, } static void isci_task_build_tmf(struct isci_tmf *tmf, - enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), - void *cb_data) + enum isci_tmf_function_codes code) { memset(tmf, 0, sizeof(*tmf)); - - tmf->tmf_code = code; - tmf->cb_state_func = tmf_sent_cb; - tmf->cb_data = cb_data; + tmf->tmf_code = code; } static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf, enum isci_tmf_function_codes code, - void (*tmf_sent_cb)(enum isci_tmf_cb_state, - struct isci_tmf *, - void *), struct isci_request *old_request) { - isci_task_build_tmf(tmf, code, tmf_sent_cb, old_request); + isci_task_build_tmf(tmf, code); tmf->io_tag = old_request->io_tag; } -/** - * isci_task_validate_request_to_abort() - This function checks the given I/O - * against the "started" state. If the request is still "started", it's - * state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD - * BEFORE CALLING THIS FUNCTION. - * @isci_request: This parameter specifies the request object to control. - * @isci_host: This parameter specifies the ISCI host object - * @isci_device: This is the device to which the request is pending. - * @aborted_io_completion: This is a completion structure that will be added to - * the request in case it is changed to aborting; this completion is - * triggered when the request is fully completed. - * - * Either "started" on successful change of the task status to "aborted", or - * "unallocated" if the task cannot be controlled. - */ -static enum isci_request_status isci_task_validate_request_to_abort( - struct isci_request *isci_request, - struct isci_host *isci_host, - struct isci_remote_device *isci_device, - struct completion *aborted_io_completion) -{ - enum isci_request_status old_state = unallocated; - - /* Only abort the task if it's in the - * device's request_in_process list - */ - if (isci_request && !list_empty(&isci_request->dev_node)) { - old_state = isci_request_change_started_to_aborted( - isci_request, aborted_io_completion); - - } - - return old_state; -} - -static int isci_request_is_dealloc_managed(enum isci_request_status stat) -{ - switch (stat) { - case aborted: - case aborting: - case terminating: - case completed: - case dead: - return true; - default: - return false; - } -} - -/** - * isci_terminate_request_core() - This function will terminate the given - * request, and wait for it to complete. This function must only be called - * from a thread that can wait. Note that the request is terminated and - * completed (back to the host, if started there). - * @ihost: This SCU. - * @idev: The target. - * @isci_request: The I/O request to be terminated. - * - */ -static void isci_terminate_request_core(struct isci_host *ihost, - struct isci_remote_device *idev, - struct isci_request *isci_request) -{ - enum sci_status status = SCI_SUCCESS; - bool was_terminated = false; - bool needs_cleanup_handling = false; - unsigned long flags; - unsigned long termination_completed = 1; - struct completion *io_request_completion; - - dev_dbg(&ihost->pdev->dev, - "%s: device = %p; request = %p\n", - __func__, idev, isci_request); - - spin_lock_irqsave(&ihost->scic_lock, flags); - - io_request_completion = isci_request->io_request_completion; - - /* Note that we are not going to control - * the target to abort the request. - */ - set_bit(IREQ_COMPLETE_IN_TARGET, &isci_request->flags); - - /* Make sure the request wasn't just sitting around signalling - * device condition (if the request handle is NULL, then the - * request completed but needed additional handling here). - */ - if (!test_bit(IREQ_TERMINATED, &isci_request->flags)) { - was_terminated = true; - needs_cleanup_handling = true; - status = sci_controller_terminate_request(ihost, - idev, - isci_request); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - /* - * The only time the request to terminate will - * fail is when the io request is completed and - * being aborted. - */ - if (status != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: sci_controller_terminate_request" - " returned = 0x%x\n", - __func__, status); - - isci_request->io_request_completion = NULL; - - } else { - if (was_terminated) { - dev_dbg(&ihost->pdev->dev, - "%s: before completion wait (%p/%p)\n", - __func__, isci_request, io_request_completion); - - /* Wait here for the request to complete. */ - termination_completed - = wait_for_completion_timeout( - io_request_completion, - msecs_to_jiffies(ISCI_TERMINATION_TIMEOUT_MSEC)); - - if (!termination_completed) { - - /* The request to terminate has timed out. */ - spin_lock_irqsave(&ihost->scic_lock, flags); - - /* Check for state changes. */ - if (!test_bit(IREQ_TERMINATED, - &isci_request->flags)) { - - /* The best we can do is to have the - * request die a silent death if it - * ever really completes. - */ - isci_request_mark_zombie(ihost, - isci_request); - needs_cleanup_handling = true; - } else - termination_completed = 1; - - spin_unlock_irqrestore(&ihost->scic_lock, - flags); - - if (!termination_completed) { - - dev_dbg(&ihost->pdev->dev, - "%s: *** Timeout waiting for " - "termination(%p/%p)\n", - __func__, io_request_completion, - isci_request); - - /* The request can no longer be referenced - * safely since it may go away if the - * termination every really does complete. - */ - isci_request = NULL; - } - } - if (termination_completed) - dev_dbg(&ihost->pdev->dev, - "%s: after completion wait (%p/%p)\n", - __func__, isci_request, io_request_completion); - } - - if (termination_completed) { - - isci_request->io_request_completion = NULL; - - /* Peek at the status of the request. This will tell - * us if there was special handling on the request such that it - * needs to be detached and freed here. - */ - spin_lock_irqsave(&isci_request->state_lock, flags); - - needs_cleanup_handling - = isci_request_is_dealloc_managed( - isci_request->status); - - spin_unlock_irqrestore(&isci_request->state_lock, flags); - - } - if (needs_cleanup_handling) { - - dev_dbg(&ihost->pdev->dev, - "%s: cleanup isci_device=%p, request=%p\n", - __func__, idev, isci_request); - - if (isci_request != NULL) { - spin_lock_irqsave(&ihost->scic_lock, flags); - isci_free_tag(ihost, isci_request->io_tag); - isci_request_change_state(isci_request, unallocated); - list_del_init(&isci_request->dev_node); - spin_unlock_irqrestore(&ihost->scic_lock, flags); - } - } - } -} - -/** - * isci_terminate_pending_requests() - This function will change the all of the - * requests on the given device's state to "aborting", will terminate the - * requests, and wait for them to complete. This function must only be - * called from a thread that can wait. Note that the requests are all - * terminated and completed (back to the host, if started there). - * @isci_host: This parameter specifies SCU. - * @idev: This parameter specifies the target. - * - */ -void isci_terminate_pending_requests(struct isci_host *ihost, - struct isci_remote_device *idev) -{ - struct completion request_completion; - enum isci_request_status old_state; - unsigned long flags; - LIST_HEAD(list); - - isci_remote_device_suspend(ihost, idev); - - spin_lock_irqsave(&ihost->scic_lock, flags); - list_splice_init(&idev->reqs_in_process, &list); - - /* assumes that isci_terminate_request_core deletes from the list */ - while (!list_empty(&list)) { - struct isci_request *ireq = list_entry(list.next, typeof(*ireq), dev_node); - - /* Change state to "terminating" if it is currently - * "started". - */ - old_state = isci_request_change_started_to_newstate(ireq, - &request_completion, - terminating); - switch (old_state) { - case started: - case completed: - case aborting: - break; - default: - /* termination in progress, or otherwise dispositioned. - * We know the request was on 'list' so should be safe - * to move it back to reqs_in_process - */ - list_move(&ireq->dev_node, &idev->reqs_in_process); - ireq = NULL; - break; - } - - if (!ireq) - continue; - spin_unlock_irqrestore(&ihost->scic_lock, flags); - - init_completion(&request_completion); - - dev_dbg(&ihost->pdev->dev, - "%s: idev=%p request=%p; task=%p old_state=%d\n", - __func__, idev, ireq, - (!test_bit(IREQ_TMF, &ireq->flags) - ? isci_request_access_task(ireq) - : NULL), - old_state); - - /* If the old_state is started: - * This request was not already being aborted. If it had been, - * then the aborting I/O (ie. the TMF request) would not be in - * the aborting state, and thus would be terminated here. Note - * that since the TMF completion's call to the kernel function - * "complete()" does not happen until the pending I/O request - * terminate fully completes, we do not have to implement a - * special wait here for already aborting requests - the - * termination of the TMF request will force the request - * to finish it's already started terminate. - * - * If old_state == completed: - * This request completed from the SCU hardware perspective - * and now just needs cleaning up in terms of freeing the - * request and potentially calling up to libsas. - * - * If old_state == aborting: - * This request has already gone through a TMF timeout, but may - * not have been terminated; needs cleaning up at least. - */ - isci_terminate_request_core(ihost, idev, ireq); - spin_lock_irqsave(&ihost->scic_lock, flags); - } - spin_unlock_irqrestore(&ihost->scic_lock, flags); -} - /** * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain * Template functions. @@ -809,7 +402,7 @@ static int isci_task_send_lu_reset_sas( * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED"). */ - isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset, NULL, NULL); + isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset); #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */ ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS); @@ -829,48 +422,41 @@ static int isci_task_send_lu_reset_sas( int isci_task_lu_reset(struct domain_device *dev, u8 *lun) { struct isci_host *ihost = dev_to_ihost(dev); - struct isci_remote_device *isci_device; + struct isci_remote_device *idev; unsigned long flags; int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - isci_device = isci_lookup_device(dev); + idev = isci_lookup_device(dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); dev_dbg(&ihost->pdev->dev, "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", - __func__, dev, ihost, isci_device); + __func__, dev, ihost, idev); - if (!isci_device) { + if (!idev) { /* If the device is gone, escalate to I_T_Nexus_Reset. */ dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); ret = TMF_RESP_FUNC_FAILED; goto out; } - if (isci_remote_device_suspend(ihost, isci_device) != SCI_SUCCESS) { - dev_dbg(&ihost->pdev->dev, - "%s: device = %p; failed to suspend\n", - __func__, isci_device); - ret = TMF_RESP_FUNC_FAILED; - goto out; - } - /* Send the task management part of the reset. */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); ret = TMF_RESP_FUNC_COMPLETE; - } else - ret = isci_task_send_lu_reset_sas(ihost, isci_device, lun); - - /* If the LUN reset worked, all the I/O can now be terminated. */ - if (ret == TMF_RESP_FUNC_COMPLETE) { - /* Terminate all I/O now. */ - isci_terminate_pending_requests(ihost, isci_device); - isci_remote_device_resume(ihost, isci_device, NULL, NULL); + } else { + /* Suspend the RNC, kill all TCs */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { + ret = TMF_RESP_FUNC_FAILED; + goto out; + } + /* Send the task management part of the reset. */ + ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } out: - isci_put_device(isci_device); + isci_put_device(idev); return ret; } @@ -890,63 +476,6 @@ int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) /* Task Management Functions. Must be called from process context. */ -/** - * isci_abort_task_process_cb() - This is a helper function for the abort task - * TMF command. It manages the request state with respect to the successful - * transmission / completion of the abort task request. - * @cb_state: This parameter specifies when this function was called - after - * the TMF request has been started and after it has timed-out. - * @tmf: This parameter specifies the TMF in progress. - * - * - */ -static void isci_abort_task_process_cb( - enum isci_tmf_cb_state cb_state, - struct isci_tmf *tmf, - void *cb_data) -{ - struct isci_request *old_request; - - old_request = (struct isci_request *)cb_data; - - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: tmf=%p, old_request=%p\n", - __func__, tmf, old_request); - - switch (cb_state) { - - case isci_tmf_started: - /* The TMF has been started. Nothing to do here, since the - * request state was already set to "aborted" by the abort - * task function. - */ - if ((old_request->status != aborted) - && (old_request->status != completed)) - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: Bad request status (%d): tmf=%p, old_request=%p\n", - __func__, old_request->status, tmf, old_request); - break; - - case isci_tmf_timed_out: - - /* Set the task's state to "aborting", since the abort task - * function thread set it to "aborted" (above) in anticipation - * of the task management request working correctly. Since the - * timeout has now fired, the TMF request failed. We set the - * state such that the request completion will indicate the - * device is no longer present. - */ - isci_request_change_state(old_request, aborting); - break; - - default: - dev_dbg(&old_request->isci_host->pdev->dev, - "%s: Bad cb_state (%d): tmf=%p, old_request=%p\n", - __func__, cb_state, tmf, old_request); - break; - } -} - /** * isci_task_abort_task() - This function is one of the SAS Domain Template * functions. This function is called by libsas to abort a specified task. @@ -956,22 +485,20 @@ static void isci_abort_task_process_cb( */ int isci_task_abort_task(struct sas_task *task) { - struct isci_host *isci_host = dev_to_ihost(task->dev); + struct isci_host *ihost = dev_to_ihost(task->dev); DECLARE_COMPLETION_ONSTACK(aborted_io_completion); struct isci_request *old_request = NULL; - enum isci_request_status old_state; - struct isci_remote_device *isci_device = NULL; + struct isci_remote_device *idev = NULL; struct isci_tmf tmf; int ret = TMF_RESP_FUNC_FAILED; unsigned long flags; - int perform_termination = 0; /* Get the isci_request reference from the task. Note that * this check does not depend on the pending request list * in the device, because tasks driving resets may land here * after completion in the core. */ - spin_lock_irqsave(&isci_host->scic_lock, flags); + spin_lock_irqsave(&ihost->scic_lock, flags); spin_lock(&task->task_state_lock); old_request = task->lldd_task; @@ -980,20 +507,20 @@ int isci_task_abort_task(struct sas_task *task) if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && (task->task_state_flags & SAS_TASK_AT_INITIATOR) && old_request) - isci_device = isci_lookup_device(task->dev); + idev = isci_lookup_device(task->dev); spin_unlock(&task->task_state_lock); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); - dev_warn(&isci_host->pdev->dev, - "%s: dev = %p, task = %p, old_request == %p\n", - __func__, isci_device, task, old_request); + dev_warn(&ihost->pdev->dev, + "%s: dev = %p, task = %p, old_request == %p\n", + __func__, idev, task, old_request); /* Device reset conditions signalled in task_state_flags are the * responsbility of libsas to observe at the start of the error * handler thread. */ - if (!isci_device || !old_request) { + if (!idev || !old_request) { /* The request has already completed and there * is nothing to do here other than to set the task * done bit, and indicate that the task abort function @@ -1007,126 +534,66 @@ int isci_task_abort_task(struct sas_task *task) ret = TMF_RESP_FUNC_COMPLETE; - dev_warn(&isci_host->pdev->dev, - "%s: abort task not needed for %p\n", - __func__, task); + dev_warn(&ihost->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); goto out; } - - spin_lock_irqsave(&isci_host->scic_lock, flags); - - /* Check the request status and change to "aborted" if currently - * "starting"; if true then set the I/O kernel completion - * struct that will be triggered when the request completes. - */ - old_state = isci_task_validate_request_to_abort( - old_request, isci_host, isci_device, - &aborted_io_completion); - if ((old_state != started) && - (old_state != completed) && - (old_state != aborting)) { - - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - - /* The request was already being handled by someone else (because - * they got to set the state away from started). - */ - dev_warn(&isci_host->pdev->dev, - "%s: device = %p; old_request %p already being aborted\n", - __func__, - isci_device, old_request); - ret = TMF_RESP_FUNC_COMPLETE; + /* Suspend the RNC, kill the TC */ + if (isci_remote_device_suspend_terminate(ihost, idev, old_request) + != SCI_SUCCESS) { + dev_warn(&ihost->pdev->dev, + "%s: isci_remote_device_reset_terminate(dev=%p, " + "req=%p, task=%p) failed\n", + __func__, idev, old_request, task); + ret = TMF_RESP_FUNC_FAILED; goto out; } + spin_lock_irqsave(&ihost->scic_lock, flags); + if (task->task_proto == SAS_PROTOCOL_SMP || sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { - spin_unlock_irqrestore(&isci_host->scic_lock, flags); + /* No task to send, so explicitly resume the device here */ + sci_remote_device_resume(idev, NULL, NULL); - dev_warn(&isci_host->pdev->dev, - "%s: %s request" - " or complete_in_target (%d), thus no TMF\n", - __func__, - ((task->task_proto == SAS_PROTOCOL_SMP) - ? "SMP" - : (sas_protocol_ata(task->task_proto) - ? "SATA/STP" - : "") - ), - test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)); - - if (test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { - spin_lock_irqsave(&task->task_state_lock, flags); - task->task_state_flags |= SAS_TASK_STATE_DONE; - task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | - SAS_TASK_STATE_PENDING); - spin_unlock_irqrestore(&task->task_state_lock, flags); - ret = TMF_RESP_FUNC_COMPLETE; - } else { - spin_lock_irqsave(&task->task_state_lock, flags); - task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | - SAS_TASK_STATE_PENDING); - spin_unlock_irqrestore(&task->task_state_lock, flags); - } + spin_unlock_irqrestore(&ihost->scic_lock, flags); - /* STP and SMP devices are not sent a TMF, but the - * outstanding I/O request is terminated below. This is - * because SATA/STP and SMP discovery path timeouts directly - * call the abort task interface for cleanup. - */ - perform_termination = 1; - - if (isci_device && !test_bit(IDEV_GONE, &isci_device->flags) && - (isci_remote_device_suspend(isci_host, isci_device) - != SCI_SUCCESS)) { - dev_warn(&isci_host->pdev->dev, - "%s: device = %p; failed to suspend\n", - __func__, isci_device); - goto out; - } + dev_warn(&ihost->pdev->dev, + "%s: %s request" + " or complete_in_target (%d), thus no TMF\n", + __func__, + ((task->task_proto == SAS_PROTOCOL_SMP) + ? "SMP" + : (sas_protocol_ata(task->task_proto) + ? "SATA/STP" + : "") + ), + test_bit(IREQ_COMPLETE_IN_TARGET, + &old_request->flags)); + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | + SAS_TASK_STATE_PENDING); + task->task_state_flags |= SAS_TASK_STATE_DONE; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + ret = TMF_RESP_FUNC_COMPLETE; } else { /* Fill in the tmf stucture */ isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, - isci_abort_task_process_cb, old_request); - spin_unlock_irqrestore(&isci_host->scic_lock, flags); - - if (isci_remote_device_suspend(isci_host, isci_device) - != SCI_SUCCESS) { - dev_warn(&isci_host->pdev->dev, - "%s: device = %p; failed to suspend\n", - __func__, isci_device); - goto out; - } + spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* Send the task management request. */ #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ - ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, + ret = isci_task_execute_tmf(ihost, idev, &tmf, ISCI_ABORT_TASK_TIMEOUT_MS); - - if (ret == TMF_RESP_FUNC_COMPLETE) - perform_termination = 1; - else - dev_warn(&isci_host->pdev->dev, - "%s: isci_task_send_tmf failed\n", __func__); - } - if (perform_termination) { - set_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags); - - /* Clean up the request on our side, and wait for the aborted - * I/O to complete. - */ - isci_terminate_request_core(isci_host, isci_device, - old_request); - isci_remote_device_resume(isci_host, isci_device, NULL, NULL); } - - /* Make sure we do not leave a reference to aborted_io_completion */ - old_request->io_request_completion = NULL; - out: - isci_put_device(isci_device); +out: + isci_put_device(idev); return ret; } @@ -1222,14 +689,11 @@ isci_task_request_complete(struct isci_host *ihost, { struct isci_tmf *tmf = isci_request_access_tmf(ireq); struct completion *tmf_complete = NULL; - struct completion *request_complete = ireq->io_request_completion; dev_dbg(&ihost->pdev->dev, "%s: request = %p, status=%d\n", __func__, ireq, completion_status); - isci_request_change_state(ireq, completed); - set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); if (tmf) { @@ -1253,20 +717,8 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - /* As soon as something is in the terminate path, deallocation is - * managed there. Note that the final non-managed state of a task - * request is "completed". - */ - if ((ireq->status == completed) || - !isci_request_is_dealloc_managed(ireq->status)) { - isci_request_change_state(ireq, unallocated); - isci_free_tag(ihost, ireq->io_tag); - list_del_init(&ireq->dev_node); - } - - /* "request_complete" is set if the task was being terminated. */ - if (request_complete) - complete(request_complete); + isci_free_tag(ihost, ireq->io_tag); + list_del_init(&ireq->dev_node); /* The task management part completes last. */ if (tmf_complete) @@ -1277,37 +729,37 @@ static int isci_reset_device(struct isci_host *ihost, struct domain_device *dev, struct isci_remote_device *idev) { - int rc; - enum sci_status status; + int rc = TMF_RESP_FUNC_COMPLETE, reset_stat; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); - if (isci_remote_device_reset(ihost, idev) != SCI_SUCCESS) { + /* Suspend the RNC, terminate all outstanding TCs. */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { rc = TMF_RESP_FUNC_FAILED; goto out; } + /* Note that since the termination for outstanding requests succeeded, + * this function will return success. This is because the resets will + * only fail if the device has been removed (ie. hotplug), and the + * primary duty of this function is to cleanup tasks, so that is the + * relevant status. + */ if (scsi_is_sas_phy_local(phy)) { struct isci_phy *iphy = &ihost->phys[phy->number]; - rc = isci_port_perform_hard_reset(ihost, iport, iphy); + reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy); } else - rc = sas_phy_reset(phy, !dev_is_sata(dev)); + reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); - /* Terminate in-progress I/O now. */ - isci_remote_device_nuke_requests(ihost, idev); - - /* Since all pending TCs have been cleaned, resume the RNC. */ - status = isci_remote_device_reset_complete(ihost, idev); - - if (status != SCI_SUCCESS) - dev_dbg(&ihost->pdev->dev, - "%s: isci_remote_device_reset_complete(%p) " - "returned %d!\n", __func__, idev, status); + /* Explicitly resume the RNC here, since there was no task sent. */ + isci_remote_device_resume(ihost, idev, NULL, NULL); - dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev); + dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", + __func__, idev, reset_stat); out: sas_put_local_phy(phy); return rc; @@ -1321,7 +773,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_get_device(dev); + idev = isci_get_device(dev->lldd_dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); if (!idev) { diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h index 7b6d0e32fd9..9c06cbad1d2 100644 --- a/drivers/scsi/isci/task.h +++ b/drivers/scsi/isci/task.h @@ -62,19 +62,6 @@ struct isci_request; -/** - * enum isci_tmf_cb_state - This enum defines the possible states in which the - * TMF callback function is invoked during the TMF execution process. - * - * - */ -enum isci_tmf_cb_state { - - isci_tmf_init_state = 0, - isci_tmf_started, - isci_tmf_timed_out -}; - /** * enum isci_tmf_function_codes - This enum defines the possible preparations * of task management requests. @@ -87,6 +74,7 @@ enum isci_tmf_function_codes { isci_tmf_ssp_task_abort = TMF_ABORT_TASK, isci_tmf_ssp_lun_reset = TMF_LU_RESET, }; + /** * struct isci_tmf - This class represents the task management object which * acts as an interface to libsas for processing task management requests @@ -106,15 +94,6 @@ struct isci_tmf { u16 io_tag; enum isci_tmf_function_codes tmf_code; int status; - - /* The optional callback function allows the user process to - * track the TMF transmit / timeout conditions. - */ - void (*cb_state_func)( - enum isci_tmf_cb_state, - struct isci_tmf *, void *); - void *cb_data; - }; static inline void isci_print_tmf(struct isci_host *ihost, struct isci_tmf *tmf) @@ -208,113 +187,4 @@ int isci_queuecommand( struct scsi_cmnd *scsi_cmd, void (*donefunc)(struct scsi_cmnd *)); -/** - * enum isci_completion_selection - This enum defines the possible actions to - * take with respect to a given request's notification back to libsas. - * - * - */ -enum isci_completion_selection { - - isci_perform_normal_io_completion, /* Normal notify (task_done) */ - isci_perform_aborted_io_completion, /* No notification. */ - isci_perform_error_io_completion /* Use sas_task_abort */ -}; - -/** - * isci_task_set_completion_status() - This function sets the completion status - * for the request. - * @task: This parameter is the completed request. - * @response: This parameter is the response code for the completed task. - * @status: This parameter is the status code for the completed task. - * -* @return The new notification mode for the request. -*/ -static inline enum isci_completion_selection -isci_task_set_completion_status( - struct sas_task *task, - enum service_response response, - enum exec_status status, - enum isci_completion_selection task_notification_selection) -{ - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - - /* If a device reset is being indicated, make sure the I/O - * is in the error path. - */ - if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) { - /* Fail the I/O to make sure it goes into the error path. */ - response = SAS_TASK_UNDELIVERED; - status = SAM_STAT_TASK_ABORTED; - - task_notification_selection = isci_perform_error_io_completion; - } - task->task_status.resp = response; - task->task_status.stat = status; - - switch (task->task_proto) { - - case SAS_PROTOCOL_SATA: - case SAS_PROTOCOL_STP: - case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: - - if (task_notification_selection - == isci_perform_error_io_completion) { - /* SATA/STP I/O has it's own means of scheduling device - * error handling on the normal path. - */ - task_notification_selection - = isci_perform_normal_io_completion; - } - break; - default: - break; - } - - switch (task_notification_selection) { - - case isci_perform_error_io_completion: - - if (task->task_proto == SAS_PROTOCOL_SMP) { - /* There is no error escalation in the SMP case. - * Convert to a normal completion to avoid the - * timeout in the discovery path and to let the - * next action take place quickly. - */ - task_notification_selection - = isci_perform_normal_io_completion; - - /* Fall through to the normal case... */ - } else { - /* Use sas_task_abort */ - /* Leave SAS_TASK_STATE_DONE clear - * Leave SAS_TASK_AT_INITIATOR set. - */ - break; - } - - case isci_perform_aborted_io_completion: - /* This path can occur with task-managed requests as well as - * requests terminated because of LUN or device resets. - */ - /* Fall through to the normal case... */ - case isci_perform_normal_io_completion: - /* Normal notification (task_done) */ - task->task_state_flags |= SAS_TASK_STATE_DONE; - task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | - SAS_TASK_STATE_PENDING); - break; - default: - WARN_ONCE(1, "unknown task_notification_selection: %d\n", - task_notification_selection); - break; - } - - spin_unlock_irqrestore(&task->task_state_lock, flags); - - return task_notification_selection; - -} #endif /* !defined(_SCI_TASK_H_) */ -- cgit v1.2.3-70-g09d2 From 59e35396436c564b5019e1a70073900bc3e19f4f Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:54 -0800 Subject: isci: Add suspension cases for RNC INVALIDATING, POSTING states. The RNC can be any of the states in the loop from suspended to ready when the API "suspend" or "resume" are called. This change adds destination states parameters that control the suspension / resumption action of the RNC statemachine for those transition states. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 11 --- drivers/scsi/isci/remote_device.c | 2 +- drivers/scsi/isci/remote_node_context.c | 145 +++++++++++++++++++++----------- drivers/scsi/isci/remote_node_context.h | 10 ++- 4 files changed, 105 insertions(+), 63 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index ed206c5a00a..f1866b0dc19 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -1688,17 +1688,6 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor __func__, iport, status); } - - /* If the hard reset for the port has failed, consider this - * the same as link failures on all phys in the port. - */ - if (ret != TMF_RESP_FUNC_COMPLETE) { - - dev_err(&ihost->pdev->dev, - "%s: iport = %p; hard reset failed " - "(0x%x) - driving explicit link fail for all phys\n", - __func__, iport, iport->hard_reset_status); - } return ret; } diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index c47304cea45..cf5d554e505 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -192,7 +192,7 @@ static void isci_remote_device_not_ready(struct isci_host *ihost, u32 reason) { dev_dbg(&ihost->pdev->dev, - "%s: isci_device = %p\n", __func__, idev); + "%s: isci_device = %p; reason = %d\n", __func__, idev, reason); switch (reason) { case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index feeca17f0f1..75cf043e2ad 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -165,21 +165,24 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont static void sci_remote_node_context_setup_to_resume( struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback callback, - void *callback_parameter) + void *callback_parameter, + enum sci_remote_node_context_destination_state dest_param) { - if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) { - sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY; - sci_rnc->user_callback = callback; - sci_rnc->user_cookie = callback_parameter; + if (sci_rnc->destination_state != RNC_DEST_FINAL) { + sci_rnc->destination_state = dest_param; + if (callback != NULL) { + sci_rnc->user_callback = callback; + sci_rnc->user_cookie = callback_parameter; + } } } -static void sci_remote_node_context_setup_to_destory( +static void sci_remote_node_context_setup_to_destroy( struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback callback, void *callback_parameter) { - sci_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL; + sci_rnc->destination_state = RNC_DEST_FINAL; sci_rnc->user_callback = callback; sci_rnc->user_cookie = callback_parameter; } @@ -203,9 +206,13 @@ static void sci_remote_node_context_notify_user( static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc) { - if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) + if ((rnc->destination_state == RNC_DEST_READY) || + (rnc->destination_state == RNC_DEST_SUSPENDED_RESUME)) { + rnc->destination_state = RNC_DEST_READY; sci_remote_node_context_resume(rnc, rnc->user_callback, rnc->user_cookie); + } else + rnc->destination_state = RNC_DEST_UNSPECIFIED; } static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc) @@ -252,7 +259,7 @@ static void sci_remote_node_context_initial_state_enter(struct sci_base_state_ma * someone requested to destroy the remote node context object. */ if (sm->previous_state_id == SCI_RNC_INVALIDATING) { - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + rnc->destination_state = RNC_DEST_UNSPECIFIED; sci_remote_node_context_notify_user(rnc); } } @@ -297,10 +304,26 @@ static void sci_remote_node_context_resuming_state_enter(struct sci_base_state_m static void sci_remote_node_context_ready_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); - - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; - - if (rnc->user_callback) + enum sci_remote_node_context_destination_state dest_select; + scics_sds_remote_node_context_callback usr_cb = rnc->user_callback; + void *usr_param = rnc->user_cookie; + int tell_user = 1; + + dest_select = rnc->destination_state; + rnc->destination_state = RNC_DEST_UNSPECIFIED; + + if ((dest_select == RNC_DEST_SUSPENDED) || + (dest_select == RNC_DEST_SUSPENDED_RESUME)) { + sci_remote_node_context_suspend( + rnc, SCI_SOFTWARE_SUSPENSION, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); + + if (dest_select == RNC_DEST_SUSPENDED_RESUME) { + sci_remote_node_context_resume(rnc, usr_cb, usr_param); + tell_user = 0; /* Wait until ready again. */ + } + } + if (tell_user && rnc->user_callback) sci_remote_node_context_notify_user(rnc); } @@ -366,7 +389,7 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, memset(rnc, 0, sizeof(struct sci_remote_node_context)); rnc->remote_node_index = remote_node_index; - rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + rnc->destination_state = RNC_DEST_UNSPECIFIED; sci_init_sm(&rnc->sm, sci_remote_node_context_state_table, SCI_RNC_INITIAL); } @@ -390,11 +413,11 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con break; case SCI_RNC_INVALIDATING: if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) { - if (sci_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) - state = SCI_RNC_INITIAL; + if (sci_rnc->destination_state == RNC_DEST_FINAL) + next_state = SCI_RNC_INITIAL; else - state = SCI_RNC_POSTING; - sci_change_state(&sci_rnc->sm, state); + next_state = SCI_RNC_POSTING; + sci_change_state(&sci_rnc->sm, next_state); } else { switch (scu_get_event_type(event_code)) { case SCU_EVENT_TYPE_RNC_SUSPEND_TX: @@ -483,7 +506,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context state = sci_rnc->sm.current_state_id; switch (state) { case SCI_RNC_INVALIDATING: - sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); return SCI_SUCCESS; case SCI_RNC_POSTING: case SCI_RNC_RESUMING: @@ -491,7 +514,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_destory(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); return SCI_SUCCESS; case SCI_RNC_INITIAL: @@ -522,6 +545,8 @@ enum sci_status sci_remote_node_context_suspend( = sci_rnc->sm.current_state_id; struct isci_remote_device *idev = rnc_to_dev(sci_rnc); enum sci_status status = SCI_FAILURE_INVALID_STATE; + enum sci_remote_node_context_destination_state dest_param = + RNC_DEST_UNSPECIFIED; dev_dbg(scirdev_to_dev(idev), "%s: current state %d, current suspend_type %x dest state %d," @@ -531,12 +556,31 @@ enum sci_status sci_remote_node_context_suspend( suspend_type); /* Disable automatic state continuations if explicitly suspending. */ - if (suspend_reason == SCI_SOFTWARE_SUSPENSION) - sci_rnc->destination_state - = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + if ((suspend_reason != SCI_SOFTWARE_SUSPENSION) || + (sci_rnc->destination_state == RNC_DEST_FINAL)) + dest_param = sci_rnc->destination_state; + switch (state) { + case SCI_RNC_RESUMING: + break; /* The RNC has been posted, so start the suspend. */ case SCI_RNC_READY: break; + case SCI_RNC_INVALIDATING: + if (sci_rnc->destination_state == RNC_DEST_FINAL) { + dev_warn(scirdev_to_dev(idev), + "%s: already destroying %p\n", + __func__, sci_rnc); + return SCI_FAILURE_INVALID_STATE; + } + /* Fall through and handle like SCI_RNC_POSTING */ + case SCI_RNC_POSTING: + /* Set the destination state to AWAIT - this signals the + * entry into the SCI_RNC_READY state that a suspension + * needs to be done immediately. + */ + sci_rnc->destination_state = RNC_DEST_SUSPENDED; + return SCI_SUCCESS; + case SCI_RNC_TX_SUSPENDED: if (suspend_type == SCU_EVENT_TL_RNC_SUSPEND_TX) status = SCI_SUCCESS; @@ -556,6 +600,7 @@ enum sci_status sci_remote_node_context_suspend( rnc_state_name(state)); return SCI_FAILURE_INVALID_STATE; } + sci_rnc->destination_state = dest_param; sci_rnc->user_callback = cb_fn; sci_rnc->user_cookie = cb_p; sci_rnc->suspend_type = suspend_type; @@ -590,18 +635,31 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) return SCI_FAILURE_INVALID_STATE; - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); sci_remote_node_context_construct_buffer(sci_rnc); sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); return SCI_SUCCESS; case SCI_RNC_POSTING: case SCI_RNC_INVALIDATING: case SCI_RNC_RESUMING: - if (sci_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) - return SCI_FAILURE_INVALID_STATE; - - sci_rnc->user_callback = cb_fn; - sci_rnc->user_cookie = cb_p; + /* We are still waiting to post when a resume was requested. */ + switch (sci_rnc->destination_state) { + case RNC_DEST_SUSPENDED: + case RNC_DEST_SUSPENDED_RESUME: + /* Previously waiting to suspend after posting. Now + * continue onto resumption. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); + break; + default: + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + break; + } return SCI_SUCCESS; case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: { @@ -613,7 +671,8 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s * to clear the TCi to NCQ tag mapping table for the RNi. * All other device types we can just resume. */ - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); if (dev_is_sata(dev) && dev->parent) sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); @@ -622,7 +681,9 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s return SCI_SUCCESS; } case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p); + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), @@ -663,24 +724,12 @@ enum sci_status sci_remote_node_context_start_task( scics_sds_remote_node_context_callback cb_fn, void *cb_p) { - enum scis_sds_remote_node_context_states state; - - state = sci_rnc->sm.current_state_id; - switch (state) { - case SCI_RNC_RESUMING: - case SCI_RNC_READY: - case SCI_RNC_AWAIT_SUSPENSION: - return SCI_SUCCESS; - case SCI_RNC_TX_SUSPENDED: - case SCI_RNC_TX_RX_SUSPENDED: - sci_remote_node_context_resume(sci_rnc, cb_fn, cb_p); - return SCI_SUCCESS; - default: + enum sci_status status = sci_remote_node_context_resume(sci_rnc, + cb_fn, cb_p); + if (status != SCI_SUCCESS) dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: invalid state %s\n", __func__, - rnc_state_name(state)); - return SCI_FAILURE_INVALID_STATE; - } + "%s: resume failed: %d\n", __func__, status); + return status; } int sci_remote_node_context_is_safe_to_abort( diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 2870af14eda..48319066b4d 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -141,9 +141,13 @@ const char *rnc_state_name(enum scis_sds_remote_node_context_states state); * node context. */ enum sci_remote_node_context_destination_state { - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED, - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY, - SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL + RNC_DEST_UNSPECIFIED, + RNC_DEST_READY, + RNC_DEST_FINAL, + RNC_DEST_SUSPENDED, /* Set when suspend during post/invalidate */ + RNC_DEST_SUSPENDED_RESUME /* Set when a resume was done during posting + * or invalidating and already suspending. + */ }; /** -- cgit v1.2.3-70-g09d2 From 637325028f3a9e9cf411ede96063997611f976e4 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:55 -0800 Subject: isci: Device access in the error path does not depend on IDEV_GONE. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 29ce8815e79..9d8720d6266 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -427,7 +427,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) int ret; spin_lock_irqsave(&ihost->scic_lock, flags); - idev = isci_lookup_device(dev); + idev = isci_get_device(dev->lldd_dev); spin_unlock_irqrestore(&ihost->scic_lock, flags); dev_dbg(&ihost->pdev->dev, @@ -507,7 +507,7 @@ int isci_task_abort_task(struct sas_task *task) if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && (task->task_state_flags & SAS_TASK_AT_INITIATOR) && old_request) - idev = isci_lookup_device(task->dev); + idev = isci_get_device(task->dev->lldd_dev); spin_unlock(&task->task_state_lock); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -593,6 +593,9 @@ int isci_task_abort_task(struct sas_task *task) ISCI_ABORT_TASK_TIMEOUT_MS); } out: + dev_warn(&ihost->pdev->dev, + "%s: Done; dev = %p, task = %p , old_request == %p\n", + __func__, idev, task, old_request); isci_put_device(idev); return ret; } -- cgit v1.2.3-70-g09d2 From aa20d9343079b1f0bebd43dec82ecfd4af5e43da Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:56 -0800 Subject: isci: All pending TCs are terminated when the RNC is invalidated. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_node_context.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 75cf043e2ad..adbb4b80d9e 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -275,8 +275,8 @@ static void sci_remote_node_context_invalidating_state_enter(struct sci_base_sta { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); - /* Terminate outstanding requests pending abort. */ - sci_remote_device_abort_requests_pending_abort(rnc_to_dev(rnc)); + /* Terminate all outstanding requests. */ + sci_remote_device_terminate_requests(rnc_to_dev(rnc)); sci_remote_node_context_invalidate_context_buffer(rnc); } -- cgit v1.2.3-70-g09d2 From 033751f6643adf616b85ac5eea7ce784bdde1b50 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:56 -0800 Subject: isci: Only set IDEV_GONE in the device stop path. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 23 ----------------------- drivers/scsi/isci/remote_device.c | 3 --- 2 files changed, 26 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index f1866b0dc19..da0c4e1b9b3 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -240,32 +240,9 @@ static void isci_port_link_down(struct isci_host *isci_host, struct isci_phy *isci_phy, struct isci_port *isci_port) { - struct isci_remote_device *isci_device; - dev_dbg(&isci_host->pdev->dev, "%s: isci_port = %p\n", __func__, isci_port); - if (isci_port) { - - /* check to see if this is the last phy on this port. */ - if (isci_phy->sas_phy.port && - isci_phy->sas_phy.port->num_phys == 1) { - /* change the state for all devices on this port. The - * next task sent to this device will be returned as - * SAS_TASK_UNDELIVERED, and the scsi mid layer will - * remove the target - */ - list_for_each_entry(isci_device, - &isci_port->remote_dev_list, - node) { - dev_dbg(&isci_host->pdev->dev, - "%s: isci_device = %p\n", - __func__, isci_device); - set_bit(IDEV_GONE, &isci_device->flags); - } - } - } - /* Notify libsas of the borken link, this will trigger calls to our * isci_port_deformed and isci_dev_gone functions. */ diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index cf5d554e505..b26ab05107d 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -195,9 +195,6 @@ static void isci_remote_device_not_ready(struct isci_host *ihost, "%s: isci_device = %p; reason = %d\n", __func__, idev, reason); switch (reason) { - case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: - set_bit(IDEV_GONE, &idev->flags); - break; case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: set_bit(IDEV_IO_NCQERROR, &idev->flags); -- cgit v1.2.3-70-g09d2 From d6b2a0e4a066ea51322e16e66b25028cb0b4ca7e Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:57 -0800 Subject: isci: Remove isci_device reqs_in_process and dev_node from isci_device. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/init.c | 1 - drivers/scsi/isci/remote_device.c | 6 +----- drivers/scsi/isci/remote_device.h | 1 - drivers/scsi/isci/request.c | 12 ------------ drivers/scsi/isci/request.h | 2 -- drivers/scsi/isci/task.c | 4 ---- 6 files changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 39f12703b89..f2a8a334630 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -569,7 +569,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) { struct isci_remote_device *idev = &ihost->devices[i]; - INIT_LIST_HEAD(&idev->reqs_in_process); INIT_LIST_HEAD(&idev->node); } diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b26ab05107d..b14eff3c76d 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -919,7 +919,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_ * here should go through isci_remote_device_nuke_requests. * If we hit this condition, we will need a way to complete * io requests in process */ - BUG_ON(!list_empty(&idev->reqs_in_process)); + BUG_ON(idev->started_request_count > 0); sci_remote_device_destruct(idev); list_del_init(&idev->node); @@ -1345,10 +1345,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport) dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__); return NULL; } - - if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n")) - return NULL; - if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n")) return NULL; diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index da43698e9eb..8b7817cf435 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -90,7 +90,6 @@ struct isci_remote_device { struct isci_port *isci_port; struct domain_device *domain_dev; struct list_head node; - struct list_head reqs_in_process; struct sci_base_state_machine sm; u32 device_port_width; enum sas_linkrate connection_rate; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index f4e80f31423..662f36de805 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2956,9 +2956,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, /* Add to the completed list. */ list_add(&request->completed_node, &ihost->requests_to_complete); - /* Take the request off the device's pending request list. */ - list_del_init(&request->dev_node); - /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); @@ -3412,7 +3409,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t ireq->flags = 0; ireq->num_sg_entries = 0; INIT_LIST_HEAD(&ireq->completed_node); - INIT_LIST_HEAD(&ireq->dev_node); return ireq; } @@ -3496,17 +3492,9 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide spin_unlock_irqrestore(&ihost->scic_lock, flags); return status; } - /* Either I/O started OK, or the core has signaled that * the device needs a target reset. - * - * In either case, hold onto the I/O for later. - * - * Update it's status and add it to the list in the - * remote device object. */ - list_add(&ireq->dev_node, &idev->reqs_in_process); - if (status != SCI_SUCCESS) { /* The request did not really start in the * hardware, so clear the request handle diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index f3116a51235..d12e97531da 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -96,8 +96,6 @@ struct isci_request { struct isci_host *isci_host; /* For use in the requests_to_{complete|abort} lists: */ struct list_head completed_node; - /* For use in the reqs_in_process list: */ - struct list_head dev_node; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; unsigned int num_sg_entries; diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 9d8720d6266..222fb0de4d5 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -317,9 +317,6 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - /* add the request to the remote device request list. */ - list_add(&ireq->dev_node, &idev->reqs_in_process); - /* The RNC must be unsuspended before the TMF can get a response. */ sci_remote_device_resume(idev, NULL, NULL); @@ -721,7 +718,6 @@ isci_task_request_complete(struct isci_host *ihost, set_bit(IREQ_TERMINATED, &ireq->flags); isci_free_tag(ihost, ireq->io_tag); - list_del_init(&ireq->dev_node); /* The task management part completes last. */ if (tmf_complete) -- cgit v1.2.3-70-g09d2 From c94fc1ad25de885e1c59f714f19bc726e7a21caf Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:58 -0800 Subject: isci: Distinguish between remote device suspension cases For NCQ error conditions among others, there is no need to enable the link layer hang detect timer. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 27 +++++++++++---------------- drivers/scsi/isci/remote_node_context.c | 19 +++++++++++++------ drivers/scsi/isci/remote_node_context.h | 5 +++-- drivers/scsi/isci/request.c | 2 +- 4 files changed, 28 insertions(+), 25 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index b14eff3c76d..cc8ab69a202 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -72,10 +72,11 @@ const char *dev_state_name(enum sci_remote_device_states state) } #undef C -static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev) +static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, + enum sci_remote_node_suspension_reasons reason) { return sci_remote_node_context_suspend(&idev->rnc, - SCI_SOFTWARE_SUSPENSION, + reason, SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); } @@ -199,7 +200,7 @@ static void isci_remote_device_not_ready(struct isci_host *ihost, set_bit(IDEV_IO_NCQERROR, &idev->flags); /* Suspend the remote device so the I/O can be terminated. */ - sci_remote_device_suspend(idev); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL); /* Kill all outstanding requests for the device. */ sci_remote_device_terminate_requests(idev); @@ -268,7 +269,8 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev, rnc_destruct_done, idev); else { - sci_remote_device_suspend(idev); + sci_remote_device_suspend( + idev, SCI_SW_SUSPEND_LINKHANG_DETECT); sci_remote_device_terminate_requests(idev); } return SCI_SUCCESS; @@ -473,11 +475,7 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, status = SCI_SUCCESS; /* Suspend the associated RNC */ - sci_remote_node_context_suspend( - &idev->rnc, - SCI_SOFTWARE_SUSPENSION, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, - NULL, NULL); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL); dev_dbg(scirdev_to_dev(idev), "%s: device: %p event code: %x: %s\n", @@ -789,9 +787,8 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost, * the correct action when the remote node context is suspended * and later resumed. */ - sci_remote_node_context_suspend( - &idev->rnc, SCI_SOFTWARE_SUSPENSION, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); + sci_remote_device_suspend(idev, + SCI_SW_SUSPEND_LINKHANG_DETECT); status = sci_remote_node_context_start_task(&idev->rnc, ireq, sci_remote_device_continue_request, idev); @@ -986,9 +983,7 @@ static void sci_remote_device_resetting_state_enter(struct sci_base_state_machin dev_dbg(&ihost->pdev->dev, "%s: isci_device = %p\n", __func__, idev); - sci_remote_node_context_suspend( - &idev->rnc, SCI_SOFTWARE_SUSPENSION, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); } static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm) @@ -1486,7 +1481,7 @@ enum sci_status isci_remote_device_suspend_terminate( /* Put the device into suspension. */ spin_lock_irqsave(&ihost->scic_lock, flags); - sci_remote_device_suspend(idev); + sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); spin_unlock_irqrestore(&ihost->scic_lock, flags); /* Terminate and wait for the completions. */ diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index adbb4b80d9e..85bf5ec2641 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -52,7 +52,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - +#include #include "host.h" #include "isci.h" #include "remote_device.h" @@ -315,7 +315,7 @@ static void sci_remote_node_context_ready_state_enter(struct sci_base_state_mach if ((dest_select == RNC_DEST_SUSPENDED) || (dest_select == RNC_DEST_SUSPENDED_RESUME)) { sci_remote_node_context_suspend( - rnc, SCI_SOFTWARE_SUSPENSION, + rnc, SCI_SW_SUSPEND_NORMAL, SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); if (dest_select == RNC_DEST_SUSPENDED_RESUME) { @@ -352,8 +352,10 @@ static void sci_remote_node_context_await_suspend_state_exit( { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); - isci_dev_set_hang_detection_timeout(rnc_to_dev(rnc), 0); + if (dev_is_sata(idev->domain_dev)) + isci_dev_set_hang_detection_timeout(idev, 0); } static const struct sci_base_state sci_remote_node_context_state_table[] = { @@ -556,7 +558,7 @@ enum sci_status sci_remote_node_context_suspend( suspend_type); /* Disable automatic state continuations if explicitly suspending. */ - if ((suspend_reason != SCI_SOFTWARE_SUSPENSION) || + if ((suspend_reason == SCI_HW_SUSPEND) || (sci_rnc->destination_state == RNC_DEST_FINAL)) dest_param = sci_rnc->destination_state; @@ -612,8 +614,13 @@ enum sci_status sci_remote_node_context_suspend( wake_up_all(&ihost->eventq); /* Let observers look. */ return SCI_SUCCESS; } - if (suspend_reason == SCI_SOFTWARE_SUSPENSION) { - isci_dev_set_hang_detection_timeout(idev, 0x00000001); + if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) || + (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) { + + if ((suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT) + && dev_is_sata(idev->domain_dev)) + isci_dev_set_hang_detection_timeout(idev, 0x00000001); + sci_remote_device_post_request( idev, SCI_SOFTWARE_SUSPEND_CMD); } diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 48319066b4d..364da3722aa 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -76,8 +76,9 @@ #define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF enum sci_remote_node_suspension_reasons { - SCU_HARDWARE_SUSPENSION, - SCI_SOFTWARE_SUSPENSION + SCI_HW_SUSPEND, + SCI_SW_SUSPEND_NORMAL, + SCI_SW_SUSPEND_LINKHANG_DETECT }; #define SCI_SOFTWARE_SUSPEND_CMD SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX #define SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT SCU_EVENT_TL_RNC_SUSPEND_TX_RX diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 662f36de805..48b409d68c0 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2380,7 +2380,7 @@ static void sci_request_handle_suspending_completions( sci_remote_node_context_suspend( &ireq->target_device->rnc, - SCU_HARDWARE_SUSPENSION, + SCI_HW_SUSPEND, (is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX : SCU_EVENT_TL_RNC_SUSPEND_TX, NULL, NULL); -- cgit v1.2.3-70-g09d2 From e3c84dfdb8f4c675b0ba5cf3fa252dc4056b7ddd Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:58 -0800 Subject: isci: Fix the terminated I/O to not call sas_task_abort(). This addresses a regression from the commit "isci: Redesign device suspension, abort, cleanup." in which the sas_task end condition for terminated I/Os was made to call back on sas_task_abort()". This commit will be rolled into the original. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 48b409d68c0..809d3683d0c 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2832,7 +2832,7 @@ static void isci_request_io_request_complete(struct isci_host *ihost, __func__, request, task); /* The request was terminated explicitly. */ - clear_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); + set_bit(IREQ_COMPLETE_IN_TARGET, &request->flags); response = SAS_TASK_UNDELIVERED; /* See if the device has been/is being stopped. Note -- cgit v1.2.3-70-g09d2 From 447bfbcee070a0b43dd6abc743063d7a02fe65ca Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:41:59 -0800 Subject: isci: Save the suspension hint for upcoming suspensions. In the case of a suspend call while in SCI_RNC_POSTING or INVALIDATING states, the LLHANG detect needed to be saved so the upcoming suspension would enable it correctly. The unused suspend callback parameters were removed. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 6 ++---- drivers/scsi/isci/remote_node_context.c | 16 +++++++--------- drivers/scsi/isci/remote_node_context.h | 5 ++--- drivers/scsi/isci/request.c | 3 +-- 4 files changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index cc8ab69a202..1a85e9edef6 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -75,10 +75,8 @@ const char *dev_state_name(enum sci_remote_device_states state) static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, enum sci_remote_node_suspension_reasons reason) { - return sci_remote_node_context_suspend(&idev->rnc, - reason, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, - NULL, NULL); + return sci_remote_node_context_suspend(&idev->rnc, reason, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); } /** diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 85bf5ec2641..2ac92608cc2 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -315,8 +315,8 @@ static void sci_remote_node_context_ready_state_enter(struct sci_base_state_mach if ((dest_select == RNC_DEST_SUSPENDED) || (dest_select == RNC_DEST_SUSPENDED_RESUME)) { sci_remote_node_context_suspend( - rnc, SCI_SW_SUSPEND_NORMAL, - SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT, NULL, NULL); + rnc, rnc->suspend_reason, + SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); if (dest_select == RNC_DEST_SUSPENDED_RESUME) { sci_remote_node_context_resume(rnc, usr_cb, usr_param); @@ -539,9 +539,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context enum sci_status sci_remote_node_context_suspend( struct sci_remote_node_context *sci_rnc, enum sci_remote_node_suspension_reasons suspend_reason, - u32 suspend_type, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p) + u32 suspend_type) { enum scis_sds_remote_node_context_states state = sci_rnc->sm.current_state_id; @@ -581,6 +579,8 @@ enum sci_status sci_remote_node_context_suspend( * needs to be done immediately. */ sci_rnc->destination_state = RNC_DEST_SUSPENDED; + sci_rnc->suspend_type = suspend_type; + sci_rnc->suspend_reason = suspend_reason; return SCI_SUCCESS; case SCI_RNC_TX_SUSPENDED: @@ -603,14 +603,12 @@ enum sci_status sci_remote_node_context_suspend( return SCI_FAILURE_INVALID_STATE; } sci_rnc->destination_state = dest_param; - sci_rnc->user_callback = cb_fn; - sci_rnc->user_cookie = cb_p; - sci_rnc->suspend_type = suspend_type; + sci_rnc->suspend_type = suspend_type; + sci_rnc->suspend_reason = suspend_reason; if (status == SCI_SUCCESS) { /* Already in the destination state? */ struct isci_host *ihost = idev->owning_port->owning_controller; - sci_remote_node_context_notify_user(sci_rnc); wake_up_all(&ihost->eventq); /* Let observers look. */ return SCI_SUCCESS; } diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 364da3722aa..9eee304fdf9 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -169,6 +169,7 @@ struct sci_remote_node_context { * context suspension. */ u32 suspend_type; + enum sci_remote_node_suspension_reasons suspend_reason; /** * This field is true if the remote node context is resuming from its current @@ -209,9 +210,7 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context void *callback_parameter); enum sci_status sci_remote_node_context_suspend(struct sci_remote_node_context *sci_rnc, u32 suspend_type, - u32 suspension_code, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p); + u32 suspension_code); enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *sci_rnc, scics_sds_remote_node_context_callback cb_fn, void *cb_p); diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 809d3683d0c..432585b04dc 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2382,8 +2382,7 @@ static void sci_request_handle_suspending_completions( &ireq->target_device->rnc, SCI_HW_SUSPEND, (is_tx_rx) ? SCU_EVENT_TL_RNC_SUSPEND_TX_RX - : SCU_EVENT_TL_RNC_SUSPEND_TX, - NULL, NULL); + : SCU_EVENT_TL_RNC_SUSPEND_TX); } } -- cgit v1.2.3-70-g09d2 From 9608b6408e637abeec101abb6aebd3343f0ebac4 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:00 -0800 Subject: isci: Manage the LLHANG timer enable/disable per-device. The LLHANG timer should be enabled once per device. This patch corrects both the timer enable and the timer disable for the remote device. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 17 +++++++++++++++++ drivers/scsi/isci/remote_device.h | 8 ++------ drivers/scsi/isci/remote_node_context.c | 3 +-- 3 files changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 1a85e9edef6..86aca11120f 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1520,3 +1520,20 @@ enum sci_status isci_remote_device_reset_complete( return status; } +void isci_dev_set_hang_detection_timeout( + struct isci_remote_device *idev, + u32 timeout) +{ + if (dev_is_sata(idev->domain_dev)) { + if (timeout) { + if (test_and_set_bit(IDEV_RNC_LLHANG_ENABLED, + &idev->flags)) + return; /* Already enabled. */ + } else if (!test_and_clear_bit(IDEV_RNC_LLHANG_ENABLED, + &idev->flags)) + return; /* Not enabled. */ + + sci_port_set_hang_detection_timeout(idev->owning_port, + timeout); + } +} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 8b7817cf435..ef563e5360a 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -85,6 +85,7 @@ struct isci_remote_device { #define IDEV_GONE 3 #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 + #define IDEV_RNC_LLHANG_ENABLED 6 unsigned long flags; struct kref kref; struct isci_port *isci_port; @@ -308,12 +309,7 @@ static inline void sci_remote_device_decrement_request_count(struct isci_remote_ idev->started_request_count--; } -static inline void isci_dev_set_hang_detection_timeout( - struct isci_remote_device *idev, - u32 timeout) -{ - sci_port_set_hang_detection_timeout(idev->owning_port, timeout); -} +void isci_dev_set_hang_detection_timeout(struct isci_remote_device *idev, u32 timeout); enum sci_status sci_remote_device_frame_handler( struct isci_remote_device *idev, diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 2ac92608cc2..48565de5001 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -615,8 +615,7 @@ enum sci_status sci_remote_node_context_suspend( if ((suspend_reason == SCI_SW_SUSPEND_NORMAL) || (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT)) { - if ((suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT) - && dev_is_sata(idev->domain_dev)) + if (suspend_reason == SCI_SW_SUSPEND_LINKHANG_DETECT) isci_dev_set_hang_detection_timeout(idev, 0x00000001); sci_remote_device_post_request( -- cgit v1.2.3-70-g09d2 From 08c031e4e3294a66a64074e12482abda846dd39c Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:00 -0800 Subject: isci: Make sure all TCs are terminated and cleaned in LUN reset. In the libsas error path, SATA disks require extra handling in libata to recover operation. However, libsas expects to be able to immediately recover all outstanding I/O once the error handler escalation stops. This patch fixes the condition where the libata error handler is scheduled for operation but libsas has already deleted the outstanding sas_tasks. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 222fb0de4d5..5d738fd5f88 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -439,16 +439,18 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) goto out; } + /* Suspend the RNC, kill all TCs */ + if (isci_remote_device_suspend_terminate(ihost, idev, NULL) + != SCI_SUCCESS) { + /* The suspend/terminate only fails if isci_get_device fails */ + ret = TMF_RESP_FUNC_FAILED; + goto out; + } + /* All pending I/Os have been terminated and cleaned up. */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); ret = TMF_RESP_FUNC_COMPLETE; } else { - /* Suspend the RNC, kill all TCs */ - if (isci_remote_device_suspend_terminate(ihost, idev, NULL) - != SCI_SUCCESS) { - ret = TMF_RESP_FUNC_FAILED; - goto out; - } /* Send the task management part of the reset. */ ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } -- cgit v1.2.3-70-g09d2 From 31a38ef0a5ad12dbe262ca55d0a905657be55a8d Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:01 -0800 Subject: isci: Implement waiting for suspend in the abort path. In order to prevent a device from receiving an I/O request while still in an RNC suspending or resuming state (and therefore failing that I/O back to libsas with a reset required status) wait for the RNC state change before proceding in the abort path. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 44 ++++++++++++++++++++++++--------- drivers/scsi/isci/remote_device.h | 6 ++--- drivers/scsi/isci/remote_node_context.c | 21 ++++++++++++++++ drivers/scsi/isci/remote_node_context.h | 3 +++ drivers/scsi/isci/task.c | 14 +++++------ 5 files changed, 66 insertions(+), 22 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 86aca11120f..acc94a454a1 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -137,6 +137,14 @@ static enum sci_status sci_remote_device_terminate_reqs_checkabort( return status; } +static bool isci_compare_suspendcount( + struct isci_remote_device *idev, + u32 localcount) +{ + smp_rmb(); + return localcount != idev->rnc.suspend_count; +} + enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, struct isci_remote_device *idev, @@ -144,31 +152,44 @@ enum sci_status isci_remote_device_terminate_requests( { enum sci_status status = SCI_SUCCESS; unsigned long flags; + u32 rnc_suspend_count; spin_lock_irqsave(&ihost->scic_lock, flags); + if (isci_get_device(idev) == NULL) { dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", __func__, idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); status = SCI_FAILURE; } else { + /* If already suspended, don't wait for another suspension. */ + smp_rmb(); + rnc_suspend_count + = sci_remote_node_context_is_suspended(&idev->rnc) + ? 0 : idev->rnc.suspend_count; + dev_dbg(&ihost->pdev->dev, "%s: idev=%p, ireq=%p; started_request_count=%d, " + "rnc_suspend_count=%d, rnc.suspend_count=%d" "about to wait\n", - __func__, idev, ireq, idev->started_request_count); + __func__, idev, ireq, idev->started_request_count, + rnc_suspend_count, idev->rnc.suspend_count); if (ireq) { /* Terminate a specific TC. */ sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_event(ihost->eventq, !test_bit(IREQ_ACTIVE, - &ireq->flags)); - + wait_event(ihost->eventq, + (isci_compare_suspendcount(idev, + rnc_suspend_count) + && !test_bit(IREQ_ACTIVE, &ireq->flags))); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, - idev->started_request_count == 0); + (isci_compare_suspendcount(idev, + rnc_suspend_count) + && idev->started_request_count == 0)); } dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", __func__, idev); @@ -1234,19 +1255,20 @@ enum sci_status sci_remote_device_resume( return status; } -enum sci_status isci_remote_device_resume( +enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, - struct isci_remote_device *idev, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p) + struct isci_remote_device *idev) { unsigned long flags; enum sci_status status; spin_lock_irqsave(&ihost->scic_lock, flags); - status = sci_remote_device_resume(idev, cb_fn, cb_p); + /* Preserve any current resume callbacks, for instance from other + * resumptions. + */ + status = sci_remote_device_resume(idev, idev->rnc.user_callback, + idev->rnc.user_cookie); spin_unlock_irqrestore(&ihost->scic_lock, flags); - return status; } /** diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index ef563e5360a..d1d18925fbf 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -357,11 +357,9 @@ enum sci_status sci_remote_device_resume( scics_sds_remote_node_context_callback cb_fn, void *cb_p); -enum sci_status isci_remote_device_resume( +enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, - struct isci_remote_device *idev, - scics_sds_remote_node_context_callback cb_fn, - void *cb_p); + struct isci_remote_device *idev); enum sci_status isci_remote_device_reset( struct isci_host *ihost, diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 48565de5001..77c8b5138b7 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -90,6 +90,15 @@ bool sci_remote_node_context_is_ready( return false; } +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc) +{ + u32 current_state = sci_rnc->sm.current_state_id; + + if (current_state == SCI_RNC_TX_RX_SUSPENDED) + return true; + return false; +} + static union scu_remote_node_context *sci_rnc_by_id(struct isci_host *ihost, u16 id) { if (id < ihost->remote_node_entries && @@ -339,6 +348,13 @@ static void sci_remote_node_context_tx_rx_suspended_state_enter(struct sci_base_ struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); struct isci_remote_device *idev = rnc_to_dev(rnc); struct isci_host *ihost = idev->owning_port->owning_controller; + u32 new_count = rnc->suspend_count + 1; + + if (new_count == 0) + rnc->suspend_count = 1; + else + rnc->suspend_count = new_count; + smp_wmb(); /* Terminate outstanding requests pending abort. */ sci_remote_device_abort_requests_pending_abort(idev); @@ -634,6 +650,11 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s enum scis_sds_remote_node_context_states state; state = sci_rnc->sm.current_state_id; + dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n", + __func__, rnc_state_name(state), cb_fn, cb_p, + sci_rnc->destination_state); + switch (state) { case SCI_RNC_INITIAL: if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 9eee304fdf9..c61c02e095a 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -170,6 +170,7 @@ struct sci_remote_node_context { */ u32 suspend_type; enum sci_remote_node_suspension_reasons suspend_reason; + u32 suspend_count; /** * This field is true if the remote node context is resuming from its current @@ -203,6 +204,8 @@ void sci_remote_node_context_construct(struct sci_remote_node_context *rnc, bool sci_remote_node_context_is_ready( struct sci_remote_node_context *sci_rnc); +bool sci_remote_node_context_is_suspended(struct sci_remote_node_context *sci_rnc); + enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_context *sci_rnc, u32 event_code); enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context *sci_rnc, diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 5d738fd5f88..c1c6dd0473a 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -317,11 +317,11 @@ static int isci_task_execute_tmf(struct isci_host *ihost, spin_unlock_irqrestore(&ihost->scic_lock, flags); goto err_tci; } - /* The RNC must be unsuspended before the TMF can get a response. */ - sci_remote_device_resume(idev, NULL, NULL); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* The RNC must be unsuspended before the TMF can get a response. */ + isci_remote_device_resume_from_abort(ihost, idev); + /* Wait for the TMF to complete, or a timeout. */ timeleft = wait_for_completion_timeout(&completion, msecs_to_jiffies(timeout_ms)); @@ -554,11 +554,11 @@ int isci_task_abort_task(struct sas_task *task) sas_protocol_ata(task->task_proto) || test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { - /* No task to send, so explicitly resume the device here */ - sci_remote_device_resume(idev, NULL, NULL); - spin_unlock_irqrestore(&ihost->scic_lock, flags); + /* No task to send, so explicitly resume the device here */ + isci_remote_device_resume_from_abort(ihost, idev); + dev_warn(&ihost->pdev->dev, "%s: %s request" " or complete_in_target (%d), thus no TMF\n", @@ -757,7 +757,7 @@ static int isci_reset_device(struct isci_host *ihost, reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); /* Explicitly resume the RNC here, since there was no task sent. */ - isci_remote_device_resume(ihost, idev, NULL, NULL); + isci_remote_device_resume_from_abort(ihost, idev); dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", __func__, idev, reset_stat); -- cgit v1.2.3-70-g09d2 From 0c3ce38f1bc8b6a6d8df0959e3c0dece31f9350c Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:02 -0800 Subject: isci: When in the abort path, defeat other resume calls until done. Completion of I/Os during the one of the abort path interface calls from libsas can drive remote device state changes and the resumption of the device RNC. This is a problem when the abort path is attempting to cleanup outstanding I/O at the same time - the resumption can prevent the termination from occuring correctly. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 2 + drivers/scsi/isci/remote_device.h | 1 + drivers/scsi/isci/remote_node_context.c | 126 ++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 46 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index acc94a454a1..d1c2a2294a3 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1266,6 +1266,7 @@ enum sci_status isci_remote_device_resume_from_abort( /* Preserve any current resume callbacks, for instance from other * resumptions. */ + clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); status = sci_remote_device_resume(idev, idev->rnc.user_callback, idev->rnc.user_cookie); spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1501,6 +1502,7 @@ enum sci_status isci_remote_device_suspend_terminate( /* Put the device into suspension. */ spin_lock_irqsave(&ihost->scic_lock, flags); + set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); spin_unlock_irqrestore(&ihost->scic_lock, flags); diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index d1d18925fbf..53564c35cf2 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -86,6 +86,7 @@ struct isci_remote_device { #define IDEV_IO_READY 4 #define IDEV_IO_NCQERROR 5 #define IDEV_RNC_LLHANG_ENABLED 6 + #define IDEV_ABORT_PATH_ACTIVE 7 unsigned long flags; struct kref kref; struct isci_port *isci_port; diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 77c8b5138b7..faeae9554ee 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -161,6 +161,14 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont rnc->ssp.oaf_more_compatibility_features = 0; } +static void sci_remote_node_context_save_cbparams( + struct sci_remote_node_context *sci_rnc, + scics_sds_remote_node_context_callback callback, + void *callback_parameter) +{ + sci_rnc->user_callback = callback; + sci_rnc->user_cookie = callback_parameter; +} /** * * @sci_rnc: @@ -179,10 +187,9 @@ static void sci_remote_node_context_setup_to_resume( { if (sci_rnc->destination_state != RNC_DEST_FINAL) { sci_rnc->destination_state = dest_param; - if (callback != NULL) { - sci_rnc->user_callback = callback; - sci_rnc->user_cookie = callback_parameter; - } + if (callback != NULL) + sci_remote_node_context_save_cbparams( + sci_rnc, callback, callback_parameter); } } @@ -648,67 +655,94 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s void *cb_p) { enum scis_sds_remote_node_context_states state; + struct isci_remote_device *idev = rnc_to_dev(sci_rnc); state = sci_rnc->sm.current_state_id; - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), - "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d\n", - __func__, rnc_state_name(state), cb_fn, cb_p, - sci_rnc->destination_state); + dev_dbg(scirdev_to_dev(idev), + "%s: state %s, cb_fn = %p, cb_p = %p; dest_state = %d; " + "dev resume path %s\n", + __func__, rnc_state_name(state), cb_fn, cb_p, + sci_rnc->destination_state, + test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags) + ? "" : ""); switch (state) { case SCI_RNC_INITIAL: if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) return SCI_FAILURE_INVALID_STATE; - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, - RNC_DEST_READY); - sci_remote_node_context_construct_buffer(sci_rnc); - sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else { + sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, + cb_p, RNC_DEST_READY); + sci_remote_node_context_construct_buffer(sci_rnc); + sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); + } return SCI_SUCCESS; + case SCI_RNC_POSTING: case SCI_RNC_INVALIDATING: case SCI_RNC_RESUMING: - /* We are still waiting to post when a resume was requested. */ - switch (sci_rnc->destination_state) { - case RNC_DEST_SUSPENDED: - case RNC_DEST_SUSPENDED_RESUME: - /* Previously waiting to suspend after posting. Now - * continue onto resumption. + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else { + /* We are still waiting to post when a resume was + * requested. */ - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); - break; - default: - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_READY); - break; + switch (sci_rnc->destination_state) { + case RNC_DEST_SUSPENDED: + case RNC_DEST_SUSPENDED_RESUME: + /* Previously waiting to suspend after posting. + * Now continue onto resumption. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); + break; + default: + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + break; + } } return SCI_SUCCESS; + case SCI_RNC_TX_SUSPENDED: - case SCI_RNC_TX_RX_SUSPENDED: { - struct isci_remote_device *idev = rnc_to_dev(sci_rnc); - struct domain_device *dev = idev->domain_dev; - - /* If this is an expander attached SATA device we must - * invalidate and repost the RNC since this is the only way - * to clear the TCi to NCQ tag mapping table for the RNi. - * All other device types we can just resume. - */ - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, - RNC_DEST_READY); + case SCI_RNC_TX_RX_SUSPENDED: + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else { + struct domain_device *dev = idev->domain_dev; + /* If this is an expander attached SATA device we must + * invalidate and repost the RNC since this is the only + * way to clear the TCi to NCQ tag mapping table for + * the RNi. All other device types we can just resume. + */ + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, RNC_DEST_READY); - if (dev_is_sata(dev) && dev->parent) - sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); - else - sci_change_state(&sci_rnc->sm, SCI_RNC_RESUMING); + if (dev_is_sata(dev) && dev->parent) + sci_change_state(&sci_rnc->sm, + SCI_RNC_INVALIDATING); + else + sci_change_state(&sci_rnc->sm, + SCI_RNC_RESUMING); + } return SCI_SUCCESS; - } + case SCI_RNC_AWAIT_SUSPENSION: - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); + if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) + sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, + cb_p); + else + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), -- cgit v1.2.3-70-g09d2 From 033d19d298b4245da2d3d6c795ea97e419f9ac61 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:03 -0800 Subject: isci: Callbacks to libsas occur under scic_lock and are synchronized. This patch changes the callback mechanism to libsas to only occur while the scic_lock is held; the abort path cleanup of I/Os also checks to make sure IREQ_ABORT_PATH_ACTIVE is clear before proceding. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 10 +++------ drivers/scsi/isci/remote_device.c | 43 +++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 13 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 25d537e2f5c..53c3ad64c99 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1105,8 +1105,6 @@ void isci_host_completion_routine(unsigned long data) list_splice_init(&ihost->requests_to_complete, &completed_request_list); - spin_unlock_irq(&ihost->scic_lock); - /* Process any completions in the list. */ list_for_each_safe(current_position, next_position, &completed_request_list) { @@ -1115,7 +1113,6 @@ void isci_host_completion_routine(unsigned long data) completed_node); task = isci_request_access_task(request); - /* Return the task to libsas */ if (task != NULL) { @@ -1141,11 +1138,12 @@ void isci_host_completion_routine(unsigned long data) } } } + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags)) + wake_up_all(&ihost->eventq); - spin_lock_irq(&ihost->scic_lock); isci_free_tag(ihost, request->io_tag); - spin_unlock_irq(&ihost->scic_lock); } + spin_unlock_irq(&ihost->scic_lock); /* the coalesence timeout doubles at each encoding step, so * update it based on the ilog2 value of the outstanding requests @@ -2703,8 +2701,6 @@ enum sci_status sci_controller_complete_io(struct isci_host *ihost, index = ISCI_TAG_TCI(ireq->io_tag); clear_bit(IREQ_ACTIVE, &ireq->flags); - if (test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) - wake_up_all(&ihost->eventq); return SCI_SUCCESS; default: dev_warn(&ihost->pdev->dev, "%s invalid state: %d\n", diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index d1c2a2294a3..21a9800a9be 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -145,6 +145,39 @@ static bool isci_compare_suspendcount( return localcount != idev->rnc.suspend_count; } +static bool isci_check_reqterm( + struct isci_host *ihost, + struct isci_remote_device *idev, + struct isci_request *ireq, + u32 localcount) +{ + unsigned long flags; + bool res; + + spin_lock_irqsave(&ihost->scic_lock, flags); + res = isci_compare_suspendcount(idev, localcount) + && !test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return res; +} + +static bool isci_check_devempty( + struct isci_host *ihost, + struct isci_remote_device *idev, + u32 localcount) +{ + unsigned long flags; + bool res; + + spin_lock_irqsave(&ihost->scic_lock, flags); + res = isci_compare_suspendcount(idev, localcount) + && idev->started_request_count == 0; + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return res; +} + enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, struct isci_remote_device *idev, @@ -179,17 +212,15 @@ enum sci_status isci_remote_device_terminate_requests( sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, - (isci_compare_suspendcount(idev, - rnc_suspend_count) - && !test_bit(IREQ_ACTIVE, &ireq->flags))); + isci_check_reqterm(ihost, idev, ireq, + rnc_suspend_count)); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, - (isci_compare_suspendcount(idev, - rnc_suspend_count) - && idev->started_request_count == 0)); + isci_check_devempty(ihost, idev, + rnc_suspend_count)); } dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", __func__, idev); -- cgit v1.2.3-70-g09d2 From 621120ca56850249554996c94efe75f8200a2cc0 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:03 -0800 Subject: isci: Manage tag releases differently when aborting tasks. When an individual request is being terminated, the request's tag is managed in the terminate function. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 3 ++- drivers/scsi/isci/remote_device.c | 11 +++++++---- drivers/scsi/isci/request.h | 1 + drivers/scsi/isci/task.c | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 53c3ad64c99..ef2790faeab 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1141,7 +1141,8 @@ void isci_host_completion_routine(unsigned long data) if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags)) wake_up_all(&ihost->eventq); - isci_free_tag(ihost, request->io_tag); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &request->flags)) + isci_free_tag(ihost, request->io_tag); } spin_unlock_irq(&ihost->scic_lock); diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 21a9800a9be..adeda64e512 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -104,15 +104,15 @@ static enum sci_status sci_remote_device_terminate_req( int check_abort, struct isci_request *ireq) { - dev_dbg(&ihost->pdev->dev, - "%s: idev=%p; flags=%lx; req=%p; req target=%p\n", - __func__, idev, idev->flags, ireq, ireq->target_device); - if (!test_bit(IREQ_ACTIVE, &ireq->flags) || (ireq->target_device != idev) || (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags))) return SCI_SUCCESS; + dev_dbg(&ihost->pdev->dev, + "%s: idev=%p; flags=%lx; req=%p; req target=%p\n", + __func__, idev, idev->flags, ireq, ireq->target_device); + set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); return sci_controller_terminate_request(ihost, idev, ireq); @@ -209,11 +209,14 @@ enum sci_status isci_remote_device_terminate_requests( rnc_suspend_count, idev->rnc.suspend_count); if (ireq) { /* Terminate a specific TC. */ + set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); wait_event(ihost->eventq, isci_check_reqterm(ihost, idev, ireq, rnc_suspend_count)); + clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); + isci_free_tag(ihost, ireq->io_tag); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index d12e97531da..1a651579bb3 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -87,6 +87,7 @@ struct isci_request { #define IREQ_PENDING_ABORT 4 /* Set == device was not suspended yet */ #define IREQ_TC_ABORT_POSTED 5 #define IREQ_ABORT_PATH_ACTIVE 6 + #define IREQ_NO_AUTO_FREE_TAG 7 /* Set when being explicitly managed */ unsigned long flags; /* XXX kill ttype and ttype_ptr, allocate full sas_task */ union ttype_ptr_union { diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index c1c6dd0473a..e798c6ae959 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -719,7 +719,8 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); - isci_free_tag(ihost, ireq->io_tag); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); /* The task management part completes last. */ if (tmf_complete) -- cgit v1.2.3-70-g09d2 From d76689e46c8b2180c08575adc830cfda890ceb87 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:04 -0800 Subject: isci: Fix RNC suspend call for SCI_RESUMING state. Instead of immediately transitioning to the SCI_RNC_AWAIT_SUSPENSION state, handle the SCI_RNC_RESUMING suspend transition from the SCI_RNC_READY state like the SCI_RNC_INVALIDATING --> SCI_RNC_POSTING transitions do now, by setting the destination state for the entry into the READY state. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_node_context.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index faeae9554ee..b698081ce2d 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -321,8 +321,6 @@ static void sci_remote_node_context_ready_state_enter(struct sci_base_state_mach { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); enum sci_remote_node_context_destination_state dest_select; - scics_sds_remote_node_context_callback usr_cb = rnc->user_callback; - void *usr_param = rnc->user_cookie; int tell_user = 1; dest_select = rnc->destination_state; @@ -334,12 +332,10 @@ static void sci_remote_node_context_ready_state_enter(struct sci_base_state_mach rnc, rnc->suspend_reason, SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); - if (dest_select == RNC_DEST_SUSPENDED_RESUME) { - sci_remote_node_context_resume(rnc, usr_cb, usr_param); + if (dest_select == RNC_DEST_SUSPENDED_RESUME) tell_user = 0; /* Wait until ready again. */ - } } - if (tell_user && rnc->user_callback) + if (tell_user) sci_remote_node_context_notify_user(rnc); } @@ -584,8 +580,6 @@ enum sci_status sci_remote_node_context_suspend( dest_param = sci_rnc->destination_state; switch (state) { - case SCI_RNC_RESUMING: - break; /* The RNC has been posted, so start the suspend. */ case SCI_RNC_READY: break; case SCI_RNC_INVALIDATING: @@ -596,6 +590,8 @@ enum sci_status sci_remote_node_context_suspend( return SCI_FAILURE_INVALID_STATE; } /* Fall through and handle like SCI_RNC_POSTING */ + case SCI_RNC_RESUMING: + /* Fall through and handle like SCI_RNC_POSTING */ case SCI_RNC_POSTING: /* Set the destination state to AWAIT - this signals the * entry into the SCI_RNC_READY state that a suspension -- cgit v1.2.3-70-g09d2 From 0cce165e2814bc8c08ab229db5e17013971dced7 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:04 -0800 Subject: isci: Wait for RNC resumption before leaving the abort path. In the case of TMF execution, or device resets, wait for the RNC to fully resume before returning to the caller. This ensures that the remote device will not fail I/O requests while waiting for the RNC resumption to complete. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 52 +++++++++++++++++++++++++++++++++++++-- drivers/scsi/isci/remote_device.h | 3 +++ 2 files changed, 53 insertions(+), 2 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index adeda64e512..37e9bdead6f 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1289,6 +1289,48 @@ enum sci_status sci_remote_device_resume( return status; } +static void isci_remote_device_resume_from_abort_complete(void *cbparam) +{ + struct isci_remote_device *idev = cbparam; + struct isci_host *ihost = idev->owning_port->owning_controller; + scics_sds_remote_node_context_callback abort_resume_cb = + idev->abort_resume_cb; + + dev_dbg(scirdev_to_dev(idev), "%s: passing-along resume: %p\n", + __func__, abort_resume_cb); + + if (abort_resume_cb != NULL) { + idev->abort_resume_cb = NULL; + abort_resume_cb(idev->abort_resume_cbparam); + } + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + wake_up(&ihost->eventq); +} + + +void isci_remote_device_wait_for_resume_from_abort( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + dev_dbg(scirdev_to_dev(idev), "%s: starting resume wait: %p\n", + __func__, idev); + + #define MAX_RESUME_MSECS 5 + if (!wait_event_timeout(ihost->eventq, + (!test_bit(IDEV_ABORT_PATH_RESUME_PENDING, + &idev->flags) + || test_bit(IDEV_STOP_PENDING, &idev->flags)), + msecs_to_jiffies(MAX_RESUME_MSECS))) { + + dev_warn(scirdev_to_dev(idev), "%s: #### Timeout waiting for " + "resume: %p\n", __func__, idev); + } + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + + dev_dbg(scirdev_to_dev(idev), "%s: resume wait done: %p\n", + __func__, idev); +} + enum sci_status isci_remote_device_resume_from_abort( struct isci_host *ihost, struct isci_remote_device *idev) @@ -1300,12 +1342,18 @@ enum sci_status isci_remote_device_resume_from_abort( /* Preserve any current resume callbacks, for instance from other * resumptions. */ + idev->abort_resume_cb = idev->rnc.user_callback; + idev->abort_resume_cbparam = idev->rnc.user_cookie; + set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); - status = sci_remote_device_resume(idev, idev->rnc.user_callback, - idev->rnc.user_cookie); + status = sci_remote_device_resume( + idev, isci_remote_device_resume_from_abort_complete, + idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); + isci_remote_device_wait_for_resume_from_abort(ihost, idev); return status; } + /** * sci_remote_device_start() - This method will start the supplied remote * device. This method enables normal IO requests to flow through to the diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 53564c35cf2..ff34c4e8c1b 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -87,6 +87,7 @@ struct isci_remote_device { #define IDEV_IO_NCQERROR 5 #define IDEV_RNC_LLHANG_ENABLED 6 #define IDEV_ABORT_PATH_ACTIVE 7 + #define IDEV_ABORT_PATH_RESUME_PENDING 8 unsigned long flags; struct kref kref; struct isci_port *isci_port; @@ -101,6 +102,8 @@ struct isci_remote_device { u32 started_request_count; struct isci_request *working_request; u32 not_ready_reason; + scics_sds_remote_node_context_callback abort_resume_cb; + void *abort_resume_cbparam; }; #define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000 -- cgit v1.2.3-70-g09d2 From 1db79b3e784bffe7e00f9462a5c3441746e48632 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:05 -0800 Subject: isci: Directly control IREQ_ABORT_PATH_ACTIVE when completing TMFs. TMF requests, unlike normal I/O requests, need to handle I/O management conditions in the completion function because TMFs are not handled in the completion tasklet. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/task.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index e798c6ae959..084f8f73fad 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -719,6 +719,9 @@ isci_task_request_complete(struct isci_host *ihost, */ set_bit(IREQ_TERMINATED, &ireq->flags); + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) isci_free_tag(ihost, ireq->io_tag); -- cgit v1.2.3-70-g09d2 From 28de92bef0fb0c3953aa73d31a961422ef900e6a Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:06 -0800 Subject: isci: Add protocol indicator for TMF requests. Requests contructed as task management requests need to have the protocol indicator set so the completion decode can observe any RNC suspension conditions. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/request.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 432585b04dc..415d5f55d1c 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -3130,6 +3130,12 @@ enum sci_status sci_task_request_construct(struct isci_host *ihost, if (dev->dev_type == SAS_END_DEV || dev_is_sata(dev)) { set_bit(IREQ_TMF, &ireq->flags); memset(ireq->tc, 0, sizeof(struct scu_task_context)); + + /* Set the protocol indicator. */ + if (dev_is_sata(dev)) + ireq->protocol = SAS_PROTOCOL_STP; + else + ireq->protocol = SAS_PROTOCOL_SSP; } else status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; -- cgit v1.2.3-70-g09d2 From 8c731888bf1be8d15d587ab1b4da80553302e653 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:06 -0800 Subject: isci: Added timeouts to RNC suspensions in the abort path. This change adds timeouts to the RNC suspension wait. It makes the suspend and resume timeouts the same. The previous resume timeout of 5 ms was too short, and timeouts were seen in resumptions of devices in the abort task/LUN reset path - which would receive an RNC resumed message within a tenth of a second later. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 58 +++++++++++++++++++++++++++++---- drivers/scsi/isci/remote_node_context.c | 8 ++--- 2 files changed, 55 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 37e9bdead6f..be9f0e0be4f 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -207,23 +207,67 @@ enum sci_status isci_remote_device_terminate_requests( "about to wait\n", __func__, idev, ireq, idev->started_request_count, rnc_suspend_count, idev->rnc.suspend_count); + + #define MAX_SUSPEND_MSECS 10000 if (ireq) { /* Terminate a specific TC. */ set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); sci_remote_device_terminate_req(ihost, idev, 0, ireq); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_event(ihost->eventq, - isci_check_reqterm(ihost, idev, ireq, - rnc_suspend_count)); + if (!wait_event_timeout(ihost->eventq, + isci_check_reqterm(ihost, idev, ireq, + rnc_suspend_count), + msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + + dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n", + __func__, ihost->id); + dev_dbg(&ihost->pdev->dev, + "%s: ******* Timeout waiting for " + "suspend; idev=%p, current state %s; " + "started_request_count=%d, flags=%lx\n\t" + "rnc_suspend_count=%d, rnc.suspend_count=%d " + "RNC: current state %s, current " + "suspend_type %x dest state %d;\n" + "ireq=%p, ireq->flags = %lx\n", + __func__, idev, + dev_state_name(idev->sm.current_state_id), + idev->started_request_count, idev->flags, + rnc_suspend_count, idev->rnc.suspend_count, + rnc_state_name(idev->rnc.sm.current_state_id), + idev->rnc.suspend_type, + idev->rnc.destination_state, + ireq, ireq->flags); + } clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); isci_free_tag(ihost, ireq->io_tag); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - wait_event(ihost->eventq, - isci_check_devempty(ihost, idev, - rnc_suspend_count)); + if (!wait_event_timeout(ihost->eventq, + isci_check_devempty(ihost, idev, + rnc_suspend_count), + msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + + dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n", + __func__, ihost->id); + dev_dbg(&ihost->pdev->dev, + "%s: ******* Timeout waiting for " + "suspend; idev=%p, current state %s; " + "started_request_count=%d, flags=%lx\n\t" + "rnc_suspend_count=%d, " + "RNC: current state %s, " + "rnc.suspend_count=%d, current " + "suspend_type %x dest state %d\n", + __func__, idev, + dev_state_name(idev->sm.current_state_id), + idev->started_request_count, idev->flags, + rnc_suspend_count, + rnc_state_name(idev->rnc.sm.current_state_id), + idev->rnc.suspend_count, + idev->rnc.suspend_type, + idev->rnc.destination_state); + } } dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", __func__, idev); @@ -1315,7 +1359,7 @@ void isci_remote_device_wait_for_resume_from_abort( dev_dbg(scirdev_to_dev(idev), "%s: starting resume wait: %p\n", __func__, idev); - #define MAX_RESUME_MSECS 5 + #define MAX_RESUME_MSECS 10000 if (!wait_event_timeout(ihost->eventq, (!test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags) diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index b698081ce2d..a0a62e3a500 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -445,7 +445,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: /* We really dont care if the hardware is going to suspend * the device since it's being invalidated anyway */ - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: SCIC Remote Node Context 0x%p was " "suspeneded by hardware while being " "invalidated.\n", __func__, sci_rnc); @@ -464,7 +464,7 @@ enum sci_status sci_remote_node_context_event_handler(struct sci_remote_node_con case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: /* We really dont care if the hardware is going to suspend * the device since it's being resumed anyway */ - dev_dbg(scirdev_to_dev(rnc_to_dev(sci_rnc)), + dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: SCIC Remote Node Context 0x%p was " "suspeneded by hardware while being resumed.\n", __func__, sci_rnc); @@ -568,9 +568,9 @@ enum sci_status sci_remote_node_context_suspend( RNC_DEST_UNSPECIFIED; dev_dbg(scirdev_to_dev(idev), - "%s: current state %d, current suspend_type %x dest state %d," + "%s: current state %s, current suspend_type %x dest state %d," " arg suspend_reason %d, arg suspend_type %x", - __func__, state, sci_rnc->suspend_type, + __func__, rnc_state_name(state), sci_rnc->suspend_type, sci_rnc->destination_state, suspend_reason, suspend_type); -- cgit v1.2.3-70-g09d2 From c5457a82a404db3c447df22e6425c5c140c4bee1 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:07 -0800 Subject: isci: Change the phy control and link reset interface for HW reasons. There is an apparent HW lockup caused when the PE is disabled while there is an outstanding TC in progress. This change puts the link into OOB to force the TC to end before the PE is disabled. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/phy.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index 85b26ac9074..18f43d4c30b 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -1442,12 +1442,14 @@ int isci_phy_control(struct asd_sas_phy *sas_phy, switch (func) { case PHY_FUNC_DISABLE: spin_lock_irqsave(&ihost->scic_lock, flags); + scu_link_layer_start_oob(iphy); sci_phy_stop(iphy); spin_unlock_irqrestore(&ihost->scic_lock, flags); break; case PHY_FUNC_LINK_RESET: spin_lock_irqsave(&ihost->scic_lock, flags); + scu_link_layer_start_oob(iphy); sci_phy_stop(iphy); sci_phy_start(iphy); spin_unlock_irqrestore(&ihost->scic_lock, flags); -- cgit v1.2.3-70-g09d2 From 1f05388933cb6e57ed9e51768c194ff145002f3b Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:08 -0800 Subject: isci: Don't wait for an RNC suspend if it's being destroyed. Make sure that the wait for suspend can handle the RNC destruction case. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 20 ++++++++++++++++---- drivers/scsi/isci/remote_node_context.c | 5 +++++ drivers/scsi/isci/remote_node_context.h | 7 +++++++ 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index be9f0e0be4f..68ab4fb9032 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -142,7 +142,12 @@ static bool isci_compare_suspendcount( u32 localcount) { smp_rmb(); - return localcount != idev->rnc.suspend_count; + + /* Check for a change in the suspend count, or the RNC + * being destroyed. + */ + return (localcount != idev->rnc.suspend_count) + || sci_remote_node_context_is_being_destroyed(&idev->rnc); } static bool isci_check_reqterm( @@ -1380,7 +1385,8 @@ enum sci_status isci_remote_device_resume_from_abort( struct isci_remote_device *idev) { unsigned long flags; - enum sci_status status; + enum sci_status status = SCI_SUCCESS; + int destroyed; spin_lock_irqsave(&ihost->scic_lock, flags); /* Preserve any current resume callbacks, for instance from other @@ -1390,11 +1396,17 @@ enum sci_status isci_remote_device_resume_from_abort( idev->abort_resume_cbparam = idev->rnc.user_cookie; set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); - status = sci_remote_device_resume( + destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc); + if (!destroyed) + status = sci_remote_device_resume( idev, isci_remote_device_resume_from_abort_complete, idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - isci_remote_device_wait_for_resume_from_abort(ihost, idev); + if (!destroyed) + isci_remote_device_wait_for_resume_from_abort(ihost, idev); + else + clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + return status; } diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index a0a62e3a500..920c2bb3933 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -270,6 +270,8 @@ static void sci_remote_node_context_invalidate_context_buffer(struct sci_remote_ static void sci_remote_node_context_initial_state_enter(struct sci_base_state_machine *sm) { struct sci_remote_node_context *rnc = container_of(sm, typeof(*rnc), sm); + struct isci_remote_device *idev = rnc_to_dev(rnc); + struct isci_host *ihost = idev->owning_port->owning_controller; /* Check to see if we have gotten back to the initial state because * someone requested to destroy the remote node context object. @@ -277,6 +279,9 @@ static void sci_remote_node_context_initial_state_enter(struct sci_base_state_ma if (sm->previous_state_id == SCI_RNC_INVALIDATING) { rnc->destination_state = RNC_DEST_UNSPECIFIED; sci_remote_node_context_notify_user(rnc); + + smp_wmb(); + wake_up(&ihost->eventq); } } diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index c61c02e095a..0d4a24d647b 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -226,4 +226,11 @@ enum sci_status sci_remote_node_context_start_io(struct sci_remote_node_context int sci_remote_node_context_is_safe_to_abort( struct sci_remote_node_context *sci_rnc); +static inline bool sci_remote_node_context_is_being_destroyed( + struct sci_remote_node_context *sci_rnc) +{ + return ((sci_rnc->sm.current_state_id == SCI_RNC_INVALIDATING) + && (sci_rnc->destination_state == RNC_DEST_FINAL)) + || (sci_rnc->sm.current_state_id == SCI_RNC_INITIAL); +} #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ -- cgit v1.2.3-70-g09d2 From 87805162b6af20d2ad386a49aec13b753cca523a Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Thu, 8 Mar 2012 22:42:09 -0800 Subject: isci: Restore the ATAPI device RNC management code. The ATAPI specific and STP general RNC suspension code had been incorrectly removed from the remote device code. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 49 +++++++++++++++++++++++---------------- drivers/scsi/isci/remote_device.h | 2 ++ drivers/scsi/isci/request.c | 3 +++ 3 files changed, 34 insertions(+), 20 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 68ab4fb9032..48765aa8432 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -72,8 +72,8 @@ const char *dev_state_name(enum sci_remote_device_states state) } #undef C -static enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, - enum sci_remote_node_suspension_reasons reason) +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, + enum sci_remote_node_suspension_reasons reason) { return sci_remote_node_context_suspend(&idev->rnc, reason, SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT); @@ -565,6 +565,8 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, u32 event_code) { enum sci_status status; + struct sci_base_state_machine *sm = &idev->sm; + enum sci_remote_device_states state = sm->current_state_id; switch (scu_get_event_type(event_code)) { case SCU_EVENT_TYPE_RNC_OPS_MISC: @@ -603,6 +605,30 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev, if (status != SCI_SUCCESS) return status; + /* Decode device-specific states that may require an RNC resume during + * normal operation. When the abort path is active, these resumes are + * managed when the abort path exits. + */ + if (state == SCI_STP_DEV_ATAPI_ERROR) { + /* For ATAPI error state resume the RNC right away. */ + if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || + scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { + return sci_remote_node_context_resume(&idev->rnc, + atapi_remote_device_resume_done, + idev); + } + } + + if (state == SCI_STP_DEV_IDLE) { + + /* We pick up suspension events to handle specifically to this + * state. We resume the RNC right away. + */ + if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || + scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) + status = sci_remote_node_context_resume(&idev->rnc, NULL, NULL); + } + return status; } @@ -1137,21 +1163,6 @@ static void sci_stp_remote_device_ready_ncq_error_substate_enter(struct sci_base idev->not_ready_reason); } -static void sci_stp_remote_device_atapi_error_substate_enter( - struct sci_base_state_machine *sm) -{ - struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); - - /* This state is entered when an I/O is decoded with an error - * condition. By this point the RNC expected suspension state is set. - * The error conditions suspend the device, so unsuspend here if - * possible. - */ - sci_remote_node_context_resume(&idev->rnc, - atapi_remote_device_resume_done, - idev); -} - static void sci_smp_remote_device_ready_idle_substate_enter(struct sci_base_state_machine *sm) { struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); @@ -1202,9 +1213,7 @@ static const struct sci_base_state sci_remote_device_state_table[] = { [SCI_STP_DEV_NCQ_ERROR] = { .enter_state = sci_stp_remote_device_ready_ncq_error_substate_enter, }, - [SCI_STP_DEV_ATAPI_ERROR] = { - .enter_state = sci_stp_remote_device_atapi_error_substate_enter, - }, + [SCI_STP_DEV_ATAPI_ERROR] = { }, [SCI_STP_DEV_AWAIT_RESET] = { }, [SCI_SMP_DEV_IDLE] = { .enter_state = sci_smp_remote_device_ready_idle_substate_enter, diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index ff34c4e8c1b..7674caae1d8 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -382,4 +382,6 @@ enum sci_status isci_remote_device_terminate_requests( struct isci_host *ihost, struct isci_remote_device *idev, struct isci_request *ireq); +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, + enum sci_remote_node_suspension_reasons reason); #endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 415d5f55d1c..6c530e4275e 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2118,6 +2118,9 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq * completion. */ if (ireq->stp.rsp.fis_type == FIS_REGD2H) { + sci_remote_device_suspend(ireq->target_device, + SCI_SW_SUSPEND_NORMAL); + ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE; ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID; sci_change_state(&ireq->sm, SCI_REQ_COMPLETED); -- cgit v1.2.3-70-g09d2 From 397497dd61948b0d59d1d21812b93c97b0eeb2dd Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Sat, 10 Mar 2012 05:46:46 +0000 Subject: isci: Check IDEV_GONE before performing abort path operations. In the link fail path, set IDEV_GONE for every device on the domain when the last link in the port fails. In the abort path functions like isci_reset_device, make sure that there has not already been a detected domain failure with the device by checking IDEV_GONE, before performing any kind of hard reset, SMP phy control, or TMF operation. The check for IDEV_GONE makes sure that the device in the abort path really has control of the port with which it is associated. This prevents starting hard resets at incorrect times and scheduling unnecessary LUN resets for SATA devices. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/port.c | 23 ++++++++++++++++++++ drivers/scsi/isci/task.c | 55 ++++++++++++++++++++++++++++++------------------ 2 files changed, 57 insertions(+), 21 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index da0c4e1b9b3..2fb85bf7544 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -240,9 +240,32 @@ static void isci_port_link_down(struct isci_host *isci_host, struct isci_phy *isci_phy, struct isci_port *isci_port) { + struct isci_remote_device *isci_device; + dev_dbg(&isci_host->pdev->dev, "%s: isci_port = %p\n", __func__, isci_port); + if (isci_port) { + + /* check to see if this is the last phy on this port. */ + if (isci_phy->sas_phy.port && + isci_phy->sas_phy.port->num_phys == 1) { + /* change the state for all devices on this port. The + * next task sent to this device will be returned as + * SAS_TASK_UNDELIVERED, and the scsi mid layer will + * remove the target + */ + list_for_each_entry(isci_device, + &isci_port->remote_dev_list, + node) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", + __func__, isci_device); + set_bit(IDEV_GONE, &isci_device->flags); + } + } + } + /* Notify libsas of the borken link, this will trigger calls to our * isci_port_deformed and isci_dev_gone functions. */ diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 084f8f73fad..6bc74eb012c 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -421,7 +421,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) struct isci_host *ihost = dev_to_ihost(dev); struct isci_remote_device *idev; unsigned long flags; - int ret; + int ret = TMF_RESP_FUNC_COMPLETE; spin_lock_irqsave(&ihost->scic_lock, flags); idev = isci_get_device(dev->lldd_dev); @@ -447,12 +447,12 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun) goto out; } /* All pending I/Os have been terminated and cleaned up. */ - if (dev_is_sata(dev)) { - sas_ata_schedule_reset(dev); - ret = TMF_RESP_FUNC_COMPLETE; - } else { - /* Send the task management part of the reset. */ - ret = isci_task_send_lu_reset_sas(ihost, idev, lun); + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (dev_is_sata(dev)) + sas_ata_schedule_reset(dev); + else + /* Send the task management part of the reset. */ + ret = isci_task_send_lu_reset_sas(ihost, idev, lun); } out: isci_put_device(idev); @@ -512,8 +512,17 @@ int isci_task_abort_task(struct sas_task *task) spin_unlock_irqrestore(&ihost->scic_lock, flags); dev_warn(&ihost->pdev->dev, - "%s: dev = %p, task = %p, old_request == %p\n", - __func__, idev, task, old_request); + "%s: dev = %p (%s%s), task = %p, old_request == %p\n", + __func__, idev, + (dev_is_sata(task->dev) ? "STP/SATA" + : ((dev_is_expander(task->dev)) + ? "SMP" + : "SSP")), + ((idev) ? ((test_bit(IDEV_GONE, &idev->flags)) + ? " IDEV_GONE" + : "") + : " "), + task, old_request); /* Device reset conditions signalled in task_state_flags are the * responsbility of libsas to observe at the start of the error @@ -552,7 +561,8 @@ int isci_task_abort_task(struct sas_task *task) if (task->task_proto == SAS_PROTOCOL_SMP || sas_protocol_ata(task->task_proto) || - test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) { + test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) || + test_bit(IDEV_GONE, &idev->flags)) { spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -561,7 +571,8 @@ int isci_task_abort_task(struct sas_task *task) dev_warn(&ihost->pdev->dev, "%s: %s request" - " or complete_in_target (%d), thus no TMF\n", + " or complete_in_target (%d), " + "or IDEV_GONE (%d), thus no TMF\n", __func__, ((task->task_proto == SAS_PROTOCOL_SMP) ? "SMP" @@ -570,7 +581,8 @@ int isci_task_abort_task(struct sas_task *task) : "") ), test_bit(IREQ_COMPLETE_IN_TARGET, - &old_request->flags)); + &old_request->flags), + test_bit(IDEV_GONE, &idev->flags)); spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | @@ -734,7 +746,7 @@ static int isci_reset_device(struct isci_host *ihost, struct domain_device *dev, struct isci_remote_device *idev) { - int rc = TMF_RESP_FUNC_COMPLETE, reset_stat; + int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1; struct sas_phy *phy = sas_get_local_phy(dev); struct isci_port *iport = dev->port->lldd_port; @@ -752,14 +764,15 @@ static int isci_reset_device(struct isci_host *ihost, * primary duty of this function is to cleanup tasks, so that is the * relevant status. */ - - if (scsi_is_sas_phy_local(phy)) { - struct isci_phy *iphy = &ihost->phys[phy->number]; - - reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy); - } else - reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); - + if (!test_bit(IDEV_GONE, &idev->flags)) { + if (scsi_is_sas_phy_local(phy)) { + struct isci_phy *iphy = &ihost->phys[phy->number]; + + reset_stat = isci_port_perform_hard_reset(ihost, iport, + iphy); + } else + reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); + } /* Explicitly resume the RNC here, since there was no task sent. */ isci_remote_device_resume_from_abort(ihost, idev); -- cgit v1.2.3-70-g09d2 From f8381807ebdfffa34c2c5aa38eda33673d1a7adf Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Sun, 4 Mar 2012 12:44:53 +0000 Subject: isci: Remove obviated host callback list. Since the callbacks to libsas now occur under scic_lock, there is no longer any reason to save the completed requests in a separate list for completion to libsas. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.c | 78 +++++++++++++++------------------------------ drivers/scsi/isci/host.h | 2 +- drivers/scsi/isci/init.c | 1 - drivers/scsi/isci/request.c | 14 +++----- drivers/scsi/isci/request.h | 2 -- 5 files changed, 31 insertions(+), 66 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index ef2790faeab..45385f53164 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1077,6 +1077,32 @@ static void sci_controller_completion_handler(struct isci_host *ihost) writel(0, &ihost->smu_registers->interrupt_mask); } +void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task) +{ + task->lldd_task = NULL; + if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags) && + !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (test_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags)) { + /* Normal notification (task_done) */ + dev_dbg(&ihost->pdev->dev, + "%s: Normal - ireq/task = %p/%p\n", + __func__, ireq, task); + + task->task_done(task); + } else { + dev_dbg(&ihost->pdev->dev, + "%s: Error - ireq/task = %p/%p\n", + __func__, ireq, task); + + sas_task_abort(task); + } + } + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + wake_up_all(&ihost->eventq); + + if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); +} /** * isci_host_completion_routine() - This function is the delayed service * routine that calls the sci core library's completion handler. It's @@ -1088,62 +1114,10 @@ static void sci_controller_completion_handler(struct isci_host *ihost) void isci_host_completion_routine(unsigned long data) { struct isci_host *ihost = (struct isci_host *)data; - struct list_head completed_request_list; - struct list_head *current_position; - struct list_head *next_position; - struct isci_request *request; - struct sas_task *task; u16 active; - INIT_LIST_HEAD(&completed_request_list); - spin_lock_irq(&ihost->scic_lock); - sci_controller_completion_handler(ihost); - - /* Take the lists of completed I/Os from the host. */ - list_splice_init(&ihost->requests_to_complete, - &completed_request_list); - - /* Process any completions in the list. */ - list_for_each_safe(current_position, next_position, - &completed_request_list) { - - request = list_entry(current_position, struct isci_request, - completed_node); - task = isci_request_access_task(request); - - /* Return the task to libsas */ - if (task != NULL) { - - task->lldd_task = NULL; - if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags) && - !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - if (test_bit(IREQ_COMPLETE_IN_TARGET, - &request->flags)) { - - /* Normal notification (task_done) */ - dev_dbg(&ihost->pdev->dev, "%s: Normal" - " - request/task = %p/%p\n", - __func__, request, task); - - task->task_done(task); - } else { - dev_warn(&ihost->pdev->dev, - "%s: Error - request/task" - " = %p/%p\n", - __func__, request, task); - - sas_task_abort(task); - } - } - } - if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &request->flags)) - wake_up_all(&ihost->eventq); - - if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &request->flags)) - isci_free_tag(ihost, request->io_tag); - } spin_unlock_irq(&ihost->scic_lock); /* the coalesence timeout doubles at each encoding step, so diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index eaa13c0be09..8e8b46322c6 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -204,7 +204,6 @@ struct isci_host { unsigned long flags; wait_queue_head_t eventq; struct tasklet_struct completion_tasklet; - struct list_head requests_to_complete; spinlock_t scic_lock; struct isci_request *reqs[SCI_MAX_IO_REQUESTS]; struct isci_remote_device devices[SCI_MAX_REMOTE_DEVICES]; @@ -473,6 +472,7 @@ void isci_host_scan_start(struct Scsi_Host *); u16 isci_alloc_tag(struct isci_host *ihost); enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag); void isci_tci_free(struct isci_host *ihost, u16 tci); +void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task); int isci_host_init(struct isci_host *); void isci_host_completion_routine(unsigned long data); diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index f2a8a334630..47e28b55502 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -555,7 +555,6 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) return NULL; } - INIT_LIST_HEAD(&ihost->requests_to_complete); for (i = 0; i < SCI_MAX_PORTS; i++) { struct isci_port *iport = &ihost->ports[i]; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 6c530e4275e..7a0431c7349 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -2748,13 +2748,9 @@ static void isci_request_io_request_complete(struct isci_host *ihost, enum exec_status status = SAS_ABORTED_TASK; dev_dbg(&ihost->pdev->dev, - "%s: request = %p, task = %p,\n" + "%s: request = %p, task = %p, " "task->data_dir = %d completion_status = 0x%x\n", - __func__, - request, - task, - task->data_dir, - completion_status); + __func__, request, task, task->data_dir, completion_status); /* The request is done from an SCU HW perspective. */ @@ -2955,9 +2951,6 @@ static void isci_request_io_request_complete(struct isci_host *ihost, } spin_unlock_irqrestore(&task->task_state_lock, task_flags); - /* Add to the completed list. */ - list_add(&request->completed_node, &ihost->requests_to_complete); - /* complete the io request to the core. */ sci_controller_complete_io(ihost, request->target_device, request); @@ -2966,6 +2959,8 @@ static void isci_request_io_request_complete(struct isci_host *ihost, * task to recognize the already completed case. */ set_bit(IREQ_TERMINATED, &request->flags); + + ireq_done(ihost, request, task); } static void sci_request_started_state_enter(struct sci_base_state_machine *sm) @@ -3416,7 +3411,6 @@ static struct isci_request *isci_request_from_tag(struct isci_host *ihost, u16 t ireq->io_request_completion = NULL; ireq->flags = 0; ireq->num_sg_entries = 0; - INIT_LIST_HEAD(&ireq->completed_node); return ireq; } diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h index 1a651579bb3..aff95317fcf 100644 --- a/drivers/scsi/isci/request.h +++ b/drivers/scsi/isci/request.h @@ -95,8 +95,6 @@ struct isci_request { struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */ } ttype_ptr; struct isci_host *isci_host; - /* For use in the requests_to_{complete|abort} lists: */ - struct list_head completed_node; dma_addr_t request_daddr; dma_addr_t zero_scatter_daddr; unsigned int num_sg_entries; -- cgit v1.2.3-70-g09d2 From 3ef768c6c0caa83b9fe66f19a18898ed0315ac36 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Mon, 12 Mar 2012 17:29:51 -0700 Subject: isci: Manage the IREQ_NO_AUTO_FREE_TAG under scic_lock. Since there is a possibilty of a timeout waiting for the RNC suspension, handle the exit case from the task termination under scic_lock, and leave the tag allocated if the termination timed-out. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 48765aa8432..a3a6487264e 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -243,8 +243,11 @@ enum sci_status isci_remote_device_terminate_requests( idev->rnc.destination_state, ireq, ireq->flags); } + spin_lock_irqsave(&ihost->scic_lock, flags); clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); - isci_free_tag(ihost, ireq->io_tag); + if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) + isci_free_tag(ihost, ireq->io_tag); + spin_unlock_irqrestore(&ihost->scic_lock, flags); } else { /* Terminate all TCs. */ sci_remote_device_terminate_requests(idev); -- cgit v1.2.3-70-g09d2 From 79cbab89ff31b6c6ab896d4ed5e3b2ae65193a96 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Tue, 13 Mar 2012 16:36:35 -0700 Subject: isci: Fix RNC AWAIT_SUSPENSION->INVALIDATING transition. The RNC state machine would incorrectly transition from SCI_RNC_AWAIT_SUSPENSION directly to SCI_RNC_INVALIDATING when a destruct request was made. This would skip the increment of the suspension count and the abort of pending TCs (although the invalidating state would at least cleanup outstanding TCs). Instead, the RNC will transition to SCI_RNC_SUSPENDED and then start the destruction process. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_node_context.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 920c2bb3933..6f0b61b2cca 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -222,13 +222,19 @@ static void sci_remote_node_context_notify_user( static void sci_remote_node_context_continue_state_transitions(struct sci_remote_node_context *rnc) { - if ((rnc->destination_state == RNC_DEST_READY) || - (rnc->destination_state == RNC_DEST_SUSPENDED_RESUME)) { + switch (rnc->destination_state) { + case RNC_DEST_READY: + case RNC_DEST_SUSPENDED_RESUME: rnc->destination_state = RNC_DEST_READY; + /* Fall through... */ + case RNC_DEST_FINAL: sci_remote_node_context_resume(rnc, rnc->user_callback, - rnc->user_cookie); - } else + rnc->user_cookie); + break; + default: rnc->destination_state = RNC_DEST_UNSPECIFIED; + break; + } } static void sci_remote_node_context_validate_context_buffer(struct sci_remote_node_context *sci_rnc) @@ -539,10 +545,12 @@ enum sci_status sci_remote_node_context_destruct(struct sci_remote_node_context case SCI_RNC_READY: case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: - case SCI_RNC_AWAIT_SUSPENSION: sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); sci_change_state(&sci_rnc->sm, SCI_RNC_INVALIDATING); return SCI_SUCCESS; + case SCI_RNC_AWAIT_SUSPENSION: + sci_remote_node_context_setup_to_destroy(sci_rnc, cb_fn, cb_p); + return SCI_SUCCESS; case SCI_RNC_INITIAL: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), "%s: invalid state: %s\n", __func__, -- cgit v1.2.3-70-g09d2 From 6c6aacbb7787dccc6fb662bae66e599bbf0f07b5 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Tue, 13 Mar 2012 17:03:00 -0700 Subject: isci: Fixed RNC bug that lost the suspension or resumption during destroy This fix corrects the saving of resume parameters when the destruction of the RNC has already been directed, and makes sure not to overwrite the RNC destruction callbacks. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/remote_node_context.c | 96 +++++++++++++-------------------- 1 file changed, 38 insertions(+), 58 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index 6f0b61b2cca..f5792a901e0 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -160,15 +160,6 @@ static void sci_remote_node_context_construct_buffer(struct sci_remote_node_cont rnc->ssp.oaf_source_zone_group = 0; rnc->ssp.oaf_more_compatibility_features = 0; } - -static void sci_remote_node_context_save_cbparams( - struct sci_remote_node_context *sci_rnc, - scics_sds_remote_node_context_callback callback, - void *callback_parameter) -{ - sci_rnc->user_callback = callback; - sci_rnc->user_cookie = callback_parameter; -} /** * * @sci_rnc: @@ -187,9 +178,10 @@ static void sci_remote_node_context_setup_to_resume( { if (sci_rnc->destination_state != RNC_DEST_FINAL) { sci_rnc->destination_state = dest_param; - if (callback != NULL) - sci_remote_node_context_save_cbparams( - sci_rnc, callback, callback_parameter); + if (callback != NULL) { + sci_rnc->user_callback = callback; + sci_rnc->user_cookie = callback_parameter; + } } } @@ -610,7 +602,8 @@ enum sci_status sci_remote_node_context_suspend( * entry into the SCI_RNC_READY state that a suspension * needs to be done immediately. */ - sci_rnc->destination_state = RNC_DEST_SUSPENDED; + if (sci_rnc->destination_state != RNC_DEST_FINAL) + sci_rnc->destination_state = RNC_DEST_SUSPENDED; sci_rnc->suspend_type = suspend_type; sci_rnc->suspend_reason = suspend_reason; return SCI_SUCCESS; @@ -680,12 +673,9 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s if (sci_rnc->remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) return SCI_FAILURE_INVALID_STATE; - if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) - sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, - cb_p); - else { - sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, - cb_p, RNC_DEST_READY); + sci_remote_node_context_setup_to_resume(sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) { sci_remote_node_context_construct_buffer(sci_rnc); sci_change_state(&sci_rnc->sm, SCI_RNC_POSTING); } @@ -694,38 +684,30 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s case SCI_RNC_POSTING: case SCI_RNC_INVALIDATING: case SCI_RNC_RESUMING: - if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) - sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, - cb_p); - else { - /* We are still waiting to post when a resume was - * requested. + /* We are still waiting to post when a resume was + * requested. + */ + switch (sci_rnc->destination_state) { + case RNC_DEST_SUSPENDED: + case RNC_DEST_SUSPENDED_RESUME: + /* Previously waiting to suspend after posting. + * Now continue onto resumption. */ - switch (sci_rnc->destination_state) { - case RNC_DEST_SUSPENDED: - case RNC_DEST_SUSPENDED_RESUME: - /* Previously waiting to suspend after posting. - * Now continue onto resumption. - */ - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); - break; - default: - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_READY); - break; - } + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_SUSPENDED_RESUME); + break; + default: + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, + RNC_DEST_READY); + break; } return SCI_SUCCESS; case SCI_RNC_TX_SUSPENDED: case SCI_RNC_TX_RX_SUSPENDED: - if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) - sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, - cb_p); - else { + { struct domain_device *dev = idev->domain_dev; /* If this is an expander attached SATA device we must * invalidate and repost the RNC since this is the only @@ -735,23 +717,21 @@ enum sci_status sci_remote_node_context_resume(struct sci_remote_node_context *s sci_remote_node_context_setup_to_resume( sci_rnc, cb_fn, cb_p, RNC_DEST_READY); - if (dev_is_sata(dev) && dev->parent) - sci_change_state(&sci_rnc->sm, - SCI_RNC_INVALIDATING); - else - sci_change_state(&sci_rnc->sm, - SCI_RNC_RESUMING); + if (!test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) { + if ((dev_is_sata(dev) && dev->parent) || + (sci_rnc->destination_state == RNC_DEST_FINAL)) + sci_change_state(&sci_rnc->sm, + SCI_RNC_INVALIDATING); + else + sci_change_state(&sci_rnc->sm, + SCI_RNC_RESUMING); + } } return SCI_SUCCESS; case SCI_RNC_AWAIT_SUSPENSION: - if (test_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags)) - sci_remote_node_context_save_cbparams(sci_rnc, cb_fn, - cb_p); - else - sci_remote_node_context_setup_to_resume( - sci_rnc, cb_fn, cb_p, - RNC_DEST_SUSPENDED_RESUME); + sci_remote_node_context_setup_to_resume( + sci_rnc, cb_fn, cb_p, RNC_DEST_SUSPENDED_RESUME); return SCI_SUCCESS; default: dev_warn(scirdev_to_dev(rnc_to_dev(sci_rnc)), -- cgit v1.2.3-70-g09d2 From de2eb4d5c5c25e8fb75d1e19092f24b83cb7d8d5 Mon Sep 17 00:00:00 2001 From: Jeff Skirvin Date: Tue, 13 Mar 2012 17:15:11 -0700 Subject: isci: End the RNC resumption wait when the RNC is destroyed. While the RNC is suspended for I/O cleanup, the remote device can be stopped and the RNC setup for destruction. These changes accomodate that case in the abort path. Signed-off-by: Jeff Skirvin Signed-off-by: Dan Williams --- drivers/scsi/isci/host.h | 5 +++++ drivers/scsi/isci/remote_device.c | 29 +++++++++++++++++++++-------- drivers/scsi/isci/remote_node_context.c | 4 ++++ drivers/scsi/isci/remote_node_context.h | 6 +++--- 4 files changed, 33 insertions(+), 11 deletions(-) (limited to 'drivers/scsi') diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 8e8b46322c6..9ab58e0540e 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -340,6 +340,11 @@ static inline struct isci_host *dev_to_ihost(struct domain_device *dev) return dev->port->ha->lldd_ha; } +static inline struct isci_host *idev_to_ihost(struct isci_remote_device *idev) +{ + return dev_to_ihost(idev->domain_dev); +} + /* we always use protocol engine group zero */ #define ISCI_PEG 0 diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index a3a6487264e..c3aa6c5457b 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -1368,27 +1368,40 @@ static void isci_remote_device_resume_from_abort_complete(void *cbparam) wake_up(&ihost->eventq); } +static bool isci_remote_device_test_resume_done( + struct isci_host *ihost, + struct isci_remote_device *idev) +{ + unsigned long flags; + bool done; + + spin_lock_irqsave(&ihost->scic_lock, flags); + done = !test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags) + || test_bit(IDEV_STOP_PENDING, &idev->flags) + || sci_remote_node_context_is_being_destroyed(&idev->rnc); + spin_unlock_irqrestore(&ihost->scic_lock, flags); + + return done; +} void isci_remote_device_wait_for_resume_from_abort( struct isci_host *ihost, struct isci_remote_device *idev) { - dev_dbg(scirdev_to_dev(idev), "%s: starting resume wait: %p\n", + dev_dbg(&ihost->pdev->dev, "%s: starting resume wait: %p\n", __func__, idev); #define MAX_RESUME_MSECS 10000 if (!wait_event_timeout(ihost->eventq, - (!test_bit(IDEV_ABORT_PATH_RESUME_PENDING, - &idev->flags) - || test_bit(IDEV_STOP_PENDING, &idev->flags)), - msecs_to_jiffies(MAX_RESUME_MSECS))) { + isci_remote_device_test_resume_done(ihost, idev), + msecs_to_jiffies(MAX_RESUME_MSECS))) { - dev_warn(scirdev_to_dev(idev), "%s: #### Timeout waiting for " + dev_warn(&ihost->pdev->dev, "%s: #### Timeout waiting for " "resume: %p\n", __func__, idev); } clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); - dev_dbg(scirdev_to_dev(idev), "%s: resume wait done: %p\n", + dev_dbg(&ihost->pdev->dev, "%s: resume wait done: %p\n", __func__, idev); } @@ -1414,7 +1427,7 @@ enum sci_status isci_remote_device_resume_from_abort( idev, isci_remote_device_resume_from_abort_complete, idev); spin_unlock_irqrestore(&ihost->scic_lock, flags); - if (!destroyed) + if (!destroyed && (status == SCI_SUCCESS)) isci_remote_device_wait_for_resume_from_abort(ihost, idev); else clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c index f5792a901e0..1910100638a 100644 --- a/drivers/scsi/isci/remote_node_context.c +++ b/drivers/scsi/isci/remote_node_context.c @@ -190,9 +190,13 @@ static void sci_remote_node_context_setup_to_destroy( scics_sds_remote_node_context_callback callback, void *callback_parameter) { + struct isci_host *ihost = idev_to_ihost(rnc_to_dev(sci_rnc)); + sci_rnc->destination_state = RNC_DEST_FINAL; sci_rnc->user_callback = callback; sci_rnc->user_cookie = callback_parameter; + + wake_up(&ihost->eventq); } /** diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h index 0d4a24d647b..a703b9ce0c2 100644 --- a/drivers/scsi/isci/remote_node_context.h +++ b/drivers/scsi/isci/remote_node_context.h @@ -229,8 +229,8 @@ int sci_remote_node_context_is_safe_to_abort( static inline bool sci_remote_node_context_is_being_destroyed( struct sci_remote_node_context *sci_rnc) { - return ((sci_rnc->sm.current_state_id == SCI_RNC_INVALIDATING) - && (sci_rnc->destination_state == RNC_DEST_FINAL)) - || (sci_rnc->sm.current_state_id == SCI_RNC_INITIAL); + return (sci_rnc->destination_state == RNC_DEST_FINAL) + || ((sci_rnc->sm.current_state_id == SCI_RNC_INITIAL) + && (sci_rnc->destination_state == RNC_DEST_UNSPECIFIED)); } #endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ -- cgit v1.2.3-70-g09d2