Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 509e215d authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Greg Kroah-Hartman
Browse files

scsi: scsi_dh_alua: Avoid crash during alua_bus_detach()

[ Upstream commit 5faf50e9e9fdc2117c61ff7e20da49cd6a29e0ca ]

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>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 319261de
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -672,8 +672,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
					rcu_read_lock();
					list_for_each_entry_rcu(h,
						&tmp_pg->dh_list, node) {
						/* h->sdev should always be valid */
						BUG_ON(!h->sdev);
						if (!h->sdev)
							continue;
						h->sdev->access_state = desc[0];
					}
					rcu_read_unlock();
@@ -719,7 +719,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
			pg->expiry = 0;
			rcu_read_lock();
			list_for_each_entry_rcu(h, &pg->dh_list, node) {
				BUG_ON(!h->sdev);
				if (!h->sdev)
					continue;
				h->sdev->access_state =
					(pg->state & SCSI_ACCESS_STATE_MASK);
				if (pg->pref)
@@ -1160,7 +1161,6 @@ static void alua_bus_detach(struct scsi_device *sdev)
	spin_lock(&h->pg_lock);
	pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
	rcu_assign_pointer(h->pg, NULL);
	h->sdev = NULL;
	spin_unlock(&h->pg_lock);
	if (pg) {
		spin_lock_irq(&pg->lock);
@@ -1169,6 +1169,7 @@ static void alua_bus_detach(struct scsi_device *sdev)
		kref_put(&pg->kref, release_port_group);
	}
	sdev->handler_data = NULL;
	synchronize_rcu();
	kfree(h);
}