--- /dev/null
+From: Robert Love <robert.w.love@intel.com>
+Subject: libfc: Ensure correct device_put/get usage (round 2)
+References:
+
+Reference counting was barely used and where used
+it was incorrect. This patch creates a few simple
+policies.
+
+When the rport->dev [e.g. struct device] is initialized
+it starts with a refcnt of 1. Whenever we're using the
+rport we will increment the count. When we logoff we
+should decrement the count to 0 and the 'release'
+function will be called. The FC transport provides the
+release function for real rports and libfc provides it
+for rogue rports. When we switch from a rogue to real
+rport we'll decrement the refcnt on the rogue rport
+and increment it for the real rport, after we've created
+it.
+
+Any externally initiated action on an rport (login,
+logoff) will not require the caller to increment and
+decrement the refcnt.
+
+For rport_login(), the rport will have just been created
+and therefore no other thread would be able to access
+this object.
+
+For rport_logoff(), the rport will have been removed
+from the list of rports and therefore no other thread
+would be able to lookup() this rport.
+
+This patch removes the get_device() from the rport_lookup
+function. These are the places where it is called and why
+we don't need a reference.
+
+fc_disc_recv_rscn_req() - called for single port RSCNs
+ the disc mutex is held and
+ ensures that no other thread
+ will find this rport.
+
+fc_disc_new_target() - Same. The rport cannot be looked up
+ so no other thread can free the rport.
+ This code looks buggy though, we
+ shouldn't be calling rport_login() on
+ a 'real' rport, which we could do.
+
+fc_disc_single() - Same. disc mutex protects the list.
+
+fc_lport_recv_req() - Similar, but this time the lport lock
+ ensures that no incoming requests are
+ processed until the current request
+ for an rport has returned.
+
+When the rport layer needs to send a request it will
+increment the count so that the EM can be confident that
+the rport is present when making the callback. If
+fc_remote_port_delete() is called before the response
+callback, which is often the case for LOGO commands, the
+refcnt will still have a value of 1 becuase we grabbed the
+lock before the ctels_send() is called. The exchange would
+have been removed and so the callback will be called with
+an error code. After processing the error code we'll
+decrement the refcnt for the last time and the rport will
+be free'd.
+
+Since point-to-point mode is not working this patch
+does not consider point-to-point.
+
+Signed-off-by: Robert Love <robert.w.love@intel.com>
+Acked-by: Bernhard Walle <bwalle@suse.de>
+---
+
+ drivers/scsi/libfc/fc_disc.c | 5 +----
+ drivers/scsi/libfc/fc_lport.c | 5 ++---
+ drivers/scsi/libfc/fc_rport.c | 21 ++++++++++++++-------
+ 3 files changed, 17 insertions(+), 14 deletions(-)
+
+
+--- a/drivers/scsi/libfc/fc_disc.c
++++ b/drivers/scsi/libfc/fc_disc.c
+@@ -81,7 +81,6 @@ struct fc_rport *fc_disc_lookup_rport(co
+ if (rport->port_id == port_id) {
+ disc_found = 1;
+ found = rport;
+- get_device(&found->dev);
+ break;
+ }
+ }
+@@ -767,10 +766,8 @@ static void fc_disc_single(struct fc_dis
+ goto out;
+
+ rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+- if (rport) {
++ if (rport)
+ fc_disc_del_target(disc, rport);
+- put_device(&rport->dev); /* hold from lookup */
+- }
+
+ new_rport = fc_rport_rogue_create(dp);
+ if (new_rport) {
+--- a/drivers/scsi/libfc/fc_lport.c
++++ b/drivers/scsi/libfc/fc_lport.c
+@@ -908,10 +908,9 @@ static void fc_lport_recv_req(struct fc_
+ d_id = ntoh24(fh->fh_d_id);
+
+ rport = lport->tt.rport_lookup(lport, s_id);
+- if (rport) {
++ if (rport)
+ lport->tt.rport_recv_req(sp, fp, rport);
+- put_device(&rport->dev); /* hold from lookup */
+- } else {
++ else {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_NONE;
+--- a/drivers/scsi/libfc/fc_rport.c
++++ b/drivers/scsi/libfc/fc_rport.c
+@@ -111,16 +111,11 @@ struct fc_rport *fc_rport_rogue_create(s
+ rport->roles = dp->ids.roles;
+ rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ /*
+- * init the device, so other code can manipulate the rport as if
+- * it came from the fc class. We also do an extra get because
+- * libfc will free this rport instead of relying on the normal
+- * refcounting.
+- *
+ * Note: all this libfc rogue rport code will be removed for
+ * upstream so it fine that this is really ugly and hacky right now.
+ */
+ device_initialize(&rport->dev);
+- get_device(&rport->dev);
++ rport->dev.release = fc_rport_rogue_destroy; // XXX: bwalle
+
+ mutex_init(&rdata->rp_mutex);
+ rdata->local_port = dp->lp;
+@@ -402,9 +397,9 @@ static void fc_rport_timeout(struct work
+ case RPORT_ST_NONE:
+ break;
+ }
+- put_device(&rport->dev);
+
+ mutex_unlock(&rdata->rp_mutex);
++ put_device(&rport->dev);
+ }
+
+ /**
+@@ -531,6 +526,7 @@ out:
+ fc_frame_free(fp);
+ err:
+ mutex_unlock(&rdata->rp_mutex);
++ put_device(&rport->dev);
+ }
+
+ /**
+@@ -562,6 +558,8 @@ static void fc_rport_enter_plogi(struct
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
++ else
++ get_device(&rport->dev);
+ }
+
+ /**
+@@ -631,6 +629,7 @@ out:
+ fc_frame_free(fp);
+ err:
+ mutex_unlock(&rdata->rp_mutex);
++ put_device(&rport->dev);
+ }
+
+ /**
+@@ -679,6 +678,7 @@ out:
+ fc_frame_free(fp);
+ err:
+ mutex_unlock(&rdata->rp_mutex);
++ put_device(&rport->dev);
+ }
+
+ /**
+@@ -712,6 +712,8 @@ static void fc_rport_enter_prli(struct f
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
+ fc_rport_prli_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
++ else
++ get_device(&rport->dev);
+ }
+
+ /**
+@@ -777,6 +779,7 @@ out:
+ fc_frame_free(fp);
+ err:
+ mutex_unlock(&rdata->rp_mutex);
++ put_device(&rport->dev);
+ }
+
+ /**
+@@ -806,6 +809,8 @@ static void fc_rport_enter_rtv(struct fc
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
+ fc_rport_rtv_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
++ else
++ get_device(&rport->dev);
+ }
+
+ /**
+@@ -835,6 +840,8 @@ static void fc_rport_enter_logo(struct f
+ if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
+ fc_rport_logo_resp, rport, lport->e_d_tov))
+ fc_rport_error(rport, fp);
++ else
++ get_device(&rport->dev);
+ }
+
+