[PATCH 3/3] libfc: fixup locking in fc_disc_stop()

Hannes Reinecke
2017-05-30T15:14:38Z

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 <hare@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