fc_disc_stop() calls fc_disc_stop_rports(), which requires the
disc_mutex to be held.
And we need to ensure that no fc_disc_timeout functions are queued
after this function, so set the callback to NULL and ensure that
all functions terminate early when the callback is not set.

Signed-off-by: Hannes Reinecke <h...@suse.com>
---
 drivers/scsi/libfc/fc_disc.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 82b1c97..109174f 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -283,6 +283,7 @@ static void fc_disc_done(struct fc_disc *disc, enum 
fc_disc_event event)
 {
        struct fc_lport *lport = fc_disc_lport(disc);
        struct fc_rport_priv *rdata;
+       void (*callback)(struct fc_lport *, enum fc_disc_event);
 
        FC_DISC_DBG(disc, "Discovery complete\n");
 
@@ -311,8 +312,10 @@ static void fc_disc_done(struct fc_disc *disc, enum 
fc_disc_event event)
                kref_put(&rdata->kref, fc_rport_destroy);
        }
        rcu_read_unlock();
+       callback = disc->disc_callback;
        mutex_unlock(&disc->disc_mutex);
-       disc->disc_callback(lport, event);
+       if (callback)
+               callback(lport, event);
        mutex_lock(&disc->disc_mutex);
 }
 
@@ -711,9 +714,13 @@ static void fc_disc_stop(struct fc_lport *lport)
 {
        struct fc_disc *disc = &lport->disc;
 
-       if (disc->pending)
-               cancel_delayed_work_sync(&disc->disc_work);
+       mutex_lock(&disc->disc_mutex);
+       disc->disc_callback = NULL;
+       mutex_unlock(&disc->disc_mutex);
+       cancel_delayed_work_sync(&disc->disc_work);
+       mutex_lock(&disc->disc_mutex);
        fc_disc_stop_rports(disc);
+       mutex_unlock(&disc->disc_mutex);
 }
 
 /**
-- 
1.8.5.6

Reply via email to