Modify alua_rtpg_queue() such that it only requests the caller to drop the sdev reference if necessary. This patch fixes a recently introduced regression. Cc: Sachin Sant <sachinp@xxxxxxxxxxxxx> Cc: Hannes Reinecke <hare@xxxxxxx> Cc: Martin Wilck <mwilck@xxxxxxxx> Reported-by: Sachin Sant <sachinp@xxxxxxxxxxxxx> Fixes: 0b25e17e9018 ("scsi: alua: Move a scsi_device_put() call out of alua_check_vpd()") Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx> --- drivers/scsi/device_handler/scsi_dh_alua.c | 31 ++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 693cd827e138..c1bf75673e89 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -102,7 +102,7 @@ struct alua_queue_data { static void alua_rtpg_work(struct work_struct *work); static bool alua_rtpg_queue(struct alua_port_group *pg, - struct scsi_device *sdev, + struct scsi_device *sdev, bool *put_sdev, struct alua_queue_data *qdata, bool force); static void alua_check(struct scsi_device *sdev, bool force); @@ -374,9 +374,9 @@ 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); - put_sdev = alua_rtpg_queue(rcu_dereference_protected(h->pg, + alua_rtpg_queue(rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)), - sdev, NULL, true); + sdev, &put_sdev, NULL, true); spin_unlock(&h->pg_lock); if (put_sdev) @@ -983,17 +983,23 @@ static void alua_rtpg_work(struct work_struct *work) * * Returns true if and only if alua_rtpg_work() will be called asynchronously. * That function is responsible for calling @qdata->fn(). If this function - * returns true, the caller is responsible for invoking scsi_device_put(@sdev). + * sets *put_sdev to true, the caller is responsible for calling + * scsi_device_put(@sdev). */ -static bool __must_check alua_rtpg_queue(struct alua_port_group *pg, - struct scsi_device *sdev, +static bool alua_rtpg_queue(struct alua_port_group *pg, + struct scsi_device *sdev, bool *put_sdev, struct alua_queue_data *qdata, bool force) { int start_queue = 0; unsigned long flags; + + *put_sdev = false; + if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev)) return false; + *put_sdev = true; + spin_lock_irqsave(&pg->lock, flags); if (qdata) { list_add_tail(&qdata->entry, &pg->rtpg_list); @@ -1020,7 +1026,7 @@ static bool __must_check alua_rtpg_queue(struct alua_port_group *pg, if (start_queue) { if (queue_delayed_work(kaluad_wq, &pg->rtpg_work, msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS))) - sdev = NULL; + *put_sdev = false; else kref_put(&pg->kref, release_port_group); } @@ -1108,6 +1114,7 @@ static int alua_activate(struct scsi_device *sdev, int err = SCSI_DH_OK; struct alua_queue_data *qdata; struct alua_port_group *pg; + bool put_sdev; qdata = kzalloc(sizeof(*qdata), GFP_KERNEL); if (!qdata) { @@ -1130,8 +1137,9 @@ static int alua_activate(struct scsi_device *sdev, rcu_read_unlock(); mutex_unlock(&h->init_mutex); - if (alua_rtpg_queue(pg, sdev, qdata, true)) { - scsi_device_put(sdev); + if (alua_rtpg_queue(pg, sdev, &put_sdev, qdata, true)) { + if (put_sdev) + scsi_device_put(sdev); fn = NULL; } else { err = SCSI_DH_DEV_OFFLINED; @@ -1153,6 +1161,7 @@ static void alua_check(struct scsi_device *sdev, bool force) { struct alua_dh_data *h = sdev->handler_data; struct alua_port_group *pg; + bool put_sdev; rcu_read_lock(); pg = rcu_dereference(h->pg); @@ -1161,8 +1170,8 @@ static void alua_check(struct scsi_device *sdev, bool force) return; } rcu_read_unlock(); - - if (alua_rtpg_queue(pg, sdev, NULL, force)) + alua_rtpg_queue(pg, sdev, &put_sdev, NULL, force); + if (put_sdev) scsi_device_put(sdev); kref_put(&pg->kref, release_port_group); }