Skip to content

Commit

Permalink
scsi: device_handler: alua: Call scsi_device_put() from non-atomic co…
Browse files Browse the repository at this point in the history
…ntext

Since commit f93ed74 ("scsi: core: Release SCSI devices
synchronously"), scsi_device_put() might sleep. Avoid calling it from
alua_rtpg_queue() with the pg_lock held. The lock only pretects h->pg,
anyway. To avoid the pg being freed under us, because of a race with
another thread, take a temporary reference. In alua_rtpg_queue(), verify
that the pg still belongs to the sdev being passed before actually queueing
the RTPG.

This patch fixes the following smatch warning:

drivers/scsi/device_handler/scsi_dh_alua.c:1013 alua_rtpg_queue() warn: sleeping in atomic context

alua_check_vpd() <- disables preempt
-> alua_rtpg_queue()
   -> scsi_device_put()

Cc: Martin Wilck <[email protected]>
Cc: Hannes Reinecke <[email protected]>
Cc: Sachin Sant <[email protected]>
Cc: Benjamin Block <[email protected]>
Suggested-by: Martin Wilck <[email protected]>
Reported-by: Dan Carpenter <[email protected]>
Signed-off-by: Bart Van Assche <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Tested-by: Sachin Sant <[email protected]>
Signed-off-by: Martin K. Petersen <[email protected]>
  • Loading branch information
bvanassche authored and martinkpetersen committed Nov 26, 2022
1 parent a500c4c commit 50759b8
Showing 1 changed file with 19 additions and 8 deletions.
27 changes: 19 additions & 8 deletions drivers/scsi/device_handler/scsi_dh_alua.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h,
"%s: port group %x rel port %x\n",
ALUA_DH_NAME, group_id, rel_port);

kref_get(&pg->kref);

/* Check for existing port group references */
spin_lock(&h->pg_lock);
old_pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock));
Expand All @@ -373,11 +375,11 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h,
list_add_rcu(&h->node, &pg->dh_list);
spin_unlock_irqrestore(&pg->lock, flags);

alua_rtpg_queue(rcu_dereference_protected(h->pg,
lockdep_is_held(&h->pg_lock)),
sdev, NULL, true);
spin_unlock(&h->pg_lock);

alua_rtpg_queue(pg, sdev, NULL, true);
kref_put(&pg->kref, release_port_group);

if (old_pg)
kref_put(&old_pg->kref, release_port_group);

Expand Down Expand Up @@ -986,6 +988,9 @@ static bool alua_rtpg_queue(struct alua_port_group *pg,
{
int start_queue = 0;
unsigned long flags;

might_sleep();

if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev))
return false;

Expand All @@ -996,11 +1001,17 @@ static bool alua_rtpg_queue(struct alua_port_group *pg,
force = true;
}
if (pg->rtpg_sdev == NULL) {
pg->interval = 0;
pg->flags |= ALUA_PG_RUN_RTPG;
kref_get(&pg->kref);
pg->rtpg_sdev = sdev;
start_queue = 1;
struct alua_dh_data *h = sdev->handler_data;

rcu_read_lock();
if (h && rcu_dereference(h->pg) == pg) {
pg->interval = 0;
pg->flags |= ALUA_PG_RUN_RTPG;
kref_get(&pg->kref);
pg->rtpg_sdev = sdev;
start_queue = 1;
}
rcu_read_unlock();
} else if (!(pg->flags & ALUA_PG_RUN_RTPG) && force) {
pg->flags |= ALUA_PG_RUN_RTPG;
/* Do not queue if the worker is already running */
Expand Down

0 comments on commit 50759b8

Please sign in to comment.