Commit 5faf50e9 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Martin K. Petersen

scsi: scsi_dh_alua: Avoid crash during alua_bus_detach()

alua_bus_detach() might be running concurrently with alua_rtpg_work(), so
we might trip over h->sdev == NULL and call BUG_ON().  The correct way of
handling it is to not set h->sdev to NULL in alua_bus_detach(), and call
rcu_synchronize() before the final delete to ensure that all concurrent
threads have left the critical section.  Then we can get rid of the
BUG_ON() and replace it with a simple if condition.

Link: https://lore.kernel.org/r/1600167537-12509-1-git-send-email-jitendra.khasdev@oracle.com
Link: https://lore.kernel.org/r/20200924104559.26753-1-hare@suse.de
Cc: Brian Bunker <brian@purestorage.com>
Acked-by: default avatarBrian Bunker <brian@purestorage.com>
Tested-by: default avatarJitendra Khasdev <jitendra.khasdev@oracle.com>
Reviewed-by: default avatarJitendra Khasdev <jitendra.khasdev@oracle.com>
Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent af61bc1e
...@@ -658,8 +658,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) ...@@ -658,8 +658,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(h, list_for_each_entry_rcu(h,
&tmp_pg->dh_list, node) { &tmp_pg->dh_list, node) {
/* h->sdev should always be valid */ if (!h->sdev)
BUG_ON(!h->sdev); continue;
h->sdev->access_state = desc[0]; h->sdev->access_state = desc[0];
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -705,7 +705,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) ...@@ -705,7 +705,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
pg->expiry = 0; pg->expiry = 0;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(h, &pg->dh_list, node) { list_for_each_entry_rcu(h, &pg->dh_list, node) {
BUG_ON(!h->sdev); if (!h->sdev)
continue;
h->sdev->access_state = h->sdev->access_state =
(pg->state & SCSI_ACCESS_STATE_MASK); (pg->state & SCSI_ACCESS_STATE_MASK);
if (pg->pref) if (pg->pref)
...@@ -1147,7 +1148,6 @@ static void alua_bus_detach(struct scsi_device *sdev) ...@@ -1147,7 +1148,6 @@ static void alua_bus_detach(struct scsi_device *sdev)
spin_lock(&h->pg_lock); spin_lock(&h->pg_lock);
pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)); pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
rcu_assign_pointer(h->pg, NULL); rcu_assign_pointer(h->pg, NULL);
h->sdev = NULL;
spin_unlock(&h->pg_lock); spin_unlock(&h->pg_lock);
if (pg) { if (pg) {
spin_lock_irq(&pg->lock); spin_lock_irq(&pg->lock);
...@@ -1156,6 +1156,7 @@ static void alua_bus_detach(struct scsi_device *sdev) ...@@ -1156,6 +1156,7 @@ static void alua_bus_detach(struct scsi_device *sdev)
kref_put(&pg->kref, release_port_group); kref_put(&pg->kref, release_port_group);
} }
sdev->handler_data = NULL; sdev->handler_data = NULL;
synchronize_rcu();
kfree(h); kfree(h);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment