From 3bd6f43f5cb3714f70c591514f344389df593501 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 4 Dec 2017 10:06:23 -0800 Subject: scsi: core: Ensure that the SCSI error handler gets woken up If scsi_eh_scmd_add() is called concurrently with scsi_host_queue_ready() while shost->host_blocked > 0 then it can happen that neither function wakes up the SCSI error handler. Fix this by making every function that decreases the host_busy counter wake up the error handler if necessary and by protecting the host_failed checks with the SCSI host lock. Reported-by: Pavel Tikhomirov References: https://marc.info/?l=linux-kernel&m=150461610630736 Fixes: commit 746650160866 ("scsi: convert host_busy to atomic_t") Signed-off-by: Bart Van Assche Reviewed-by: Pavel Tikhomirov Tested-by: Stuart Hayes Cc: Konstantin Khorenko Cc: Stuart Hayes Cc: Pavel Tikhomirov Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Cc: Signed-off-by: Martin K. Petersen --- include/scsi/scsi_host.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/scsi') diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index a8b7bf879ced..1a1df0d21ee3 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -571,6 +571,8 @@ struct Scsi_Host { struct blk_mq_tag_set tag_set; }; + struct rcu_head rcu; + atomic_t host_busy; /* commands actually active on low-level */ atomic_t host_blocked; -- cgit v1.2.3 From e4c9470b9f21a0ee3b4625f6d29792c240b71c67 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Thu, 7 Dec 2017 15:59:31 -0800 Subject: scsi: core: Unexport scsi_initialize_rq() Commit 651a01364994 ("scsi: scsi_transport_sas: switch to bsg-lib for SMP passthrough") removed the only call to scsi_initialize_rq() from outside the SCSI core. Hence unexport scsi_initialize_rq(). Signed-off-by: Bart Van Assche Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 3 +-- include/scsi/scsi_cmnd.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 1827956b33c9..c3fc4353af3c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1145,7 +1145,7 @@ EXPORT_SYMBOL(scsi_init_io); * Called from inside blk_get_request() for pass-through requests and from * inside scsi_init_command() for filesystem requests. */ -void scsi_initialize_rq(struct request *rq) +static void scsi_initialize_rq(struct request *rq) { struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); @@ -1153,7 +1153,6 @@ void scsi_initialize_rq(struct request *rq) cmd->jiffies_at_alloc = jiffies; cmd->retries = 0; } -EXPORT_SYMBOL(scsi_initialize_rq); /* Add a command to the list used by the aacraid and dpt_i2o drivers */ void scsi_add_cmd_to_list(struct scsi_cmnd *cmd) diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 7fb57e905526..949a016dd7fa 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -171,7 +171,6 @@ extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, extern void scsi_kunmap_atomic_sg(void *virt); extern int scsi_init_io(struct scsi_cmnd *cmd); -extern void scsi_initialize_rq(struct request *rq); extern int scsi_dma_map(struct scsi_cmnd *cmd); extern void scsi_dma_unmap(struct scsi_cmnd *cmd); -- cgit v1.2.3 From cc019a5a3b58670efe765f19aec42e28c16d7aed Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 21 Dec 2017 14:25:52 -0800 Subject: scsi: scsi_transport_fc: fix typos on 64/128 GBit define names The define names specified 64Bit/128Bit, not 64GBIT/128GBIT. Correct the names. Signed-off-by: James Smart Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_fc.c | 4 ++-- include/scsi/scsi_transport_fc.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 4664024bd5d3..be3be0f9cb2d 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -267,8 +267,8 @@ static const struct { { FC_PORTSPEED_50GBIT, "50 Gbit" }, { FC_PORTSPEED_100GBIT, "100 Gbit" }, { FC_PORTSPEED_25GBIT, "25 Gbit" }, - { FC_PORTSPEED_64BIT, "64 Gbit" }, - { FC_PORTSPEED_128BIT, "128 Gbit" }, + { FC_PORTSPEED_64GBIT, "64 Gbit" }, + { FC_PORTSPEED_128GBIT, "128 Gbit" }, { FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" }, }; fc_bitfield_name_search(port_speed, fc_port_speed_names) diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 8cf30215c177..15da45dc2a5d 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -139,8 +139,8 @@ enum fc_vport_state { #define FC_PORTSPEED_50GBIT 0x200 #define FC_PORTSPEED_100GBIT 0x400 #define FC_PORTSPEED_25GBIT 0x800 -#define FC_PORTSPEED_64BIT 0x1000 -#define FC_PORTSPEED_128BIT 0x2000 +#define FC_PORTSPEED_64GBIT 0x1000 +#define FC_PORTSPEED_128GBIT 0x2000 #define FC_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ /* -- cgit v1.2.3 From 1c393b970e0f4070e4376d45f89a2d19a5c895d0 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 8 Dec 2017 17:42:04 +0800 Subject: scsi: libsas: Use dynamic alloced work to avoid sas event lost Now libsas hotplug work is static, every sas event type has its own static work, LLDD driver queues the hotplug work into shost->work_q. If LLDD driver burst posts lots hotplug events to libsas, the hotplug events may pending in the workqueue like shost->work_q new work[PORTE_BYTES_DMAED] --> |[PHYE_LOSS_OF_SIGNAL][PORTE_BYTES_DMAED] -> processing |<-------wait worker to process-------->| In this case, a new PORTE_BYTES_DMAED event coming, libsas try to queue it to shost->work_q, but this work is already pending, so it would be lost. Finally, libsas delete the related sas port and sas devices, but LLDD driver expect libsas add the sas port and devices(last sas event). This patch use dynamic allocated work to avoid this issue. Signed-off-by: Yijing Wang CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Reviewed-by: Hannes Reinecke Signed-off-by: Jason Yan Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_event.c | 74 +++++++++++++++++++++++++++++--------- drivers/scsi/libsas/sas_init.c | 27 ++++++++++++-- drivers/scsi/libsas/sas_internal.h | 6 ++++ drivers/scsi/libsas/sas_phy.c | 44 +++++------------------ drivers/scsi/libsas/sas_port.c | 18 +++++----- include/scsi/libsas.h | 17 +++++---- 6 files changed, 115 insertions(+), 71 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 0bb9eefc08c8..5d7254aa2dd2 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -29,7 +29,8 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { - int rc = 0; + /* it's added to the defer_q when draining so return succeed */ + int rc = 1; if (!test_bit(SAS_HA_REGISTERED, &ha->state)) return 0; @@ -44,19 +45,15 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) return rc; } -static int sas_queue_event(int event, unsigned long *pending, - struct sas_work *work, +static int sas_queue_event(int event, struct sas_work *work, struct sas_ha_struct *ha) { - int rc = 0; + unsigned long flags; + int rc; - if (!test_and_set_bit(event, pending)) { - unsigned long flags; - - spin_lock_irqsave(&ha->lock, flags); - rc = sas_queue_work(ha, work); - spin_unlock_irqrestore(&ha->lock, flags); - } + spin_lock_irqsave(&ha->lock, flags); + rc = sas_queue_work(ha, work); + spin_unlock_irqrestore(&ha->lock, flags); return rc; } @@ -66,6 +63,7 @@ void __sas_drain_work(struct sas_ha_struct *ha) { struct workqueue_struct *wq = ha->core.shost->work_q; struct sas_work *sw, *_sw; + int ret; set_bit(SAS_HA_DRAINING, &ha->state); /* flush submitters */ @@ -78,7 +76,10 @@ void __sas_drain_work(struct sas_ha_struct *ha) clear_bit(SAS_HA_DRAINING, &ha->state); list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { list_del_init(&sw->drain_node); - sas_queue_work(ha, sw); + ret = sas_queue_work(ha, sw); + if (ret != 1) + sas_free_event(to_asd_sas_event(&sw->work)); + } spin_unlock_irq(&ha->lock); } @@ -119,29 +120,68 @@ void sas_enable_revalidation(struct sas_ha_struct *ha) if (!test_and_clear_bit(ev, &d->pending)) continue; - sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); + sas_queue_event(ev, &d->disc_work[ev].work, ha); } mutex_unlock(&ha->disco_mutex); } + +static void sas_port_event_worker(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + + sas_port_event_fns[ev->event](work); + sas_free_event(ev); +} + +static void sas_phy_event_worker(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + + sas_phy_event_fns[ev->event](work); + sas_free_event(ev); +} + static int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event) { + struct asd_sas_event *ev; struct sas_ha_struct *ha = phy->ha; + int ret; BUG_ON(event >= PORT_NUM_EVENTS); - return sas_queue_event(event, &phy->port_events_pending, - &phy->port_events[event].work, ha); + ev = sas_alloc_event(phy); + if (!ev) + return -ENOMEM; + + INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); + + ret = sas_queue_event(event, &ev->work, ha); + if (ret != 1) + sas_free_event(ev); + + return ret; } int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) { + struct asd_sas_event *ev; struct sas_ha_struct *ha = phy->ha; + int ret; BUG_ON(event >= PHY_NUM_EVENTS); - return sas_queue_event(event, &phy->phy_events_pending, - &phy->phy_events[event].work, ha); + ev = sas_alloc_event(phy); + if (!ev) + return -ENOMEM; + + INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event); + + ret = sas_queue_event(event, &ev->work, ha); + if (ret != 1) + sas_free_event(ev); + + return ret; } int sas_init_events(struct sas_ha_struct *sas_ha) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 64fa6f53cb8b..e04f6d6f5aff 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -39,6 +39,7 @@ #include "../scsi_sas_internal.h" static struct kmem_cache *sas_task_cache; +static struct kmem_cache *sas_event_cache; struct sas_task *sas_alloc_task(gfp_t flags) { @@ -364,8 +365,6 @@ void sas_prep_resume_ha(struct sas_ha_struct *ha) struct asd_sas_phy *phy = ha->sas_phy[i]; memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); - phy->port_events_pending = 0; - phy->phy_events_pending = 0; phy->frame_rcvd_size = 0; } } @@ -555,20 +554,42 @@ sas_domain_attach_transport(struct sas_domain_function_template *dft) } EXPORT_SYMBOL_GPL(sas_domain_attach_transport); + +struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy) +{ + gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + + return kmem_cache_zalloc(sas_event_cache, flags); +} + +void sas_free_event(struct asd_sas_event *event) +{ + kmem_cache_free(sas_event_cache, event); +} + /* ---------- SAS Class register/unregister ---------- */ static int __init sas_class_init(void) { sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN); if (!sas_task_cache) - return -ENOMEM; + goto out; + + sas_event_cache = KMEM_CACHE(asd_sas_event, SLAB_HWCACHE_ALIGN); + if (!sas_event_cache) + goto free_task_kmem; return 0; +free_task_kmem: + kmem_cache_destroy(sas_task_cache); +out: + return -ENOMEM; } static void __exit sas_class_exit(void) { kmem_cache_destroy(sas_task_cache); + kmem_cache_destroy(sas_event_cache); } MODULE_AUTHOR("Luben Tuikov "); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index c07e08136491..d8826a747690 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -61,6 +61,9 @@ int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); int sas_register_phys(struct sas_ha_struct *sas_ha); void sas_unregister_phys(struct sas_ha_struct *sas_ha); +struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy); +void sas_free_event(struct asd_sas_event *event); + int sas_register_ports(struct sas_ha_struct *sas_ha); void sas_unregister_ports(struct sas_ha_struct *sas_ha); @@ -99,6 +102,9 @@ void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); +extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS]; +extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS]; + #ifdef CONFIG_SCSI_SAS_HOST_SMP extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost); #else diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index cdee446c29e1..59f82929b0a3 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -35,7 +35,6 @@ static void sas_phye_loss_of_signal(struct work_struct *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); phy->error = 0; sas_deform_port(phy, 1); } @@ -45,7 +44,6 @@ static void sas_phye_oob_done(struct work_struct *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); phy->error = 0; } @@ -58,8 +56,6 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); - sas_deform_port(phy, 1); if (!port && phy->enabled && i->dft->lldd_control_phy) { @@ -88,8 +84,6 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); - clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending); - phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); } @@ -99,8 +93,6 @@ static void sas_phye_resume_timeout(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); - /* phew, lldd got the phy back in the nick of time */ if (!phy->suspended) { dev_info(&phy->phy->dev, "resume timeout cancelled\n"); @@ -119,39 +111,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) { int i; - static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { - [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, - [PHYE_OOB_DONE] = sas_phye_oob_done, - [PHYE_OOB_ERROR] = sas_phye_oob_error, - [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, - [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, - - }; - - static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { - [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, - [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, - [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, - [PORTE_TIMER_EVENT] = sas_porte_timer_event, - [PORTE_HARD_RESET] = sas_porte_hard_reset, - }; - /* Now register the phys. */ for (i = 0; i < sas_ha->num_phys; i++) { - int k; struct asd_sas_phy *phy = sas_ha->sas_phy[i]; phy->error = 0; INIT_LIST_HEAD(&phy->port_phy_el); - for (k = 0; k < PORT_NUM_EVENTS; 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_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); - phy->phy_events[k].phy = phy; - } phy->port = NULL; phy->ha = sas_ha; @@ -179,3 +144,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) return 0; } + +const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { + [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, + [PHYE_OOB_DONE] = sas_phye_oob_done, + [PHYE_OOB_ERROR] = sas_phye_oob_error, + [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, + [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, + +}; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index d3c5297c6c89..93266283f51f 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -261,8 +261,6 @@ void sas_porte_bytes_dmaed(struct work_struct *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); - sas_form_port(phy); } @@ -273,8 +271,6 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) unsigned long flags; u32 prim; - clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending); - spin_lock_irqsave(&phy->sas_prim_lock, flags); prim = phy->sas_prim; spin_unlock_irqrestore(&phy->sas_prim_lock, flags); @@ -288,8 +284,6 @@ void sas_porte_link_reset_err(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); - sas_deform_port(phy, 1); } @@ -298,8 +292,6 @@ void sas_porte_timer_event(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); - sas_deform_port(phy, 1); } @@ -308,8 +300,6 @@ void sas_porte_hard_reset(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); - sas_deform_port(phy, 1); } @@ -353,3 +343,11 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha) sas_deform_port(sas_ha->sas_phy[i], 0); } + +const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { + [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, + [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, + [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, + [PORTE_TIMER_EVENT] = sas_porte_timer_event, + [PORTE_HARD_RESET] = sas_porte_hard_reset, +}; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 0f9cbf96c093..ee1b25299dd6 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -292,6 +292,7 @@ struct asd_sas_port { struct asd_sas_event { struct sas_work work; struct asd_sas_phy *phy; + int event; }; static inline struct asd_sas_event *to_asd_sas_event(struct work_struct *work) @@ -301,17 +302,21 @@ static inline struct asd_sas_event *to_asd_sas_event(struct work_struct *work) return ev; } +static inline void INIT_SAS_EVENT(struct asd_sas_event *ev, + void (*fn)(struct work_struct *), + struct asd_sas_phy *phy, int event) +{ + INIT_SAS_WORK(&ev->work, fn); + ev->phy = phy; + ev->event = event; +} + + /* The phy pretty much is controlled by the LLDD. * The class only reads those fields. */ struct asd_sas_phy { /* private: */ - struct asd_sas_event port_events[PORT_NUM_EVENTS]; - struct asd_sas_event phy_events[PHY_NUM_EVENTS]; - - unsigned long port_events_pending; - unsigned long phy_events_pending; - int error; int suspended; -- cgit v1.2.3 From f12486e06ae87453530f00a6cb49b60ae3fe4551 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 8 Dec 2017 17:42:05 +0800 Subject: scsi: libsas: shut down the PHY if events reached the threshold If the PHY burst too many events, we will alloc a lot of events for the worker. This may leads to memory exhaustion. Dan Williams suggested to shut down the PHY if the events reached the threshold, because in this case the PHY may have gone into some erroneous state. Users can re-enable the PHY by sysfs if they want. We cannot use the fixed memory pool because if we run out of events, the shut down event and loss of signal event will lost too. The events still need to be allocated and processed in this case. Suggested-by: Dan Williams Signed-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_init.c | 33 ++++++++++++++++++++++++++++++++- drivers/scsi/libsas/sas_phy.c | 27 ++++++++++++++++++++++++++- include/scsi/libsas.h | 6 ++++++ 3 files changed, 64 insertions(+), 2 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index e04f6d6f5aff..22bfc025ae81 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -123,6 +123,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) INIT_LIST_HEAD(&sas_ha->defer_q); INIT_LIST_HEAD(&sas_ha->eh_dev_q); + sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES; + error = sas_register_phys(sas_ha); if (error) { printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); @@ -557,14 +559,43 @@ EXPORT_SYMBOL_GPL(sas_domain_attach_transport); struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy) { + struct asd_sas_event *event; gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + event = kmem_cache_zalloc(sas_event_cache, flags); + if (!event) + return NULL; - return kmem_cache_zalloc(sas_event_cache, flags); + atomic_inc(&phy->event_nr); + + if (atomic_read(&phy->event_nr) > phy->ha->event_thres) { + if (i->dft->lldd_control_phy) { + if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) { + sas_printk("The phy%02d bursting events, shut it down.\n", + phy->id); + sas_notify_phy_event(phy, PHYE_SHUTDOWN); + } + } else { + /* Do not support PHY control, stop allocating events */ + WARN_ONCE(1, "PHY control not supported.\n"); + kmem_cache_free(sas_event_cache, event); + atomic_dec(&phy->event_nr); + event = NULL; + } + } + + return event; } void sas_free_event(struct asd_sas_event *event) { + struct asd_sas_phy *phy = event->phy; + kmem_cache_free(sas_event_cache, event); + atomic_dec(&phy->event_nr); } /* ---------- SAS Class register/unregister ---------- */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index 59f82929b0a3..bf3e1b979ca6 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -35,6 +35,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; + phy->in_shutdown = 0; phy->error = 0; sas_deform_port(phy, 1); } @@ -44,6 +45,7 @@ static void sas_phye_oob_done(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; + phy->in_shutdown = 0; phy->error = 0; } @@ -105,6 +107,28 @@ static void sas_phye_resume_timeout(struct work_struct *work) } +static void sas_phye_shutdown(struct work_struct *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 = + to_sas_internal(sas_ha->core.shost->transportt); + + if (phy->enabled) { + int ret; + + phy->error = 0; + phy->enabled = 0; + ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); + if (ret) + sas_printk("lldd disable phy%02d returned %d\n", + phy->id, ret); + } else + sas_printk("phy%02d is not enabled, cannot shutdown\n", + phy->id); +} + /* ---------- Phy class registration ---------- */ int sas_register_phys(struct sas_ha_struct *sas_ha) @@ -116,6 +140,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) struct asd_sas_phy *phy = sas_ha->sas_phy[i]; phy->error = 0; + atomic_set(&phy->event_nr, 0); INIT_LIST_HEAD(&phy->port_phy_el); phy->port = NULL; @@ -151,5 +176,5 @@ const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { [PHYE_OOB_ERROR] = sas_phye_oob_error, [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, - + [PHYE_SHUTDOWN] = sas_phye_shutdown, }; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ee1b25299dd6..de8f043475c2 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -75,6 +75,7 @@ enum phy_event { PHYE_OOB_ERROR, PHYE_SPINUP_HOLD, /* hot plug SATA, no COMWAKE sent */ PHYE_RESUME_TIMEOUT, + PHYE_SHUTDOWN, PHY_NUM_EVENTS, }; @@ -311,12 +312,15 @@ static inline void INIT_SAS_EVENT(struct asd_sas_event *ev, ev->event = event; } +#define SAS_PHY_SHUTDOWN_THRES 1024 /* The phy pretty much is controlled by the LLDD. * The class only reads those fields. */ struct asd_sas_phy { /* private: */ + atomic_t event_nr; + int in_shutdown; int error; int suspended; @@ -404,6 +408,8 @@ struct sas_ha_struct { struct list_head eh_done_q; /* complete via scsi_eh_flush_done_q */ struct list_head eh_ata_q; /* scmds to promote from sas to ata eh */ + + int event_thres; }; #define SHOST_TO_SAS_HA(_shost) (*(struct sas_ha_struct **)(_shost)->hostdata) -- cgit v1.2.3 From 8eea9dd84e450e5262643823691108f2a208a2ac Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 8 Dec 2017 17:42:06 +0800 Subject: scsi: libsas: make the event threshold configurable Add a sysfs attr that LLDD can configure it for every host. We made an example in hisi_sas. Other LLDDs using libsas can implement it if they want. Suggested-by: Hannes Reinecke Signed-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Acked-by: John Garry #for hisi_sas part Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 6 ++++++ drivers/scsi/libsas/sas_init.c | 31 +++++++++++++++++++++++++++++++ include/scsi/libsas.h | 1 + 3 files changed, 38 insertions(+) (limited to 'include/scsi') diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 04e1172b0bc5..819b1d0ded89 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1672,6 +1672,11 @@ EXPORT_SYMBOL_GPL(hisi_sas_kill_tasklets); struct scsi_transport_template *hisi_sas_stt; EXPORT_SYMBOL_GPL(hisi_sas_stt); +struct device_attribute *host_attrs[] = { + &dev_attr_phy_event_threshold, + NULL, +}; + static struct scsi_host_template _hisi_sas_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -1691,6 +1696,7 @@ static struct scsi_host_template _hisi_sas_sht = { .eh_target_reset_handler = sas_eh_target_reset_handler, .target_destroy = sas_target_destroy, .ioctl = sas_ioctl, + .shost_attrs = host_attrs, }; struct scsi_host_template *hisi_sas_sht = &_hisi_sas_sht; EXPORT_SYMBOL_GPL(hisi_sas_sht); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 22bfc025ae81..afd928bf903e 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -538,6 +538,37 @@ static struct sas_function_template sft = { .smp_handler = sas_smp_handler, }; +static inline ssize_t phy_event_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres); +} + +static inline ssize_t phy_event_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + sha->event_thres = simple_strtol(buf, NULL, 10); + + /* threshold cannot be set too small */ + if (sha->event_thres < 32) + sha->event_thres = 32; + + return count; +} + +DEVICE_ATTR(phy_event_threshold, + S_IRUGO|S_IWUSR, + phy_event_threshold_show, + phy_event_threshold_store); +EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold); + struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *dft) { diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index de8f043475c2..3769615d4ce9 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -681,6 +681,7 @@ extern int sas_bios_param(struct scsi_device *, sector_t capacity, int *hsc); extern struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *); +extern struct device_attribute dev_attr_phy_event_threshold; int sas_discover_root_expander(struct domain_device *); -- cgit v1.2.3 From 93bdbd06b1644ac15aa152e91faefed86cc04937 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 8 Dec 2017 17:42:07 +0800 Subject: scsi: libsas: Use new workqueue to run sas event and disco event Now all libsas works are queued to scsi host workqueue, include sas event work post by LLDD and sas discovery work, and a sas hotplug flow may be divided into several works, e.g libsas receive a PORTE_BYTES_DMAED event, currently we process it as following steps: sas_form_port --- run in work in shost workq sas_discover_domain --- run in another work in shost workq ... sas_probe_devices --- run in new work in shost workq We found during hot-add a device, libsas may need run several works in same workqueue to add device in system, the process is not atomic, it may interrupt by other sas event works, like PHYE_LOSS_OF_SIGNAL. This patch is preparation of execute libsas sas event in sync. We need to use different workqueue to run sas event and disco event. Otherwise the work will be blocked for waiting another chained work in the same workqueue. Signed-off-by: Yijing Wang CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Signed-off-by: Jason Yan Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_discover.c | 2 +- drivers/scsi/libsas/sas_event.c | 6 +++--- drivers/scsi/libsas/sas_init.c | 18 ++++++++++++++++++ include/scsi/libsas.h | 3 +++ 4 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 60de66252fa2..14f714d05767 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -534,7 +534,7 @@ static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) * workqueue, or known to be submitted from a context that is * not racing against draining */ - scsi_queue_work(ha->core.shost, &sw->work); + queue_work(ha->disco_q, &sw->work); } static void sas_chain_event(int event, unsigned long *pending, diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 5d7254aa2dd2..8c82c007763d 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -40,7 +40,7 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) if (list_empty(&sw->drain_node)) list_add_tail(&sw->drain_node, &ha->defer_q); } else - rc = scsi_queue_work(ha->core.shost, &sw->work); + rc = queue_work(ha->event_q, &sw->work); return rc; } @@ -61,7 +61,6 @@ static int sas_queue_event(int event, struct sas_work *work, void __sas_drain_work(struct sas_ha_struct *ha) { - struct workqueue_struct *wq = ha->core.shost->work_q; struct sas_work *sw, *_sw; int ret; @@ -70,7 +69,8 @@ void __sas_drain_work(struct sas_ha_struct *ha) spin_lock_irq(&ha->lock); spin_unlock_irq(&ha->lock); - drain_workqueue(wq); + drain_workqueue(ha->event_q); + drain_workqueue(ha->disco_q); spin_lock_irq(&ha->lock); clear_bit(SAS_HA_DRAINING, &ha->state); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index afd928bf903e..c81a63b5dc71 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -110,6 +110,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) int sas_register_ha(struct sas_ha_struct *sas_ha) { + char name[64]; int error = 0; mutex_init(&sas_ha->disco_mutex); @@ -143,10 +144,24 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) goto Undo_ports; } + error = -ENOMEM; + snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev)); + sas_ha->event_q = create_singlethread_workqueue(name); + if (!sas_ha->event_q) + goto Undo_ports; + + snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev)); + sas_ha->disco_q = create_singlethread_workqueue(name); + if (!sas_ha->disco_q) + goto Undo_event_q; + INIT_LIST_HEAD(&sas_ha->eh_done_q); INIT_LIST_HEAD(&sas_ha->eh_ata_q); return 0; + +Undo_event_q: + destroy_workqueue(sas_ha->event_q); Undo_ports: sas_unregister_ports(sas_ha); Undo_phys: @@ -177,6 +192,9 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) __sas_drain_work(sas_ha); mutex_unlock(&sas_ha->drain_mutex); + destroy_workqueue(sas_ha->disco_q); + destroy_workqueue(sas_ha->event_q); + return 0; } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 3769615d4ce9..6255bb5ed1e4 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -389,6 +389,9 @@ struct sas_ha_struct { struct device *dev; /* should be set */ struct module *lldd_module; /* should be set */ + struct workqueue_struct *event_q; + struct workqueue_struct *disco_q; + u8 *sas_addr; /* must be set */ u8 hashed_sas_addr[HASHED_SAS_ADDR_SIZE]; -- cgit v1.2.3 From 0558f33c06bb910e2879e355192227a8e8f0219d Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Fri, 8 Dec 2017 17:42:09 +0800 Subject: scsi: libsas: direct call probe and destruct In commit 87c8331fcf72 ("[SCSI] libsas: prevent domain rediscovery competing with ata error handling") introduced disco mutex to prevent rediscovery competing with ata error handling and put the whole revalidation in the mutex. But the rphy add/remove needs to wait for the error handling which also grabs the disco mutex. This may leads to dead lock.So the probe and destruct event were introduce to do the rphy add/remove asynchronously and out of the lock. The asynchronously processed workers makes the whole discovery process not atomic, the other events may interrupt the process. For example, if a loss of signal event inserted before the probe event, the sas_deform_port() is called and the port will be deleted. And sas_port_delete() may run before the destruct event, but the port-x:x is the top parent of end device or expander. This leads to a kernel WARNING such as: [ 82.042979] sysfs group 'power' not found for kobject 'phy-1:0:22' [ 82.042983] ------------[ cut here ]------------ [ 82.042986] WARNING: CPU: 54 PID: 1714 at fs/sysfs/group.c:237 sysfs_remove_group+0x94/0xa0 [ 82.043059] Call trace: [ 82.043082] [] sysfs_remove_group+0x94/0xa0 [ 82.043085] [] dpm_sysfs_remove+0x60/0x70 [ 82.043086] [] device_del+0x138/0x308 [ 82.043089] [] sas_phy_delete+0x38/0x60 [ 82.043091] [] do_sas_phy_delete+0x6c/0x80 [ 82.043093] [] device_for_each_child+0x58/0xa0 [ 82.043095] [] sas_remove_children+0x40/0x50 [ 82.043100] [] sas_destruct_devices+0x64/0xa0 [ 82.043102] [] process_one_work+0x1fc/0x4b0 [ 82.043104] [] worker_thread+0x50/0x490 [ 82.043105] [] kthread+0xfc/0x128 [ 82.043107] [] ret_from_fork+0x10/0x50 Make probe and destruct a direct call in the disco and revalidate function, but put them outside the lock. The whole discovery or revalidate won't be interrupted by other events. And the DISCE_PROBE and DISCE_DESTRUCT event are deleted as a result of the direct call. Introduce a new list to destruct the sas_port and put the port delete after the destruct. This makes sure the right order of destroying the sysfs kobject and fix the warning above. In sas_ex_revalidate_domain() have a loop to find all broadcasted device, and sometimes we have a chance to find the same expander twice. Because the sas_port will be deleted at the end of the whole revalidate process, sas_port with the same name cannot be added before this. Otherwise the sysfs will complain of creating duplicate filename. Since the LLDD will send broadcast for every device change, we can only process one expander's revalidation. [mkp: kbuild test robot warning] Signed-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/libsas/sas_ata.c | 1 - drivers/scsi/libsas/sas_discover.c | 32 ++++++++++++++++++-------------- drivers/scsi/libsas/sas_expander.c | 8 +++----- drivers/scsi/libsas/sas_internal.h | 1 + drivers/scsi/libsas/sas_port.c | 3 +++ include/scsi/libsas.h | 3 +-- include/scsi/scsi_transport_sas.h | 1 + 7 files changed, 27 insertions(+), 22 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 70be4425ae0b..2b3637b40dde 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -730,7 +730,6 @@ int sas_discover_sata(struct domain_device *dev) if (res) return res; - sas_discover_event(dev->port, DISCE_PROBE); return 0; } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 14f714d05767..e4fd078e4175 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -212,13 +212,9 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) } } -static void sas_probe_devices(struct work_struct *work) +static void sas_probe_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_PROBE, &port->disc.pending); /* devices must be domain members before link recovery and probe */ list_for_each_entry(dev, &port->disco_list, disco_list_node) { @@ -294,7 +290,6 @@ int sas_discover_end_dev(struct domain_device *dev) res = sas_notify_lldd_dev_found(dev); if (res) return res; - sas_discover_event(dev->port, DISCE_PROBE); return 0; } @@ -353,13 +348,9 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d sas_put_device(dev); } -static void sas_destruct_devices(struct work_struct *work) +void sas_destruct_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_DESTRUCT, &port->disc.pending); list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { list_del_init(&dev->disco_list_node); @@ -370,6 +361,16 @@ static void sas_destruct_devices(struct work_struct *work) } } +static void sas_destruct_ports(struct asd_sas_port *port) +{ + struct sas_port *sas_port, *p; + + list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) { + list_del_init(&sas_port->del_list); + sas_port_delete(sas_port); + } +} + void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (!test_bit(SAS_DEV_DESTROY, &dev->state) && @@ -384,7 +385,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) 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); } } @@ -490,6 +490,8 @@ static void sas_discover_domain(struct work_struct *work) port->port_dev = NULL; } + sas_probe_devices(port); + SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id, task_pid_nr(current), error); } @@ -523,6 +525,10 @@ static void sas_revalidate_domain(struct work_struct *work) port->id, task_pid_nr(current), res); out: mutex_unlock(&ha->disco_mutex); + + sas_destruct_devices(port); + sas_destruct_ports(port); + sas_probe_devices(port); } /* ---------- Events ---------- */ @@ -578,10 +584,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, - [DISCE_PROBE] = sas_probe_devices, [DISCE_SUSPEND] = sas_suspend_devices, [DISCE_RESUME] = sas_resume_devices, - [DISCE_DESTRUCT] = sas_destruct_devices, }; disc->pending = 0; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index a8a57b0593e3..7444d40e261c 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1916,7 +1916,8 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, sas_port_delete_phy(phy->port, phy->phy); sas_device_set_phy(found, phy->port); if (phy->port->num_phys == 0) - sas_port_delete(phy->port); + list_add_tail(&phy->port->del_list, + &parent->port->sas_port_del_list); phy->port = NULL; } } @@ -2124,7 +2125,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) struct domain_device *dev = NULL; res = sas_find_bcast_dev(port_dev, &dev); - while (res == 0 && dev) { + if (res == 0 && dev) { struct expander_device *ex = &dev->ex_dev; int i = 0, phy_id; @@ -2136,9 +2137,6 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) res = sas_rediscover(dev, phy_id); i = phy_id + 1; } while (i < ex->num_phys); - - dev = NULL; - res = sas_find_bcast_dev(port_dev, &dev); } return res; } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index d8826a747690..50e12d662ffe 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -101,6 +101,7 @@ int sas_try_ata_reset(struct asd_sas_phy *phy); void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); +void sas_destruct_devices(struct asd_sas_port *port); extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS]; extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS]; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 64722f42b256..f07e55d3aa73 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -66,6 +66,7 @@ static void sas_resume_port(struct asd_sas_phy *phy) rc = sas_notify_lldd_dev_found(dev); if (rc) { sas_unregister_dev(port, dev); + sas_destruct_devices(port); continue; } @@ -220,6 +221,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) if (port->num_phys == 1) { sas_unregister_domain_devices(port, gone); + sas_destruct_devices(port); sas_port_delete(port->port); port->port = NULL; } else { @@ -317,6 +319,7 @@ static void sas_init_port(struct asd_sas_port *port, INIT_LIST_HEAD(&port->dev_list); INIT_LIST_HEAD(&port->disco_list); INIT_LIST_HEAD(&port->destroy_list); + INIT_LIST_HEAD(&port->sas_port_del_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); port->ha = sas_ha; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 6255bb5ed1e4..1cab6f7af425 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -82,10 +82,8 @@ enum phy_event { enum discover_event { DISCE_DISCOVER_DOMAIN = 0U, DISCE_REVALIDATE_DOMAIN, - DISCE_PROBE, DISCE_SUSPEND, DISCE_RESUME, - DISCE_DESTRUCT, DISC_NUM_EVENTS, }; @@ -262,6 +260,7 @@ struct asd_sas_port { struct list_head dev_list; struct list_head disco_list; struct list_head destroy_list; + struct list_head sas_port_del_list; enum sas_linkrate linkrate; struct sas_work work; diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 62895b405933..05ec927a3c72 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -156,6 +156,7 @@ struct sas_port { struct mutex phy_list_mutex; struct list_head phy_list; + struct list_head del_list; /* libsas only */ }; #define dev_to_sas_port(d) \ -- cgit v1.2.3 From 9c661a49e4318f11934115adb4c5a91e15efc0cc Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 19 Jan 2018 16:11:10 +0100 Subject: scsi: core: Add VENDOR_SPECIFIC sense code definitions Some older devices will return vendor specific sense codes, so we should be adding a definition for it. Signed-off-by: Hannes Reinecke Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- include/scsi/scsi_proto.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/scsi') diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index 1df8efb0ee01..c36860111932 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -236,6 +236,7 @@ struct scsi_varlen_cdb_hdr { #define UNIT_ATTENTION 0x06 #define DATA_PROTECT 0x07 #define BLANK_CHECK 0x08 +#define VENDOR_SPECIFIC 0x09 #define COPY_ABORTED 0x0a #define ABORTED_COMMAND 0x0b #define VOLUME_OVERFLOW 0x0d -- cgit v1.2.3