[Open-FCoE] [RFC PATCH 13/15] libfc: re-work fc_rport.c locking

Robert Love robert.w.love at intel.com
Tue Sep 2 22:38:01 UTC 2008


1) removes fc_rport_lock/unlock and replaces with spin_lock_irqsave/restore
2) re-implements rport locking using the following scheme

Action Funtions (called without lock, will lock, call enter_* function and unlock)
 fc_rport_gpn_id_resp
 fc_rport_gnn_id_resp
 fc_rport_plogi_resp
 fc_rport_prli_resp
 fc_rport_rtv_resp
 fc_rport_logo_resp

 fc_rport_login
 fc_rport_logout
 fc_rport_reset
 fc_rport_timeout

 fc_rport_recv_req
        - Calls one of the request handlers with the lock held

Request Handlers (need lock held before calling)
 fc_rport_recv_plogi_req
 fc_rport_recv_prli_req
 fc_rport_recv_prlo_req
 fc_rport_recv_logo_req

Enter Funtions (need lock held before calling)
 fc_rport_enter_gpn_id
 fc_rport_enter_gnn_id
 fc_rport_enter_plogi
 fc_rport_enter_prli
 fc_rport_enter_rtv
 fc_rport_enter_logo

Helpers (Lock required)
 fc_rport_state_enter
 - Changing the state so the lock must be held
 fc_rport_error
        - Always called by an action or enter function

Helpers (Lock not required)
 fc_rport_create_dummy
 fc_rport_destroy_dummy
 fc_rport_lookup
 fc_remote_port_create
 fc_rport_lock
 fc_rport_unlock
 fc_plogi_get_maxframe
 fc_lport_plogi_fill
 fc_rport_init
 fc_rport_reset_list

Unknown
 fc_rport_ns_error
        - Merge with fc_rport_error
 fc_rport_retry
        - Merge with fc_rport_error

Signed-off-by: Robert Love <robert.w.love at intel.com>
---

 drivers/scsi/libfc/fc_rport.c |  856 +++++++++++++++++++++--------------------
 include/scsi/libfc/libfc.h    |    6 
 2 files changed, 445 insertions(+), 417 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 27f485a..159c365 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -68,7 +68,6 @@ static const char *fc_rport_state_names[] = {
 	[RPORT_ST_GPN_ID] = "GPN_ID",
 	[RPORT_ST_GNN_ID] = "GNN_ID",
 	[RPORT_ST_PLOGI] = "PLOGI",
-	[RPORT_ST_PLOGI_RECV] = "PLOGI recv",
 	[RPORT_ST_PRLI] = "PRLI",
 	[RPORT_ST_RTV] = "RTV",
 	[RPORT_ST_READY] = "Ready",
@@ -173,18 +172,6 @@ static struct fc_rport *fc_remote_port_create(struct fc_lport *lp,
 	return rport;
 }
 
-static inline void fc_rport_lock(struct fc_rport *rport)
-{
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	spin_lock_bh(&rp->rp_lock);
-}
-
-static inline void fc_rport_unlock(struct fc_rport *rport)
-{
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	spin_unlock_bh(&rp->rp_lock);
-}
-
 static void fc_rport_state_enter(struct fc_rport *rport,
 				 enum fc_rport_state new)
 {
@@ -214,40 +201,37 @@ static void fc_rport_enter_ready(struct fc_rport *rp)
 
 /**
  * fc_rport_enter_gpn_id - Send Get Port Name by ID (GPN_ID) request
- * @work: The work member of the fc_ns_port structure
+ * @rp: The fc_rport
  *
- * XXX - this the following statement still valid?
- * The remote port is held by the caller for us.
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 void fc_rport_enter_gpn_id(struct fc_rport *rp)
 {
-	struct fc_rport_libfc_priv *rpp;
-	struct fc_lport *lp;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame *fp;
 	struct req {
 		struct fc_ct_hdr ct;
 		struct fc_ns_fid fid;
 	} *cp;
 
-	rpp = rp->dd_data;
-	lp = rpp->local_port;
-
 	fc_rport_state_enter(rp, RPORT_ST_GPN_ID);
 
 	if (fc_rp_debug)
 		FC_DBG("Entered GPN_ID for port (%6x)\n", rp->port_id);
 
 	fp = fc_frame_alloc(lp, sizeof(*cp));
-	if (!fp)
+	if (!fp) {
+		fc_rport_error(rp, fp);
 		return;
+	}
 
 	cp = fc_frame_payload_get(fp, sizeof(*cp));
 	fc_fill_dns_hdr(lp, &cp->ct, FC_NS_GPN_ID, sizeof(cp->fid));
 	hton24(cp->fid.fp_fid, rp->port_id);
-
-	WARN_ON(!fc_lport_test_ready(lp));
-
 	fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_gpn_id_resp,
 				  rp, lp->e_d_tov,
@@ -262,30 +246,43 @@ void fc_rport_enter_gpn_id(struct fc_rport *rp)
  * @sp: Current sequence of GPN_ID exchange
  * @fp: response frame
  * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ *
+ * Locking Note: This is an action function so we lock and then call
+ *               the next _enter_* function, unlock when it returns.
  */
 static void fc_rport_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
-			      void *rp_arg)
+				 void *rp_arg)
 {
 	struct fc_rport *rp = rp_arg;
 	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	unsigned long flags;
 
-	struct fc_lport *lp;
 	struct resp {
 		struct fc_ct_hdr ct;
 		__be64 wwn;
 	} *cp;
 	unsigned int cmd;
 
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_GPN_ID) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a GPN_ID response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
+		return;
+	}
+
 	if (IS_ERR(fp)) {
 		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		return;
 	}
 
-	lp = rpp->local_port;
 	WARN_ON(!fc_frame_is_linear(fp));	/* buffer must be contiguous */
 
 	cp = fc_frame_payload_get(fp, sizeof(cp->ct));
-	if (cp == NULL) {
+	if (!cp) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		FC_DBG("GPN_ID response too short, len %d\n", fr_len(fp));
 		return;
 	}
@@ -293,20 +290,22 @@ static void fc_rport_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 	switch (cmd) {
 	case FC_FS_ACC:
 		cp = fc_frame_payload_get(fp, sizeof(*cp));
-		if (cp == NULL) {
+		if (!cp) {
+			spin_unlock_irqrestore(&rpp->rp_lock, flags);
 			FC_DBG("GPN_ID response payload too short, len %d\n",
 			       fr_len(fp));
 			break;
 		}
 		rp->port_name = ntohll(cp->wwn);
-
 		fc_rport_enter_gnn_id(rp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		break;
 	case FC_FS_RJT:
-		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
-		fc_rport_destroy_dummy(rp);
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		break;
 	default:
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		FC_DBG("GPN_ID unexpected CT response cmd %x\n", cmd);
 		break;
 	}
@@ -315,41 +314,37 @@ static void fc_rport_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_rport_enter_gnn_id - Send Get Node Name by ID (GNN_ID) request
- * @lp: Fibre Channel host port instance
- * @dp: Temporary discovery port for holding IDs and world wide names
+ * @rp: The fc_rport object
  *
- * XXX- Is the following statement still true?
- * The remote port is held by the caller for us.
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 void fc_rport_enter_gnn_id(struct fc_rport *rp)
 {
-	struct fc_rport_libfc_priv *rpp;
-	struct fc_lport *lp;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame *fp;
 	struct req {
 		struct fc_ct_hdr ct;
 		struct fc_ns_fid fid;
 	} *cp;
 
-	rpp = rp->dd_data;
-	lp = rpp->local_port;
+	fc_rport_state_enter(rp, RPORT_ST_GNN_ID);
 
 	if (fc_rp_debug)
 		FC_DBG("Entered GNN_ID for port (%6x)\n", rp->port_id);
 
-	fc_rport_state_enter(rp, RPORT_ST_GNN_ID);
-
 	fp = fc_frame_alloc(lp, sizeof(*cp));
-	if (!fp)
+	if (!fp) {
+		fc_rport_error(rp, fp);
 		return;
+	}
 
 	cp = fc_frame_payload_get(fp, sizeof(*cp));
 	fc_fill_dns_hdr(lp, &cp->ct, FC_NS_GNN_ID, sizeof(cp->fid));
 	hton24(cp->fid.fp_fid, rp->port_id);
-
-	WARN_ON(!fc_lport_test_ready(lp));
-
 	fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_gnn_id_resp,
 				  rp, lp->e_d_tov,
@@ -364,14 +359,21 @@ void fc_rport_enter_gnn_id(struct fc_rport *rp)
  * @sp: Current sequence of GNN_ID exchange
  * @fp: response frame
  * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ *
+ * Locking Note: This is an action function so we hold the lock and then
+ * call an _enter_* function. This method is special though becuase this
+ * is where we transition from the dummy rport to the one the the
+ * transport class is aware of. We must transition the lock, state, WWNN,
+ * WWPN and FID from the dummy to the new rport.
  */
 static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 				 void *rp_arg)
 {
 	struct fc_rport *rp = rp_arg;
-	struct fc_rport_libfc_priv *rpp;
-	struct fc_lport *lp;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_rport_identifiers ids;
+	unsigned long flags;
 
 	struct resp {
 		struct fc_ct_hdr ct;
@@ -379,19 +381,27 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 	} *cp;
 	unsigned int cmd;
 
-	rpp = rp->dd_data;
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_GNN_ID) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a GNN_ID response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
+		return;
+	}
 
 	if (IS_ERR(fp)) {
 		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		return;
 	}
 
-	lp = rpp->local_port;
-	WARN_ON(!fc_frame_is_linear(fp));	/* buffer must be contiguous */
+	WARN_ON(!fc_frame_is_linear(fp));      /* buffer must be contiguous */
 
 	cp = fc_frame_payload_get(fp, sizeof(cp->ct));
-	if (cp == NULL) {
-		FC_DBG("GNN_ID response too short,  len %d\n", fr_len(fp));
+	if (!cp) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("GNN_ID response too short, len %d\n", fr_len(fp));
 		return;
 	}
 
@@ -399,7 +409,8 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 	switch (cmd) {
 	case FC_FS_ACC:
 		cp = fc_frame_payload_get(fp, sizeof(*cp));
-		if (cp == NULL) {
+		if (!cp) {
+			spin_unlock_irqrestore(&rpp->rp_lock, flags);
 			FC_DBG("GNN_ID response payload too short, len %d\n",
 			       fr_len(fp));
 			break;
@@ -411,31 +422,52 @@ static void fc_rport_gnn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
 		ids.node_name = rp->node_name;
 		ids.roles = rp->roles;
 
-		fc_rport_destroy_dummy(rp);
-
 		if ((ids.port_name != -1) && (ids.port_id != -1) &&
 		    (ids.port_id != lp->fid) && (ids.port_name != lp->wwpn)) {
-			rp = lp->tt.rport_lookup(lp, ids.port_id);
-			if (!rp)
-				rp = lp->tt.rport_create(lp, &ids);
+			struct fc_rport *new_rp;
+			new_rp = lp->tt.rport_lookup(lp, ids.port_id);
+			if (!new_rp) {
+				spin_unlock_irqrestore(&rpp->rp_lock, flags);
+				new_rp = lp->tt.rport_create(lp, &ids);
+				spin_lock_irqsave(&rpp->rp_lock, flags);
+			}
+
+			if (new_rp) {
+				/*
+				 * Transition locking and state from
+				 * the dummy rport to the real rport
+				 */
+				unsigned long new_flags;
+				struct fc_rport_libfc_priv *new_rpp =
+					new_rp->dd_data;
+
+				spin_lock_irqsave(&new_rpp->rp_lock, new_flags);
+				spin_unlock_irqrestore(&rpp->rp_lock, flags);
+
+				fc_rport_destroy_dummy(rp);
+				fc_rport_state_enter(new_rp, RPORT_ST_GNN_ID);
+				fc_rport_enter_plogi(new_rp);
+				spin_unlock_irqrestore(&new_rpp->rp_lock,
+						       new_flags);
+			} else {
+				lp->tt.lport_event_callback(lp, rp,
+							    LPORT_EV_RPORT_FAILED);
 
-			if (!rp)
+				spin_unlock_irqrestore(&rpp->rp_lock, flags);
+				fc_rport_destroy_dummy(rp);
 				FC_DBG("Could not create a remote port, "
 				       "WWNN (%llux), WWNN (%llux), "
 				       "FID (%6x)\n", ids.node_name,
 				       ids.port_name, ids.port_id);
-			else {
-				rpp = rp->dd_data;
-				rpp->rp_state = RPORT_ST_GNN_ID;
-				fc_rport_enter_plogi(rp);
 			}
 		}
 		break;
 	case FC_FS_RJT:
-		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
-		fc_rport_destroy_dummy(rp);
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		break;
 	default:
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		FC_DBG("GNN_ID unexpected CT response cmd %x\n", cmd);
 		break;
 	}
@@ -506,115 +538,76 @@ fc_lport_plogi_fill(struct fc_lport *lp,
 /**
  * fc_rport_login - Start the remote port login state machine
  * @work: member of the fc_rport_libfc_priv structure
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
 void fc_rport_login(struct work_struct *work)
 {
-	struct fc_rport *rport;
-	struct fc_rport_libfc_priv *rp;
+	struct fc_rport *rp;
+	struct fc_rport_libfc_priv *rpp;
 	struct fc_lport *lp;
+	unsigned long flags;
 
-	rp = container_of(work,
+	rpp = container_of(work,
 			  struct fc_rport_libfc_priv,
 			  login_work);
+	rp = (((void *)rpp) - sizeof(struct fc_rport));
+	lp = rpp->local_port;
 
-	rport = (((void *)rp) - sizeof(struct fc_rport));
-	lp = rp->local_port;
+	spin_lock_irqsave(&rpp->rp_lock, flags);
 
-	fc_rport_lock(rport);
-	if (rp->rp_state == RPORT_ST_ERROR) {
-		fc_rport_state_enter(rport, RPORT_ST_INIT);
-		fc_rport_unlock(rport);
-		if (fc_rp_debug)
-			FC_DBG("remote %6x closed\n", rport->port_id);
+	/*
+	 * This should be the only non _enter_* function
+	 * that sets the state.
+	 */
+	fc_rport_state_enter(rp, RPORT_ST_INIT);
 
-		lp->tt.lport_event_callback(lp, rport, LPORT_EV_RPORT_FAILED);
-	} else {
-		fc_rport_unlock(rport);
-		if (rport->port_id == FC_FID_DIR_SERV)
-			fc_rport_enter_plogi(rport);
-		else if (rport->port_name == -1)
-			fc_rport_enter_gpn_id(rport);
-		else if (rport->node_name == -1)
-			fc_rport_enter_gnn_id(rport);
-	}
+	if (rp->port_id == FC_FID_DIR_SERV)
+		fc_rport_enter_plogi(rp);
+	else if (rp->port_name == -1)
+		fc_rport_enter_gpn_id(rp);
+	else if (rp->node_name == -1)
+		fc_rport_enter_gnn_id(rp);
+
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
-/*
- * Stop the session - log it off.
+/**
+ * fc_rport_logout - Stop the remote port state machine
+ * @rp: The fc_rport to logout
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
-int fc_rport_logout(struct fc_rport *rport)
+void fc_rport_logout(struct fc_rport *rp)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
-
-	fc_rport_lock(rport);
-	switch (rp->rp_state) {
-	case RPORT_ST_PRLI:
-	case RPORT_ST_RTV:
-	case RPORT_ST_READY:
-		fc_rport_enter_logo(rport);
-		fc_rport_unlock(rport);
-		break;
-	default:
-		fc_rport_state_enter(rport, RPORT_ST_INIT);
-		fc_rport_unlock(rport);
-		if (fc_rp_debug)
-			FC_DBG("remote %6x closed\n", rport->port_id);
-		if (rport == lp->dns_rp &&
-		    lp->state != LPORT_ST_RESET) {
-			fc_lport_lock(lp);
-			del_timer(&lp->state_timer);
-			lp->dns_rp = NULL;
-
-			if (lp->state == LPORT_ST_DNS_STOP) {
-				fc_lport_unlock(lp);
-				lp->tt.lport_logout(lp);
-			} else {
-				lp->tt.lport_login(lp);
-				fc_lport_unlock(lp);
-			}
-
-			fc_remote_port_delete(rport);
-		}
-		break;
-	}
-
-	return 0;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	unsigned long flags;
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+	fc_rport_enter_logo(rp);
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
-/*
- * Reset the session - assume it is logged off.	 Used after fabric logoff.
- * The local port code takes care of resetting the exchange manager.
+/**
+ * fc_rport_reset - Stop and then start the remote port state machine
+ * @rp: The fc_rport to reset
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
  */
-void fc_rport_reset(struct fc_rport *rport)
+void fc_rport_reset(struct fc_rport *rp)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	unsigned long flags;
+	spin_lock_irqsave(&rpp->rp_lock, flags);
 
 	if (fc_rp_debug)
-		FC_DBG("sess to %6x reset\n", rport->port_id);
-	fc_rport_lock(rport);
-
-	lp = rp->local_port;
-	fc_rport_state_enter(rport, RPORT_ST_INIT);
-	fc_rport_unlock(rport);
+		FC_DBG("Port %6x reset\n", rp->port_id);
 
-	if (fc_rp_debug)
-		FC_DBG("remote %6x closed\n", rport->port_id);
-	if (rport == lp->dns_rp &&
-	    lp->state != LPORT_ST_RESET) {
-		fc_lport_lock(lp);
-		del_timer(&lp->state_timer);
-		lp->dns_rp = NULL;
-		if (lp->state == LPORT_ST_DNS_STOP) {
-			fc_lport_unlock(lp);
-			lp->tt.lport_logout(lp);
-		} else {
-			lp->tt.lport_login(lp);
-			fc_lport_unlock(lp);
-		}
-		fc_remote_port_delete(rport);
-	}
+	fc_rport_state_enter(rp, RPORT_ST_INIT);
+	fc_rport_enter_gpn_id(rp);
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
 /*
@@ -689,7 +682,6 @@ static void fc_rport_reject(struct fc_rport *rport)
 	case RPORT_ST_NONE:
 	case RPORT_ST_READY:
 	case RPORT_ST_ERROR:
-	case RPORT_ST_PLOGI_RECV:
 	case RPORT_ST_INIT:
 		BUG();
 		break;
@@ -700,41 +692,51 @@ static void fc_rport_reject(struct fc_rport *rport)
 /*
  * Timeout handler for retrying after allocation failures or exchange timeout.
  */
+
+/**
+ * fc_rport_timeout - Handler for the retry_work timer.
+ *  Simply determine what should be done next.
+ * @work: The work struct of the fc_rport_libfc_priv
+ *
+ * Locking Note: This is an action function so we grab the
+ * lock, call an _enter_* state and then unlock.
+ */
 static void fc_rport_timeout(struct work_struct *work)
 {
-	struct fc_rport_libfc_priv *rp =
+	struct fc_rport_libfc_priv *rpp =
 		container_of(work, struct fc_rport_libfc_priv, retry_work.work);
-	struct fc_rport *rport = (((void *)rp) - sizeof(struct fc_rport));
+	struct fc_rport *rp = (((void *)rpp) - sizeof(struct fc_rport));
+	unsigned long flags;
 
-	switch (rp->rp_state) {
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+	switch (rpp->rp_state) {
 	case RPORT_ST_GPN_ID:
-		fc_rport_enter_gpn_id(rport);
+		fc_rport_enter_gpn_id(rp);
 		break;
 	case RPORT_ST_GNN_ID:
-		fc_rport_enter_gnn_id(rport);
+		fc_rport_enter_gnn_id(rp);
 		break;
 	case RPORT_ST_PLOGI:
-		fc_rport_enter_plogi(rport);
+		fc_rport_enter_plogi(rp);
 		break;
 	case RPORT_ST_PRLI:
-		fc_rport_enter_prli(rport);
+		fc_rport_enter_prli(rp);
 		break;
 	case RPORT_ST_RTV:
-		fc_rport_enter_rtv(rport);
+		fc_rport_enter_rtv(rp);
 		break;
 	case RPORT_ST_LOGO:
-		fc_rport_enter_logo(rport);
+		fc_rport_enter_logo(rp);
 		break;
 	case RPORT_ST_READY:
 	case RPORT_ST_ERROR:
 	case RPORT_ST_INIT:
-		break;
 	case RPORT_ST_NONE:
-	case RPORT_ST_PLOGI_RECV:
 		BUG();
 		break;
 	}
-	put_device(&rport->dev);
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
+	put_device(&rp->dev);
 }
 
 /*
@@ -764,89 +766,103 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
 static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				void *rp_arg)
 {
-	struct fc_els_ls_rjt *rjp;
+	struct fc_rport *rp = rp_arg;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+
 	struct fc_els_flogi *plp;
 	u64 wwpn, wwnn;
 	unsigned int tov;
 	u16 csp_seq;
 	u16 cssp_seq;
 	u8 op;
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	unsigned long flags;
 
-	if (!IS_ERR(fp)) {
-		op = fc_frame_payload_op(fp);
-		fc_rport_lock(rport);
-		if (op == ELS_LS_ACC &&
-		    (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
-			wwpn = get_unaligned_be64(&plp->fl_wwpn);
-			wwnn = get_unaligned_be64(&plp->fl_wwnn);
-
-			fc_rport_set_name(rport, wwpn, wwnn);
-			tov = ntohl(plp->fl_csp.sp_e_d_tov);
-			if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
-				tov /= 1000;
-			if (tov > rp->e_d_tov)
-				rp->e_d_tov = tov;
-			csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
-			cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
-			if (cssp_seq < csp_seq)
-				csp_seq = cssp_seq;
-			rp->max_seq = csp_seq;
-			rport->maxframe_size =
-				fc_plogi_get_maxframe(plp, rp->local_port->mfs);
-			if (rp->rp_state == RPORT_ST_PLOGI)
-				fc_rport_enter_prli(rport);
-		} else {
-			if (fc_rp_debug)
-				FC_DBG("bad PLOGI response\n");
-
-			rjp = fc_frame_payload_get(fp, sizeof(*rjp));
-			if (op == ELS_LS_RJT && rjp != NULL &&
-			    rjp->er_reason == ELS_RJT_INPROG)
-				fc_rport_error(rport, fp);    /* try again */
-			else
-				fc_rport_reject(rport);   /* error */
-		}
-		fc_rport_unlock(rport);
-		fc_frame_free(fp);
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_PLOGI) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a PLOGI response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
+		return;
+	}
+
+	if (IS_ERR(fp)) {
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+	}
+
+	op = fc_frame_payload_op(fp);
+	if (op == ELS_LS_ACC &&
+	    (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
+		wwpn = get_unaligned_be64(&plp->fl_wwpn);
+		wwnn = get_unaligned_be64(&plp->fl_wwnn);
+		fc_rport_set_name(rp, wwpn, wwnn);
+		tov = ntohl(plp->fl_csp.sp_e_d_tov);
+
+		if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
+			tov /= 1000;
+		if (tov > rpp->e_d_tov)
+			rpp->e_d_tov = tov;
+
+		csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
+		cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
+
+		if (cssp_seq < csp_seq)
+			csp_seq = cssp_seq;
+
+		rpp->max_seq = csp_seq;
+		rp->maxframe_size =
+			fc_plogi_get_maxframe(plp, rpp->local_port->mfs);
+
+		fc_rport_enter_prli(rp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	} else {
-		fc_rport_error(rport, fp);
+		if (fc_rp_debug)
+			FC_DBG("Bad PLOGI response\n");
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	}
+	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer
- * @rport: Fibre Channel remote port to send PLOGI to
+ * @rp: Fibre Channel remote port to send PLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
-static void fc_rport_enter_plogi(struct fc_rport *rport)
+static void fc_rport_enter_plogi(struct fc_rport *rp)
 {
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame *fp;
 	struct fc_els_flogi *plogi;
-	struct fc_lport *lp;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+
+	fc_rport_state_enter(rp, RPORT_ST_PLOGI);
 
 	if (fc_rp_debug)
-		FC_DBG("Entered PLOGI for port (%6x)\n", rport->port_id);
+		FC_DBG("Entered PLOGI for port (%6x)\n", rp->port_id);
+
+	rp->maxframe_size = FC_MIN_MAX_PAYLOAD;
 
-	lp = rp->local_port;
-	fc_rport_state_enter(rport, RPORT_ST_PLOGI);
-	rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
 	fp = fc_frame_alloc(lp, sizeof(*plogi));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rp, fp);
+		return;
+	}
+
 	plogi = fc_frame_payload_get(fp, sizeof(*plogi));
-	WARN_ON(!plogi);
-	fc_lport_plogi_fill(rp->local_port, plogi, ELS_PLOGI);
-	rp->e_d_tov = lp->e_d_tov;
+	fc_lport_plogi_fill(lp, plogi, ELS_PLOGI);
+	rpp->e_d_tov = lp->e_d_tov;
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_plogi_resp,
-				  rport, lp->e_d_tov,
-				  rp->local_port->fid,
-				  rport->port_id,
+				  rp, lp->e_d_tov,
+				  lp->fid, rp->port_id,
 				  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-		fc_rport_error(rport, fp);
+		fc_rport_error(rp, fp);
 }
 
 /**
@@ -854,13 +870,15 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
  * @sp: current sequence in the PRLI exchange
  * @fp: response frame
  * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This is an action function, grab the lock and
+ * call the next _enter_* function.
  */
 static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 			       void *rp_arg)
 {
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
+	struct fc_rport *rp = rp_arg;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
 	struct {
 		struct fc_els_prli prli;
 		struct fc_els_spp spp;
@@ -868,49 +886,45 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
 	u32 fcp_parm = 0;
 	u8 op;
+	unsigned long flags;
 
-	if (IS_ERR(fp)) {
-		fc_rport_error(rport, fp);
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_PRLI) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a PRLI response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
 		return;
 	}
 
-	fc_rport_lock(rport);
+	if (IS_ERR(fp)) {
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+	}
+
 	op = fc_frame_payload_op(fp);
 	if (op == ELS_LS_ACC) {
 		pp = fc_frame_payload_get(fp, sizeof(*pp));
 		if (pp && pp->prli.prli_spp_len >= sizeof(pp->spp)) {
 			fcp_parm = ntohl(pp->spp.spp_params);
 			if (fcp_parm & FCP_SPPF_RETRY)
-				rp->flags |= FC_RP_FLAGS_RETRY;
+				rpp->flags |= FC_RP_FLAGS_RETRY;
 		}
 
-		rport->supported_classes = FC_COS_CLASS3;
+		rp->supported_classes = FC_COS_CLASS3;
 		if (fcp_parm & FCP_SPPF_INIT_FCN)
 			roles |= FC_RPORT_ROLE_FCP_INITIATOR;
 		if (fcp_parm & FCP_SPPF_TARG_FCN)
 			roles |= FC_RPORT_ROLE_FCP_TARGET;
 
-		fc_rport_enter_rtv(rport);
-		fc_rport_unlock(rport);
-
-		fc_remote_port_rolechg(rport, roles);
+		fc_rport_enter_rtv(rp);
+		fc_remote_port_rolechg(rp, roles);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	} else {
-		FC_DBG("bad ELS response\n");
-		fc_rport_state_enter(rport, RPORT_ST_ERROR);
-		fc_rport_unlock(rport);
-		if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) {
-			fc_lport_lock(lp);
-			del_timer(&lp->state_timer);
-			lp->dns_rp = NULL;
-			if (lp->state == LPORT_ST_DNS_STOP) {
-				fc_lport_unlock(lp);
-				lp->tt.lport_logout(lp);
-			} else {
-				lp->tt.lport_login(lp);
-				fc_lport_unlock(lp);
-			}
-			fc_remote_port_delete(rport);
-		}
+		if (fc_rp_debug)
+			FC_DBG("Bad PRLI response\n");
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	}
 
 	fc_frame_free(fp);
@@ -921,95 +935,91 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
  * @sp: current sequence in the LOGO exchange
  * @fp: response frame
  * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This is an action function, but all we care about
+ * is retrying or removing the rport that we just logged out of.
  */
 static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 			       void *rp_arg)
 {
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
-	u8 op;
+	struct fc_rport *rp = rp_arg;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	unsigned long flags;
 
-	if (IS_ERR(fp)) {
-		fc_rport_error(rport, fp);
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_LOGO) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a LOGO response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
 		return;
 	}
 
-	fc_rport_lock(rport);
-	op = fc_frame_payload_op(fp);
-	if (op == ELS_LS_ACC) {
-		fc_rport_enter_rtv(rport);
-		fc_rport_unlock(rport);
-	} else {
-		FC_DBG("bad ELS response\n");
-		fc_rport_state_enter(rport, RPORT_ST_ERROR);
-		fc_rport_unlock(rport);
-		if (rport == lp->dns_rp && lp->state != LPORT_ST_RESET) {
-			fc_lport_lock(lp);
-			del_timer(&lp->state_timer);
-			lp->dns_rp = NULL;
-			if (lp->state == LPORT_ST_DNS_STOP) {
-				fc_lport_unlock(lp);
-				lp->tt.lport_logout(lp);
-			} else {
-				lp->tt.lport_login(lp);
-				fc_lport_unlock(lp);
-			}
-			fc_remote_port_delete(rport);
-		}
+	if (IS_ERR(fp)) {
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	}
 
+	if (fc_rp_debug)
+		FC_DBG("Logged out of port (%6x)\n", rp->port_id);
+	fc_remote_port_delete(rp);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_prli - Send Process Login (PRLI) request to peer
- * @rport: Fibre Channel remote port to send PRLI to
+ * @rp: Fibre Channel remote port to send PRLI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
-static void fc_rport_enter_prli(struct fc_rport *rport)
+static void fc_rport_enter_prli(struct fc_rport *rp)
 {
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
+
 	struct {
 		struct fc_els_prli prli;
 		struct fc_els_spp spp;
 	} *pp;
 	struct fc_frame *fp;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
 
-	if (fc_rp_debug)
-		FC_DBG("Entered PRLI for port (%6x)\n", rport->port_id);
+	fc_rport_state_enter(rp, RPORT_ST_PRLI);
 
-	fc_rport_state_enter(rport, RPORT_ST_PRLI);
+	if (fc_rp_debug)
+		FC_DBG("Entered PRLI for port (%6x)\n", rp->port_id);
 
 	/*
-	 * Special case if session is for name server or any other
-	 * well-known address:	Skip the PRLI step.
-	 * This should be made more general, possibly moved to the FCP layer.
+	 * Special case if the RP is a well-known address then skip PRLI
 	 */
-	if (rport->port_id >= FC_FID_DOM_MGR) {
-		fc_rport_enter_ready(rport);
+
+	if (rp->port_id >= FC_FID_DOM_MGR) {
+		fc_rport_enter_ready(rp);
 		return;
 	}
+
 	fp = fc_frame_alloc(lp, sizeof(*pp));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rp, fp);
+		return;
+	}
+
 	pp = fc_frame_payload_get(fp, sizeof(*pp));
-	WARN_ON(!pp);
 	memset(pp, 0, sizeof(*pp));
 	pp->prli.prli_cmd = ELS_PRLI;
 	pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
 	pp->prli.prli_len = htons(sizeof(*pp));
 	pp->spp.spp_type = FC_TYPE_FCP;
 	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
-	pp->spp.spp_params = htonl(rp->local_port->service_params);
+	pp->spp.spp_params = htonl(lp->service_params);
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_prli_resp,
-				  rport, lp->e_d_tov,
-				  rp->local_port->fid,
-				  rport->port_id,
+				  rp, lp->e_d_tov,
+				  lp->fid, rp->port_id,
 				  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-		fc_rport_error(rport, fp);
+		fc_rport_error(rp, fp);
 }
 
 /**
@@ -1023,16 +1033,26 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
 static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 			      void *rp_arg)
 {
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	struct fc_rport *rp = rp_arg;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
 	u8 op;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rpp->rp_lock, flags);
+
+	if (rpp->rp_state != RPORT_ST_RTV) {
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
+		FC_DBG("Recieved a RTV response, but port (%6x) is in "
+		       "the %s state\n", rp->port_id, fc_rport_state(rp));
+		return;
+	}
 
 	if (IS_ERR(fp)) {
-		fc_rport_error(rport, fp);
+		fc_rport_error(rp, fp);
+		spin_unlock_irqrestore(&rpp->rp_lock, flags);
 		return;
 	}
 
-	fc_rport_lock(rport);
 	op = fc_frame_payload_op(fp);
 	if (op == ELS_LS_ACC) {
 		struct fc_els_rtv_acc *rtv;
@@ -1045,106 +1065,119 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 			tov = ntohl(rtv->rtv_r_a_tov);
 			if (tov == 0)
 				tov = 1;
-			rp->r_a_tov = tov;
+			rpp->r_a_tov = tov;
 			tov = ntohl(rtv->rtv_e_d_tov);
 			if (toq & FC_ELS_RTV_EDRES)
 				tov /= 1000000;
 			if (tov == 0)
 				tov = 1;
-			rp->e_d_tov = tov;
+			rpp->e_d_tov = tov;
 		}
 	}
-	fc_rport_state_enter(rport, RPORT_ST_READY);
-	fc_rport_unlock(rport);
-	if (fc_rp_debug)
-		FC_DBG("remote %6x ready\n", rport->port_id);
+	fc_rport_enter_ready(rp);
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_rtv - Send Request Timeout Value (RTV) request to peer
- * @rport: Fibre Channel remote port to send RTV to
+ * @rp: Fibre Channel remote port to send RTV to
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_enter_rtv(struct fc_rport *rport)
+static void fc_rport_enter_rtv(struct fc_rport *rp)
 {
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_els_rtv *rtv;
 	struct fc_frame *fp;
-	struct fc_lport *lp;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
 
-	lp = rp->local_port;
+	fc_rport_state_enter(rp, RPORT_ST_RTV);
 
 	if (fc_rp_debug)
-		FC_DBG("Entered RTV for port (%6x)\n", rport->port_id);
+		FC_DBG("Entered RTV for port (%6x)\n", rp->port_id);
 
-	fc_rport_state_enter(rport, RPORT_ST_RTV);
 
 	fp = fc_frame_alloc(lp, sizeof(*rtv));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rp, fp);
+		return;
+	}
+
 	rtv = fc_frame_payload_get(fp, sizeof(*rtv));
-	WARN_ON(!rtv);
 	memset(rtv, 0, sizeof(*rtv));
 	rtv->rtv_cmd = ELS_RTV;
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_rtv_resp,
-				  rport, lp->e_d_tov,
-				  rp->local_port->fid,
-				  rport->port_id,
+				  rp, lp->e_d_tov,
+				  lp->fid, rp->port_id,
 				  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-		fc_rport_error(rport, fp);
+		fc_rport_error(rp, fp);
 }
 
 /**
  * fc_rport_enter_logo - Send Logout (LOGO) request to peer
- * @rport: Fibre Channel remote port to send LOGO to
+ * @rp: Fibre Channel remote port to send LOGO to
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_enter_logo(struct fc_rport *rport)
+static void fc_rport_enter_logo(struct fc_rport *rp)
 {
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame *fp;
 	struct fc_els_logo *logo;
-	struct fc_lport *lp;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
 
-	if (fc_rp_debug)
-		FC_DBG("Entered LOGO for port (%6x)\n", rport->port_id);
+	fc_rport_state_enter(rp, RPORT_ST_LOGO);
 
-	fc_rport_state_enter(rport, RPORT_ST_LOGO);
+	if (fc_rp_debug)
+		FC_DBG("Entered LOGO for port (%6x)\n", rp->port_id);
 
-	lp = rp->local_port;
 	fp = fc_frame_alloc(lp, sizeof(*logo));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rp, fp);
+		return;
+	}
+
 	logo = fc_frame_payload_get(fp, sizeof(*logo));
 	memset(logo, 0, sizeof(*logo));
 	logo->fl_cmd = ELS_LOGO;
 	hton24(logo->fl_n_port_id, lp->fid);
 	logo->fl_n_port_wwn = htonll(lp->wwpn);
-
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lp->tt.exch_seq_send(lp, fp,
 				  fc_rport_logo_resp,
-				  rport, lp->e_d_tov,
-				  rp->local_port->fid,
-				  rport->port_id,
+				  rp, lp->e_d_tov,
+				  lp->fid, rp->port_id,
 				  FC_FC_SEQ_INIT | FC_FC_END_SEQ))
-		fc_rport_error(rport, fp);
+		fc_rport_error(rp, fp);
 }
 
-/*
- * Handle a request received by the exchange manager for the session.
- * This may be an entirely new session, or a PLOGI or LOGO for an existing one.
- * This will free the frame.
+/**
+ * fc_rport_recv_req - Handle an incoming request
+ * @sp: A pointer to the sequence
+ * @fp: A pointer to the frame
+ * @rp_arg: A void pointer to the fc_rport object
+ *
+ * Locking Note: This function calls one of the request handlers. We need
+ * to lock, call the request handler and then unlock when it returns to us.
  */
 void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg)
 {
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	struct fc_rport *rp = rp_arg;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame_header *fh;
-	struct fc_lport *lp = rp->local_port;
 	struct fc_seq_els_data els_data;
 	u8 op;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rpp->rp_lock, flags);
 
 	els_data.fp = NULL;
 	els_data.explan = ELS_EXPL_NONE;
@@ -1156,16 +1189,16 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg)
 		op = fc_frame_payload_op(fp);
 		switch (op) {
 		case ELS_PLOGI:
-			fc_rport_recv_plogi_req(rport, sp, fp);
+			fc_rport_recv_plogi_req(rp, sp, fp);
 			break;
 		case ELS_PRLI:
-			fc_rport_recv_prli_req(rport, sp, fp);
+			fc_rport_recv_prli_req(rp, sp, fp);
 			break;
 		case ELS_PRLO:
-			fc_rport_recv_prlo_req(rport, sp, fp);
+			fc_rport_recv_prlo_req(rp, sp, fp);
 			break;
 		case ELS_LOGO:
-			fc_rport_recv_logo_req(rport, sp, fp);
+			fc_rport_recv_logo_req(rp, sp, fp);
 			break;
 		case ELS_RRQ:
 			els_data.fp = fp;
@@ -1184,27 +1217,31 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg)
 	} else {
 		fc_frame_free(fp);
 	}
+	spin_unlock_irqrestore(&rpp->rp_lock, flags);
 }
 
 /**
  * fc_rport_recv_plogi_req - Handle incoming Port Login (PLOGI) request
- * @rport: Fibre Channel remote port that initiated PLOGI
+ * @rp: Fibre Channel remote port that initiated PLOGI
  * @sp: current sequence in the PLOGI exchange
  * @fp: PLOGI request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+static void fc_rport_recv_plogi_req(struct fc_rport *rp,
 				    struct fc_seq *sp, struct fc_frame *rx_fp)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_frame *fp = rx_fp;
 	struct fc_frame_header *fh;
-	struct fc_lport *lp;
 	struct fc_els_flogi *pl;
 	struct fc_seq_els_data rjt_data;
+	enum fc_els_rjt_reason reject = 0;
 	u32 sid;
 	u64 wwpn;
 	u64 wwnn;
-	enum fc_els_rjt_reason reject = 0;
 	u32 f_ctl;
 
 	rjt_data.fp = NULL;
@@ -1220,8 +1257,6 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	}
 	wwpn = get_unaligned_be64(&pl->fl_wwpn);
 	wwnn = get_unaligned_be64(&pl->fl_wwnn);
-	fc_rport_lock(rport);
-	lp = rp->local_port;
 
 	/*
 	 * If the session was just created, possibly due to the incoming PLOGI,
@@ -1234,7 +1269,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	 * XXX TBD: If the session was ready before, the PLOGI should result in
 	 * all outstanding exchanges being reset.
 	 */
-	switch (rp->rp_state) {
+	switch (rpp->rp_state) {
 	case RPORT_ST_INIT:
 		if (fc_rp_debug)
 			FC_DBG("incoming PLOGI from %6x wwpn %llx state INIT "
@@ -1244,7 +1279,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	case RPORT_ST_PLOGI:
 		if (fc_rp_debug)
 			FC_DBG("incoming PLOGI from %x in PLOGI state %d\n",
-			       sid, rp->rp_state);
+			       sid, rpp->rp_state);
 		if (wwpn < lp->wwpn)
 			reject = ELS_RJT_INPROG;
 		break;
@@ -1253,14 +1288,14 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	case RPORT_ST_READY:
 		if (fc_rp_debug)
 			FC_DBG("incoming PLOGI from %x in logged - in state %d "
-			       "- ignored for now\n", sid, rp->rp_state);
+			       "- ignored for now\n", sid, rpp->rp_state);
 		/* XXX TBD - should reset */
 		break;
 	case RPORT_ST_NONE:
 	default:
 		if (fc_rp_debug)
 			FC_DBG("incoming PLOGI from %x in unexpected "
-			       "state %d\n", sid, rp->rp_state);
+			       "state %d\n", sid, rpp->rp_state);
 		break;
 	}
 
@@ -1271,7 +1306,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 		fc_frame_free(fp);
 	} else {
 		fp = fc_frame_alloc(lp, sizeof(*pl));
-		if (fp == NULL) {
+		if (!fp) {
 			fp = rx_fp;
 			rjt_data.reason = ELS_RJT_UNAB;
 			rjt_data.explan = ELS_EXPL_NONE;
@@ -1280,12 +1315,12 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 		} else {
 			sp = lp->tt.seq_start_next(sp);
 			WARN_ON(!sp);
-			fc_rport_set_name(rport, wwpn, wwnn);
+			fc_rport_set_name(rp, wwpn, wwnn);
 
 			/*
 			 * Get session payload size from incoming PLOGI.
 			 */
-			rport->maxframe_size =
+			rp->maxframe_size =
 				fc_plogi_get_maxframe(pl, lp->mfs);
 			fc_frame_free(rx_fp);
 			pl = fc_frame_payload_get(fp, sizeof(*pl));
@@ -1299,14 +1334,11 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 			f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
 			fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
 			lp->tt.seq_send(lp, sp, fp, f_ctl);
-			if (rp->rp_state == RPORT_ST_PLOGI)
-				fc_rport_enter_prli(rport);
-			else
-				fc_rport_state_enter(rport,
-						     RPORT_ST_PLOGI_RECV);
+			if (rpp->rp_state == RPORT_ST_PLOGI)
+				fc_rport_enter_prli(rp);
 		}
 	}
-	fc_rport_unlock(rport);
+
 }
 
 /**
@@ -1314,11 +1346,14 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
  * @rport: Fibre Channel remote port that initiated PRLI
  * @sp: current sequence in the PRLI exchange
  * @fp: PRLI request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_prli_req(struct fc_rport *rport,
+static void fc_rport_recv_prli_req(struct fc_rport *rp,
 				   struct fc_seq *sp, struct fc_frame *rx_fp)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
 	struct fc_frame *fp;
 	struct fc_frame_header *fh;
 	struct fc_lport *lp;
@@ -1340,9 +1375,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 
 	rjt_data.fp = NULL;
 	fh = fc_frame_header_get(rx_fp);
-	lp = rp->local_port;
-	switch (rp->rp_state) {
-	case RPORT_ST_PLOGI_RECV:
+	lp = rpp->local_port;
+	switch (rpp->rp_state) {
 	case RPORT_ST_PRLI:
 	case RPORT_ST_READY:
 		reason = ELS_RJT_NONE;
@@ -1406,16 +1440,16 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 			case FC_TYPE_FCP:
 				fcp_parm = ntohl(rspp->spp_params);
 				if (fcp_parm * FCP_SPPF_RETRY)
-					rp->flags |= FC_RP_FLAGS_RETRY;
-				rport->supported_classes = FC_COS_CLASS3;
+					rpp->flags |= FC_RP_FLAGS_RETRY;
+				rp->supported_classes = FC_COS_CLASS3;
 				if (fcp_parm & FCP_SPPF_INIT_FCN)
 					roles |= FC_RPORT_ROLE_FCP_INITIATOR;
 				if (fcp_parm & FCP_SPPF_TARG_FCN)
 					roles |= FC_RPORT_ROLE_FCP_TARGET;
 
-				fc_remote_port_rolechg(rport, roles);
+				fc_remote_port_rolechg(rp, roles);
 				spp->spp_params =
-					htonl(rp->local_port->service_params);
+					htonl(rpp->local_port->service_params);
 				break;
 			default:
 				resp = FC_SPP_RESP_INVL;
@@ -1437,14 +1471,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 		/*
 		 * Get lock and re-check state.
 		 */
-		fc_rport_lock(rport);
-		switch (rp->rp_state) {
-		case RPORT_ST_PLOGI_RECV:
+		switch (rpp->rp_state) {
 		case RPORT_ST_PRLI:
-			fc_rport_state_enter(rport, RPORT_ST_READY);
+			fc_rport_state_enter(rp, RPORT_ST_READY);
 			if (fc_rp_debug)
-				FC_DBG("remote %6x ready\n", rport->port_id);
-			if (rport == lp->dns_rp &&
+				FC_DBG("remote %6x ready\n", rp->port_id);
+			if (rp == lp->dns_rp &&
 			    lp->state == LPORT_ST_DNS) {
 				fc_lport_lock(lp);
 				del_timer(&lp->state_timer);
@@ -1457,28 +1489,31 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 		default:
 			break;
 		}
-		fc_rport_unlock(rport);
+
 	}
 	fc_frame_free(rx_fp);
 }
 
 /**
  * fc_rport_recv_prlo_req - Handle incoming Process Logout (PRLO) request
- * @rport: Fibre Channel remote port that initiated PRLO
+ * @rp: Fibre Channel remote port that initiated PRLO
  * @sp: current sequence in the PRLO exchange
  * @fp: PRLO request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_prlo_req(struct fc_rport *rp, struct fc_seq *sp,
 				   struct fc_frame *fp)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
 	struct fc_frame_header *fh;
-	struct fc_lport *lp = rp->local_port;
+	struct fc_lport *lp = rpp->local_port;
 	struct fc_seq_els_data rjt_data;
 
 	fh = fc_frame_header_get(fp);
 	FC_DBG("incoming PRLO from %x state %d\n",
-	       ntoh24(fh->fh_s_id), rp->rp_state);
+	       ntoh24(fh->fh_s_id), rpp->rp_state);
 	rjt_data.fp = NULL;
 	rjt_data.reason = ELS_RJT_UNAB;
 	rjt_data.explan = ELS_EXPL_NONE;
@@ -1491,34 +1526,27 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
  * @rport: Fibre Channel remote port that initiated LOGO
  * @sp: current sequence in the LOGO exchange
  * @fp: LOGO request frame
+ *
+ * Locking Note: This function expects that the rport lock is held before
+ * being called.
  */
-static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_logo_req(struct fc_rport *rp, struct fc_seq *sp,
 				   struct fc_frame *fp)
 {
 	struct fc_frame_header *fh;
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
 
-	fh = fc_frame_header_get(fp);
-	fc_rport_lock(rport);
-	fc_rport_state_enter(rport, RPORT_ST_INIT);
-	fc_rport_unlock(rport);
 	if (fc_rp_debug)
-		FC_DBG("remote %6x closed\n", rport->port_id);
-	if (rport == lp->dns_rp &&
-	    lp->state != LPORT_ST_RESET) {
-		fc_lport_lock(lp);
-		del_timer(&lp->state_timer);
-		lp->dns_rp = NULL;
-		if (lp->state == LPORT_ST_DNS_STOP) {
-			fc_lport_unlock(lp);
-			lp->tt.lport_logout(lp);
-		} else {
-			lp->tt.lport_login(lp);
-			fc_lport_unlock(lp);
-		}
-		fc_remote_port_delete(rport);
-	}
+		FC_DBG("Received LOGO request for port (%6x)\n", rp->port_id);
+
+	fh = fc_frame_header_get(fp);
+
+	if (rp->port_id == FC_FID_DIR_SERV)
+		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_LOGO);
+
+	fc_remote_port_delete(rp);
+
 	lp->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
 	fc_frame_free(fp);
 }
diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h
index ef0bb8e..61a7d59 100644
--- a/include/scsi/libfc/libfc.h
+++ b/include/scsi/libfc/libfc.h
@@ -103,7 +103,8 @@ enum fc_lport_state {
 
 enum fc_lport_event {
 	LPORT_EV_RPORT_CREATED = 0,
-	LPORT_EV_RPORT_FAILED
+	LPORT_EV_RPORT_FAILED,
+	LPORT_EV_RPORT_LOGO,
 };
 
 enum fc_rport_state {
@@ -112,7 +113,6 @@ enum fc_rport_state {
 	RPORT_ST_GPN_ID,        /* get the WWPN from the name server */
 	RPORT_ST_GNN_ID,        /* get the WWNN from the name server */
 	RPORT_ST_PLOGI,		/* waiting for PLOGI completion */
-	RPORT_ST_PLOGI_RECV,	/* received PLOGI (as target) */
 	RPORT_ST_PRLI,		/* waiting for PRLI completion */
 	RPORT_ST_RTV,		/* waiting for RTV completion */
 	RPORT_ST_ERROR,		/* error */
@@ -361,7 +361,7 @@ struct libfc_function_template {
 	 * Logs the specified local port out of a N_Port identified
 	 * by the ID provided.
 	 */
-	int (*rport_logout)(struct fc_rport *rport);
+	void (*rport_logout)(struct fc_rport *rport);
 
 	void (*rport_recv_req)(struct fc_seq *, struct fc_frame *, void *);
 




More information about the devel mailing list