[Open-FCoE] [RFC PATCH 13/28] libfc: Rework the rport locking

Robert Love robert.w.love at intel.com
Thu Sep 11 23:36:16 UTC 2008


Aside from the following, this patch also converts from irq safe spin locking to 
spin_lock_bh.

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
        - Does the lock need to be held or is it just becuase it's only callers
          already have the lock held?
 fc_rport_reject
        - Only call from retry handler (fc_rport_error)

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

Unknown
 fc_rport_ns_error
        - Merge with fc_rport_error
 fc_rport_retry
        - Merge with fc_rport_error
 fc_rport_reset_list

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

 drivers/scsi/libfc/fc_ns.c    |    2 
 drivers/scsi/libfc/fc_rport.c |  398 ++++++++++++++++++++++++++---------------
 include/scsi/libfc/libfc.h    |    4 
 3 files changed, 257 insertions(+), 147 deletions(-)

diff --git a/drivers/scsi/libfc/fc_ns.c b/drivers/scsi/libfc/fc_ns.c
index 551d35d..d086b9d 100644
--- a/drivers/scsi/libfc/fc_ns.c
+++ b/drivers/scsi/libfc/fc_ns.c
@@ -76,7 +76,7 @@ struct fc_rport *fc_ns_create_dummy_rport(struct fc_ns_port *dp)
 	rp->node_name = dp->ids.node_name;
 	rp->roles = dp->ids.roles;
 
-	spin_lock_init(&rpp->rp_lock);
+	mutex_init(&rpp->rp_mutex);
 	rpp->local_port = dp->lp;
 	rpp->rp_state = RPORT_ST_INIT;
 	rpp->flags = FC_RP_FLAGS_REC_SUPPORTED;
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 3aef37e..ff46c8a 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -125,7 +125,7 @@ static struct fc_rport *fc_remote_port_create(struct fc_lport *lport,
 	/* default value until service parameters are exchanged in PLOGI */
 	rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
 
-	spin_lock_init(&rdata->rp_lock);
+	mutex_init(&rdata->rp_mutex);
 	rdata->rp_state = RPORT_ST_INIT;
 	rdata->local_port = lport;
 	rdata->e_d_tov = lport->e_d_tov;
@@ -136,18 +136,6 @@ static struct fc_rport *fc_remote_port_create(struct fc_lport *lport,
 	return rport;
 }
 
-static inline void fc_rport_lock(struct fc_rport *rport)
-{
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	spin_lock_bh(&rdata->rp_lock);
-}
-
-static inline void fc_rport_unlock(struct fc_rport *rport)
-{
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	spin_unlock_bh(&rdata->rp_lock);
-}
-
 /**
  * fc_plogi_get_maxframe - Get max payload from the common service parameters
  * @flp: FLOGI payload structure
@@ -220,89 +208,73 @@ static void fc_rport_state_enter(struct fc_rport *rport,
 /**
  * fc_rport_login - Start the remote port login state machine
  * @rport: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
  */
 int fc_rport_login(struct fc_rport *rport)
 {
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	struct fc_lport *lport = rdata->local_port;
 
-	fc_rport_lock(rport);
-	if (rdata->rp_state == RPORT_ST_INIT) {
-		fc_rport_unlock(rport);
-		fc_rport_enter_plogi(rport);
-	} else if (rdata->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);
+	mutex_lock(&rdata->rp_mutex);
 
-		if (rdata->event_callback)
-			rdata->event_callback(lport, rport,
-					      LPORT_EV_RPORT_FAILED);
+	if (fc_rp_debug)
+		FC_DBG("Login to port (%6x)\n", rport->port_id);
 
-		fc_remote_port_delete(rport);
-	} else
-		fc_rport_unlock(rport);
+	fc_rport_enter_plogi(rport);
+
+	mutex_unlock(&rdata->rp_mutex);
 
 	return 0;
 }
 
-/*
- * Stop the session - log it off.
+/**
+ * fc_rport_logout - Logout of the remote port and delete it
+ * @rport: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
  */
 int fc_rport_logout(struct fc_rport *rport)
 {
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	struct fc_lport *lport = rdata->local_port;
 
-	fc_rport_lock(rport);
-	switch (rdata->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);
+	mutex_lock(&rdata->rp_mutex);
 
-		if (rdata->event_callback)
-			rdata->event_callback(lport, rport,
-					      LPORT_EV_RPORT_FAILED);
+	if (fc_rp_debug)
+		FC_DBG("Logout of port (%6x)\n", rport->port_id);
 
-			fc_remote_port_delete(rport);
-		break;
-	}
+	fc_rport_enter_logo(rport);
+	mutex_unlock(&rdata->rp_mutex);
 
 	return 0;
 }
 
-/*
- * 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 - Reset the remote port
+ * @rport: Fibre Channel remote port
+ *
+ * XXX - This functionality is currently broken
+ *
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
  */
 void fc_rport_reset(struct fc_rport *rport)
 {
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	struct fc_lport *lport = rdata->local_port;
 
-	if (fc_rp_debug)
-		FC_DBG("sess to %6x reset\n", rport->port_id);
-
-	fc_rport_lock(rport);
-	fc_rport_state_enter(rport, RPORT_ST_INIT);
-	fc_rport_unlock(rport);
+	mutex_lock(&rdata->rp_mutex);
 
 	if (fc_rp_debug)
-		FC_DBG("remote %6x closed\n", rport->port_id);
+		FC_DBG("Reset port (%6x)\n", rport->port_id);
 
-	if (rdata->event_callback)
-		rdata->event_callback(lport, rport, LPORT_EV_RPORT_FAILED);
+	fc_rport_enter_plogi(rport);
 
-	fc_remote_port_delete(rport);
+	mutex_unlock(&rdata->rp_mutex);
 }
 
 /*
@@ -330,10 +302,9 @@ static void fc_rport_enter_ready(struct fc_rport *rport)
 	fc_rport_state_enter(rport, RPORT_ST_READY);
 
 	if (fc_rp_debug)
-		FC_DBG("remote %6x ready\n", rport->port_id);
+		FC_DBG("Port (%6x) is Ready\n", rport->port_id);
 
 	fc_remote_port_rolechg(rport, rdata->roles);
-
 	if (rdata->event_callback)
 		rdata->event_callback(lport, rport, LPORT_EV_RPORT_CREATED);
 }
@@ -382,8 +353,9 @@ static void fc_rport_reject(struct fc_rport *rport)
  *  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.
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
  */
 static void fc_rport_timeout(struct work_struct *work)
 {
@@ -391,6 +363,8 @@ static void fc_rport_timeout(struct work_struct *work)
 		container_of(work, struct fc_rport_libfc_priv, retry_work.work);
 	struct fc_rport *rport = (((void *)rdata) - sizeof(struct fc_rport));
 
+	mutex_lock(&rdata->rp_mutex);
+		
 	switch (rdata->rp_state) {
 	case RPORT_ST_PLOGI:
 		fc_rport_enter_plogi(rport);
@@ -414,6 +388,8 @@ static void fc_rport_timeout(struct work_struct *work)
 		break;
 	}
 	put_device(&rport->dev);
+
+	mutex_unlock(&rdata->rp_mutex);
 }
 
 /**
@@ -447,14 +423,20 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
 }
 
 /**
- * fc_rport_plpogi_recv_resp - Handle incoming ELS PLOGI response
+ * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response
  * @sp: current sequence in the PLOGI exchange
  * @fp: response frame
  * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
  */
 static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				void *rp_arg)
 {
+	struct fc_rport *rport = rp_arg;
+	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_els_ls_rjt *rjp;
 	struct fc_els_flogi *plp;
 	u64 wwpn, wwnn;
@@ -462,21 +444,33 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	u16 csp_seq;
 	u16 cssp_seq;
 	u8 op;
-	struct fc_rport *rport = rp_arg;
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 
-	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);
+	mutex_lock(&rdata->rp_mutex);
 
-			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 (fc_rp_debug)
+		FC_DBG("Received a PLOGI response\n");
+
+	if (rdata->rp_state != RPORT_ST_PLOGI) {
+		FC_DBG("Received a PLOGI response, but in state %s\n",
+		       fc_rport_state(rport));
+		goto out;
+	}
+
+	if (IS_ERR(fp)) {
+		fc_rport_error(rport, fp);
+		goto out;
+	}
+
+	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(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 > rdata->e_d_tov)
 				rdata->e_d_tov = tov;
 			csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
@@ -496,43 +490,53 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				fc_rport_enter_ready(rport);
 			else
 				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);
-		}
-		fc_rport_unlock(rport);
-		fc_frame_free(fp);
 	} else {
-		fc_rport_error(rport, fp);
+		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);
 	}
+
+out:
+	mutex_unlock(&rdata->rp_mutex);
+	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_plogi - Send Port Login (PLOGI) request to peer
  * @rport: 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)
 {
-	struct fc_frame *fp;
-	struct fc_els_flogi *plogi;
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
+	struct fc_frame *fp;
+	struct fc_els_flogi *plogi;
+
+	if (fc_rp_debug)
+		FC_DBG("Port (%6x) entered PLOGI state from %s state\n",
+		       rport->port_id, fc_rport_state(rport));
 
 	fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+
 	rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
 	fp = fc_frame_alloc(lport, sizeof(*plogi));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rport, fp);
+		return;
+	}
+
 	plogi = fc_frame_payload_get(fp, sizeof(*plogi));
-	WARN_ON(!plogi);
 	fc_lport_plogi_fill(rdata->local_port, plogi, ELS_PLOGI);
 	rdata->e_d_tov = lport->e_d_tov;
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lport->tt.exch_seq_send(lport, fp,
 				     fc_rport_plogi_resp,
 				     rport, lport->e_d_tov,
@@ -547,6 +551,10 @@ 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 function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
  */
 static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 			       void *rp_arg)
@@ -562,12 +570,22 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 	u32 fcp_parm = 0;
 	u8 op;
 
+	mutex_lock(&rdata->rp_mutex);
+
+	if (fc_rp_debug)
+		FC_DBG("Received a PRLI response\n");
+
+	if (rdata->rp_state != RPORT_ST_PRLI) {
+		FC_DBG("Received a PRLI response, but in state %s\n",
+		       fc_rport_state(rport));
+		goto out;
+	}
+
 	if (IS_ERR(fp)) {
 		fc_rport_error(rport, fp);
-		return;
+		goto out;
 	}
 
-	fc_rport_lock(rport);
 	op = fc_frame_payload_op(fp);
 	if (op == ELS_LS_ACC) {
 		pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -585,12 +603,10 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 		rdata->roles = roles;
 		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 (rdata->event_callback)
 			rdata->event_callback(lport, rport,
 					      LPORT_EV_RPORT_FAILED);
@@ -598,6 +614,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 		fc_remote_port_delete(rport);
 	}
 
+out:
+	mutex_unlock(&rdata->rp_mutex);
 	fc_frame_free(fp);
 }
 
@@ -606,6 +624,10 @@ 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 function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
  */
 static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 			       void *rp_arg)
@@ -615,20 +637,29 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_lport *lport = rdata->local_port;
 	u8 op;
 
+	mutex_lock(&rdata->rp_mutex);
+
+	if (fc_rp_debug)
+		FC_DBG("Received a LOGO response\n");
+
+	if (rdata->rp_state != RPORT_ST_LOGO) {
+		FC_DBG("Received a LOGO response, but in state %s\n",
+		       fc_rport_state(rport));
+		goto out;
+	}
+
 	if (IS_ERR(fp)) {
 		fc_rport_error(rport, fp);
-		return;
+		goto out;
 	}
 
-	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 (rdata->event_callback)
 			rdata->event_callback(lport, rport,
@@ -637,30 +668,41 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 		fc_remote_port_delete(rport);
 	}
 
+out:
+	mutex_unlock(&rdata->rp_mutex);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_enter_prli - Send Process Login (PRLI) request to peer
  * @rport: 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)
 {
+	struct fc_rport_libfc_priv *rdata = rport->dd_data;
+	struct fc_lport *lport = rdata->local_port;
 	struct {
 		struct fc_els_prli prli;
 		struct fc_els_spp spp;
 	} *pp;
 	struct fc_frame *fp;
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-	struct fc_lport *lport = rdata->local_port;
+
+	if (fc_rp_debug)
+		FC_DBG("Port (%6x) entered PRLI state from %s state\n",
+		       rport->port_id, fc_rport_state(rport));
 
 	fc_rport_state_enter(rport, RPORT_ST_PRLI);
 
 	fp = fc_frame_alloc(lport, sizeof(*pp));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rport, 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);
@@ -669,6 +711,7 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
 	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
 	pp->spp.spp_params = htonl(lport->service_params);
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lport->tt.exch_seq_send(lport, fp,
 				     fc_rport_prli_resp,
 				     rport, lport->e_d_tov,
@@ -684,6 +727,10 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
  * @rp_arg: Fibre Channel remote port
  *
  * Many targets don't seem to support this.
+ *
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
  */
 static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 			      void *rp_arg)
@@ -692,12 +739,22 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	u8 op;
 
+	mutex_lock(&rdata->rp_mutex);
+
+	if (fc_rp_debug)
+		FC_DBG("Received a RTV response\n");
+
+	if (rdata->rp_state != RPORT_ST_RTV) {
+		FC_DBG("Received a RTV response, but in state %s\n",
+		       fc_rport_state(rport));
+		goto out;
+	}
+
 	if (IS_ERR(fp)) {
 		fc_rport_error(rport, fp);
-		return;
+		goto out;
 	}
 
-	fc_rport_lock(rport);
 	op = fc_frame_payload_op(fp);
 	if (op == ELS_LS_ACC) {
 		struct fc_els_rtv_acc *rtv;
@@ -721,13 +778,18 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 	}
 
 	fc_rport_enter_ready(rport);
-	fc_rport_unlock(rport);
+
+out:
+	mutex_unlock(&rdata->rp_mutex);
 	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
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 static void fc_rport_enter_rtv(struct fc_rport *rport)
 {
@@ -736,16 +798,23 @@ static void fc_rport_enter_rtv(struct fc_rport *rport)
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
 
+	if (fc_rp_debug)
+		FC_DBG("Port (%6x) entered RTV state from %s state\n",
+		       rport->port_id, fc_rport_state(rport));
+
 	fc_rport_state_enter(rport, RPORT_ST_RTV);
 
 	fp = fc_frame_alloc(lport, sizeof(*rtv));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rport, 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 (!lport->tt.exch_seq_send(lport, fp,
 				     fc_rport_rtv_resp,
 				     rport, lport->e_d_tov,
@@ -757,26 +826,36 @@ static void fc_rport_enter_rtv(struct fc_rport *rport)
 /**
  * fc_rport_enter_logo - Send Logout (LOGO) request to peer
  * @rport: Fibre Channel remote port to send LOGO to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
  */
 static void fc_rport_enter_logo(struct fc_rport *rport)
 {
-	struct fc_frame *fp;
-	struct fc_els_logo *logo;
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
+	struct fc_frame *fp;
+	struct fc_els_logo *logo;
+
+	if (fc_rp_debug)
+		FC_DBG("Port (%6x) entered LOGO state from %s state\n",
+		       rport->port_id, fc_rport_state(rport));
 
 	fc_rport_state_enter(rport, RPORT_ST_LOGO);
 
 	fp = fc_frame_alloc(lport, sizeof(*logo));
-	if (!fp)
-		return fc_rport_error(rport, fp);
+	if (!fp) {
+		fc_rport_error(rport, 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, lport->fid);
 	logo->fl_n_port_wwn = htonll(lport->wwpn);
-
 	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
+
 	if (!lport->tt.exch_seq_send(lport, fp,
 				     fc_rport_logo_resp,
 				     rport, lport->e_d_tov,
@@ -785,10 +864,16 @@ static void fc_rport_enter_logo(struct fc_rport *rport)
 		fc_rport_error(rport, 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 - Receive a request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @rp_arg: Fibre Channel remote port
+ *
+ * Locking Note: Called without the rport lock held. This
+ * function will hold the rport lock, call an _enter_*
+ * function and then unlock the rport.
  */
 void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 		       struct fc_rport *rport)
@@ -800,6 +885,8 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_seq_els_data els_data;
 	u8 op;
 
+	mutex_lock(&rdata->rp_mutex);
+
 	els_data.fp = NULL;
 	els_data.explan = ELS_EXPL_NONE;
 	els_data.reason = ELS_RJT_NONE;
@@ -832,12 +919,12 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 		default:
 			els_data.reason = ELS_RJT_UNSUP;
 			lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
-			fc_frame_free(fp);
 			break;
 		}
-	} else {
-		fc_frame_free(fp);
 	}
+
+	mutex_unlock(&rdata->rp_mutex);
+	fc_frame_free(fp);
 }
 
 /**
@@ -845,6 +932,9 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
  * @rport: Fibre Channel remote port that initiated PLOGI
  * @sp: current sequence in the PLOGI exchange
  * @fp: PLOGI request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
  */
 static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 				    struct fc_seq *sp, struct fc_frame *rx_fp)
@@ -861,9 +951,15 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	u64 wwnn;
 	enum fc_els_rjt_reason reject = 0;
 	u32 f_ctl;
-
 	rjt_data.fp = NULL;
+
 	fh = fc_frame_header_get(fp);
+
+	if (fc_rp_debug)
+		FC_DBG("Received PLOGI request from port (%6x) "
+		       "while in state %s\n", ntoh24(fh->fh_s_id),
+		       fc_rport_state(rport));
+
 	sid = ntoh24(fh->fh_s_id);
 	pl = fc_frame_payload_get(fp, sizeof(*pl));
 	if (!pl) {
@@ -875,7 +971,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);
 
 	/*
 	 * If the session was just created, possibly due to the incoming PLOGI,
@@ -960,7 +1055,6 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 						     RPORT_ST_PLOGI_RECV);
 		}
 	}
-	fc_rport_unlock(rport);
 }
 
 /**
@@ -968,6 +1062,9 @@ 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: The rport lock is exected to be held before calling
+ * this function.
  */
 static void fc_rport_recv_prli_req(struct fc_rport *rport,
 				   struct fc_seq *sp, struct fc_frame *rx_fp)
@@ -992,10 +1089,15 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 	u32 f_ctl;
 	u32 fcp_parm;
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
-
 	rjt_data.fp = NULL;
+
 	fh = fc_frame_header_get(rx_fp);
 
+	if (fc_rp_debug)
+		FC_DBG("Received PRLI request from port (%6x) "
+		       "while in state %s\n", ntoh24(fh->fh_s_id),
+		       fc_rport_state(rport));
+
 	switch (rdata->rp_state) {
 	case RPORT_ST_PLOGI_RECV:
 	case RPORT_ST_PRLI:
@@ -1092,7 +1194,6 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 		/*
 		 * Get lock and re-check state.
 		 */
-		fc_rport_lock(rport);
 		switch (rdata->rp_state) {
 		case RPORT_ST_PLOGI_RECV:
 		case RPORT_ST_PRLI:
@@ -1103,7 +1204,6 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 		default:
 			break;
 		}
-		fc_rport_unlock(rport);
 	}
 	fc_frame_free(rx_fp);
 }
@@ -1113,6 +1213,9 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
  * @rport: Fibre Channel remote port that initiated PRLO
  * @sp: current sequence in the PRLO exchange
  * @fp: PRLO request frame
+ *
+ * Locking Note: The rport lock is exected to be held before calling
+ * this function.
  */
 static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
 				   struct fc_frame *fp)
@@ -1124,8 +1227,12 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
 	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), rdata->rp_state);
+
+	if (fc_rp_debug)
+		FC_DBG("Received PRLO request from port (%6x) "
+		       "while in state %s\n", ntoh24(fh->fh_s_id),
+		       fc_rport_state(rport));
+
 	rjt_data.fp = NULL;
 	rjt_data.reason = ELS_RJT_UNAB;
 	rjt_data.explan = ELS_EXPL_NONE;
@@ -1138,6 +1245,9 @@ 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: The rport lock is exected to be held before calling
+ * this function.
  */
 static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
 				   struct fc_frame *fp)
@@ -1147,11 +1257,11 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
 	struct fc_lport *lport = rdata->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);
+		FC_DBG("Received LOGO request from port (%6x) "
+		       "while in state %s\n", ntoh24(fh->fh_s_id),
+		       fc_rport_state(rport));
 
 	if (rdata->event_callback)
 		rdata->event_callback(lport, rport, LPORT_EV_RPORT_LOGO);
diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h
index fb9e139..9ec715d 100644
--- a/include/scsi/libfc/libfc.h
+++ b/include/scsi/libfc/libfc.h
@@ -142,7 +142,7 @@ struct fc_ns_port {
  * @retries: retry count in current state
  * @e_d_tov: error detect timeout value (in msec)
  * @r_a_tov: resource allocation timeout value (in msec)
- * @rp_lock: lock protects state
+ * @rp_mutex: mutex protects rport
  * @retry_work:
  * @event_callback: Callback for rport READY, FAILED or LOGO
  */
@@ -156,7 +156,7 @@ struct fc_rport_libfc_priv {
 	unsigned int	retries;
 	unsigned int	e_d_tov;
 	unsigned int	r_a_tov;
-	spinlock_t	rp_lock;
+	struct mutex    rp_mutex;
 	struct delayed_work	retry_work;
 	void (*event_callback)(struct fc_lport *,
 			       struct fc_rport *,




More information about the devel mailing list