[Open-FCoE] [RFC PATCH 06/15] libfc: add gnn_id and gpn_id to RP state machine

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


This patch does two things:

1) Makes gnn_id and gpn_id states in the RP state
   machine. This means that rport_login determines what
   information the rport already has and then determines
   what state to go to next.

2) Makes rport_login start from a work thread context.
   Previously both gpn_id and gnn_id were starting from
   work context, since they're now just states of the RP
   the RP is started from the work thread.

NOTES:

1) The rport locking doesn't seem to have a good policy.
   Maybe there is one that I don't understand, but rport
   locking is currently not handled with this patch. However,
   I don't think this patch makes it any worse than it
   it already is.

2) The p2p rport may have been neglected by this patch.
   It will need to be resolved before committing.

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

 drivers/scsi/libfc/fc_lport.c |    8 -
 drivers/scsi/libfc/fc_ns.c    |  308 +-------------------------------
 drivers/scsi/libfc/fc_rport.c |  398 +++++++++++++++++++++++++++++++++++++----
 include/scsi/libfc/libfc.h    |    8 +
 4 files changed, 383 insertions(+), 339 deletions(-)

diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index f9f5ce3..2d90b86 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -796,8 +796,10 @@ fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
 	unsigned int e_d_tov;
 	u16 mfs;
 
-	if (IS_ERR(fp))
-		goto out;
+	if (IS_ERR(fp)) {
+		fc_lport_error(lp, fp);
+		return;
+	}
 
 	fh = fc_frame_header_get(fp);
 	did = ntoh24(fh->fh_d_id);
@@ -846,8 +848,6 @@ fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
 		FC_DBG("bad FLOGI response\n");
 	}
 	fc_frame_free(fp);
-out:
-	fc_lport_error(lp, fp);
 }
 
 /*
diff --git a/drivers/scsi/libfc/fc_ns.c b/drivers/scsi/libfc/fc_ns.c
index 8145e1c..7541354 100644
--- a/drivers/scsi/libfc/fc_ns.c
+++ b/drivers/scsi/libfc/fc_ns.c
@@ -44,13 +44,6 @@ static void fc_ns_disc_done(struct fc_lport *);
 static void fcdt_ns_error(struct fc_lport *, struct fc_frame *);
 static void fc_ns_timeout(struct work_struct *);
 
-static void fc_ns_gpn_id_req(struct work_struct *);
-static void fc_ns_gpn_id_resp(struct fc_seq *, struct fc_frame *, void *);
-static void fc_ns_gpn_id_error(struct fc_rport *, struct fc_frame *);
-
-static void fc_ns_gnn_id_req(struct work_struct *);
-static void fc_ns_gnn_id_resp(struct fc_seq *, struct fc_frame *, void *);
-static void fc_ns_gnn_id_error(struct fc_rport *, struct fc_frame *);
 static void fc_ns_enter_reg_pn(struct fc_lport *lp);
 static void fc_ns_error(struct fc_lport *lp, struct fc_frame *fp);
 static void fc_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
@@ -59,33 +52,6 @@ static void fc_ns_retry(struct fc_lport *lp);
 static void fc_ns_single(struct fc_lport *, struct fc_ns_port *);
 static int fc_ns_restart(struct fc_lport *);
 
-struct fc_rport *fc_ns_create_dummy_rport(struct fc_ns_port *dp)
-{
-	struct fc_rport *rp;
-	struct fc_rport_libfc_priv *rpp;
-	rp = kzalloc(sizeof(*rp) + sizeof(*rpp), GFP_KERNEL);
-	rpp = ((void *)rp + sizeof(struct fc_rport));
-
-	rp->dd_data = rpp;
-	rp->port_id = dp->ids.port_id;
-	rp->port_name = dp->ids.port_name;
-	rp->node_name = dp->ids.node_name;
-	rp->roles = dp->ids.roles;
-
-	spin_lock_init(&rpp->rp_lock);
-	rpp->local_port = dp->lp;
-	rpp->rp_state = RPORT_ST_INIT;
-	rpp->flags = FC_RP_FLAGS_REC_SUPPORTED;
-	INIT_DELAYED_WORK(&rpp->retry_work, fc_ns_timeout);
-
-	return rp;
-}
-
-void fc_ns_destroy_dummy_rport(struct fc_rport *rp)
-{
-	kfree(rp);
-}
-
 /**
  * fc_ns_rscn_req - Handle Registered State Change Notification (RSCN)
  * @sp: Current sequence of the RSCN exchange
@@ -617,15 +583,18 @@ static int fc_ns_new_target(struct fc_lport *lp,
 	    ids->port_id != lp->fid && ids->port_name != lp->wwpn) {
 		if (!rport) {
 			rport = lp->tt.rport_lookup(lp, ids->port_id);
-			if (rport == NULL)
+			if (!rport)
 				rport = lp->tt.rport_create(lp, ids);
+
 			if (!rport)
 				error = ENOMEM;
 		}
 		if (rport) {
 			rp = rport->dd_data;
 			rp->rp_state = RPORT_ST_INIT;
-			lp->tt.rport_login(rport);
+
+			INIT_WORK(&rp->login_work, lp->tt.rport_login);
+			schedule_work(&rp->login_work);
 		}
 	}
 	return error;
@@ -776,11 +745,11 @@ static int fc_ns_gpn_ft_parse(struct fc_lport *lp, void *buf, size_t len)
 		dp->ids.node_name = -1;
 		dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
 
-		rp = fc_ns_create_dummy_rport(dp);
+		rp = fc_rport_create_dummy(dp);
 		rpp = rp->dd_data;
 		kfree(dp);
 
-		INIT_WORK(&rpp->login_work, fc_ns_gnn_id_req);
+		INIT_WORK(&rpp->login_work, lp->tt.rport_login);
 		schedule_work(&rpp->login_work);
 
 		if (np->fp_flags & FC_NS_FID_LAST) {
@@ -908,137 +877,17 @@ static void fc_ns_single(struct fc_lport *lp, struct fc_ns_port *dp)
 		put_device(&rport->dev); /* hold from lookup */
 	}
 
-	rp = fc_ns_create_dummy_rport(dp);
+	rp = fc_rport_create_dummy(dp);
 	rpp = rp->dd_data;
 	kfree(dp);
 
-	INIT_WORK(&rpp->login_work, fc_ns_gpn_id_req);
+	INIT_WORK(&rpp->login_work, lp->tt.rport_login);
 	schedule_work(&rpp->login_work);
 	return;
 out:
 	kfree(dp);
 }
 
-/**
- * fc_ns_gpn_id_req - Send Get Port Name by ID (GPN_ID) request
- * @work: The work member of the fc_ns_port structure
- *
- * XXX - this the following statement still valid?
- * The remote port is held by the caller for us.
- */
-static void fc_ns_gpn_id_req(struct work_struct *work)
-{
-	struct fc_rport_libfc_priv *rpp;
-	struct fc_rport *rp;
-	struct fc_lport *lp;
-	struct fc_frame *fp;
-	struct req {
-		struct fc_ct_hdr ct;
-		struct fc_ns_fid fid;
-	} *cp;
-
-	rpp = container_of(work,
-			   struct fc_rport_libfc_priv,
-			   login_work);
-	rp = (((void *)rpp) - sizeof(struct fc_rport));
-
-	lp = rpp->local_port;
-
-	fp = fc_frame_alloc(lp, sizeof(*cp));
-	if (!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);
-	lp->tt.exch_seq_send(lp, fp,
-			     fc_ns_gpn_id_resp,
-			     rp, lp->e_d_tov,
-			     lp->fid,
-			     lp->dns_rp->port_id,
-			     FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-}
-
-/**
- * fc_ns_gpn_id_resp - Handle response to GPN_ID
- * @sp: Current sequence of GPN_ID exchange
- * @fp: response frame
- * @dp_arg: Temporary discovery port for holding IDs and world wide names
- */
-static void fc_ns_gpn_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 = rp->dd_data;
-
-	struct fc_lport *lp;
-	struct resp {
-		struct fc_ct_hdr ct;
-		__be64 wwn;
-	} *cp;
-	unsigned int cmd;
-
-	if (IS_ERR(fp)) {
-		fc_ns_gpn_id_error(rp, fp);
-		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) {
-		FC_DBG("GPN_ID response too short.  len %d", fr_len(fp));
-		return;
-	}
-	cmd = ntohs(cp->ct.ct_cmd);
-	switch (cmd) {
-	case FC_FS_ACC:
-		cp = fc_frame_payload_get(fp, sizeof(*cp));
-		if (cp == NULL) {
-			FC_DBG("GPN_ID response payload too short.  len %d",
-			       fr_len(fp));
-			break;
-		}
-		rp->port_name = ntohll(cp->wwn);
-
-		INIT_WORK(&rpp->login_work, fc_ns_gnn_id_req);
-		schedule_work(&rpp->login_work);
-		break;
-	case FC_FS_RJT:
-		fc_ns_restart(lp);
-		break;
-	default:
-		FC_DBG("GPN_ID unexpected CT response cmd %x\n", cmd);
-		break;
-	}
-	fc_frame_free(fp);
-}
-
-/**
- * fc_ns_gpn_id_error - Handle error from GPN_ID
- * @dp: Temporary discovery port for holding IDs and world wide names
- * @fp: response frame
- */
-static void fc_ns_gpn_id_error(struct fc_rport *rp, struct fc_frame *fp)
-{
-	struct fc_rport_libfc_priv *rpp = rp->dd_data;
-	struct fc_lport *lp = rpp->local_port;
-
-	switch (PTR_ERR(fp)) {
-	case -FC_EX_TIMEOUT:
-		fc_ns_restart(lp);
-		break;
-	case -FC_EX_CLOSED:
-	default:
-		break;
-	}
-}
-
 /*
  * Setup session to dNS if not already set up.
  */
@@ -1069,8 +918,9 @@ static void fc_ns_enter_dns(struct fc_lport *lp)
 		 */
 		fc_lport_unlock(lp);
 		rport = lp->tt.rport_lookup(lp, ids.port_id);
-		if (rport == NULL)
+		if (!rport)
 			rport = lp->tt.rport_create(lp, &ids);
+
 		fc_lport_lock(lp);
 		if (!rport)
 			goto err;
@@ -1084,7 +934,8 @@ static void fc_ns_enter_dns(struct fc_lport *lp)
 	 * If dNS session isn't ready, start its logon.
 	 */
 	if (rp->rp_state != RPORT_ST_READY) {
-		lp->tt.rport_login(rport);
+		INIT_WORK(&rp->login_work, lp->tt.rport_login);
+		schedule_work(&rp->login_work);
 	} else {
 		del_timer(&lp->state_timer);
 		fc_ns_enter_reg_pn(lp);
@@ -1171,136 +1022,3 @@ int fc_ns_init(struct fc_lport *lp)
 	return 0;
 }
 EXPORT_SYMBOL(fc_ns_init);
-
-/**
- * fc_ns_gnn_id_req - 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
- *
- * XXX- Is the following statement still true?
- * The remote port is held by the caller for us.
- */
-static void fc_ns_gnn_id_req(struct work_struct *work)
-{
-	struct fc_rport_libfc_priv *rpp;
-	struct fc_rport *rp;
-	struct fc_lport *lp;
-	struct fc_frame *fp;
-	struct req {
-		struct fc_ct_hdr ct;
-		struct fc_ns_fid fid;
-	} *cp;
-
-	rpp = container_of(work,
-			   struct fc_rport_libfc_priv,
-			   login_work);
-
-	rp = (((void *)rpp) - sizeof(struct fc_rport));
-	lp = rpp->local_port;
-
-	fp = fc_frame_alloc(lp, sizeof(*cp));
-	if (!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);
-	lp->tt.exch_seq_send(lp, fp,
-			     fc_ns_gnn_id_resp,
-			     rp, lp->e_d_tov,
-			     lp->fid,
-			     lp->dns_rp->port_id,
-			     FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-}
-
-/**
- * fc_ns_gnn_id_resp - Handle response to GNN_ID
- * @sp: Current sequence of GNN_ID exchange
- * @fp: response frame
- * @dp_arg: Temporary discovery port for holding IDs and world wide names
- */
-static void fc_ns_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_identifiers ids;
-
-	struct resp {
-		struct fc_ct_hdr ct;
-		__be64 wwn;
-	} *cp;
-	unsigned int cmd;
-
-	rpp = rp->dd_data;
-
-	if (IS_ERR(fp)) {
-		fc_ns_gnn_id_error(rp, fp);
-		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) {
-		FC_DBG("GNN_ID response too short.  len %d", fr_len(fp));
-		return;
-	}
-
-	cmd = ntohs(cp->ct.ct_cmd);
-	switch (cmd) {
-	case FC_FS_ACC:
-		cp = fc_frame_payload_get(fp, sizeof(*cp));
-		if (cp == NULL) {
-			FC_DBG("GNN_ID response payload too short.  len %d",
-			       fr_len(fp));
-			break;
-		}
-		rp->node_name = ntohll(cp->wwn);
-
-		ids.port_id = rp->port_id;
-		ids.port_name = rp->port_name;
-		ids.node_name = rp->node_name;
-		ids.roles = rp->roles;
-
-		fc_ns_destroy_dummy_rport(rp);
-
-		fc_ns_new_target(lp, NULL, &ids);
-		break;
-	case FC_FS_RJT:
-		fc_ns_restart(lp);
-		break;
-	default:
-		FC_DBG("GNN_ID unexpected CT response cmd %x\n", cmd);
-		break;
-	}
-
-	fc_frame_free(fp);
-}
-
-/**
- * fc_ns_gnn_id_error - Handle error from GNN_ID
- * @dp: Temporary discovery port for holding IDs and world wide names
- * @fp: response frame
- */
-static void fc_ns_gnn_id_error(struct fc_rport *rp, struct fc_frame *fp)
-{
-	struct fc_rport_libfc_priv *rpp = rp->dd_data;
-	struct fc_lport *lp = rpp->local_port;
-
-	switch (PTR_ERR(fp)) {
-	case -FC_EX_TIMEOUT:
-		fc_ns_restart(lp);
-		break;
-	case -FC_EX_CLOSED:
-	default:
-		break;
-	}
-}
-
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 92e0536..2e7eff7 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -39,6 +39,14 @@ static int fc_rp_debug;
 /*
  * static functions.
  */
+static void fc_rport_gpn_id_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_rport_gpn_id_error(struct fc_rport *, struct fc_frame *);
+
+static void fc_rport_gnn_id_resp(struct fc_seq *, struct fc_frame *, void *);
+static void fc_rport_gnn_id_error(struct fc_rport *, struct fc_frame *);
+
+static void fc_rport_enter_gpn_id(struct fc_rport *);
+static void fc_rport_enter_gnn_id(struct fc_rport *);
 static void fc_rport_enter_plogi(struct fc_rport *);
 static void fc_rport_enter_prli(struct fc_rport *);
 static void fc_rport_enter_rtv(struct fc_rport *);
@@ -56,6 +64,33 @@ static void fc_rport_timeout(struct work_struct *);
 static struct fc_rport *fc_remote_port_create(struct fc_lport *,
 					      struct fc_rport_identifiers *);
 
+struct fc_rport *fc_rport_create_dummy(struct fc_ns_port *dp)
+{
+	struct fc_rport *rp;
+	struct fc_rport_libfc_priv *rpp;
+	rp = kzalloc(sizeof(*rp) + sizeof(*rpp), GFP_KERNEL);
+	rpp = ((void *)rp + sizeof(struct fc_rport));
+
+	rp->dd_data = rpp;
+	rp->port_id = dp->ids.port_id;
+	rp->port_name = dp->ids.port_name;
+	rp->node_name = dp->ids.node_name;
+	rp->roles = dp->ids.roles;
+
+	spin_lock_init(&rpp->rp_lock);
+	rpp->local_port = dp->lp;
+	rpp->rp_state = RPORT_ST_INIT;
+	rpp->flags = FC_RP_FLAGS_REC_SUPPORTED;
+	INIT_DELAYED_WORK(&rpp->retry_work, fc_rport_timeout);
+
+	return rp;
+}
+
+void fc_rport_destroy_dummy(struct fc_rport *rp)
+{
+	kfree(rp);
+}
+
 /**
  * fc_rport_lookup - lookup a remote port by port_id
  * @lp: Fibre Channel host port instance
@@ -124,6 +159,286 @@ static inline void fc_rport_unlock(struct fc_rport *rport)
 	spin_unlock_bh(&rp->rp_lock);
 }
 
+static void fc_rport_state_enter(struct fc_rport *rport,
+				 enum fc_rport_state new)
+{
+	struct fc_rport_libfc_priv *rp = rport->dd_data;
+	if (rp->rp_state != new)
+		rp->retries = 0;
+	rp->rp_state = new;
+}
+
+/**
+ * fc_rport_enter_gpn_id - Send Get Port Name by ID (GPN_ID) request
+ * @work: The work member of the fc_ns_port structure
+ *
+ * XXX - this the following statement still valid?
+ * The remote port is held by the caller for us.
+ */
+void fc_rport_enter_gpn_id(struct fc_rport *rp)
+{
+	struct fc_rport_libfc_priv *rpp;
+	struct fc_lport *lp;
+	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)
+		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);
+	lp->tt.exch_seq_send(lp, fp,
+			     fc_rport_gpn_id_resp,
+			     rp, lp->e_d_tov,
+			     lp->fid,
+			     lp->dns_rp->port_id,
+			     FC_FC_SEQ_INIT | FC_FC_END_SEQ);
+}
+
+/**
+ * fc_ns_gpn_id_resp - Handle response to GPN_ID
+ * @sp: Current sequence of GPN_ID exchange
+ * @fp: response frame
+ * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ */
+static void fc_rport_gpn_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 = rp->dd_data;
+
+	struct fc_lport *lp;
+	struct resp {
+		struct fc_ct_hdr ct;
+		__be64 wwn;
+	} *cp;
+	unsigned int cmd;
+
+	if (IS_ERR(fp)) {
+		fc_rport_gpn_id_error(rp, fp);
+		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) {
+		FC_DBG("GPN_ID response too short.  len %d", fr_len(fp));
+		return;
+	}
+	cmd = ntohs(cp->ct.ct_cmd);
+	switch (cmd) {
+	case FC_FS_ACC:
+		cp = fc_frame_payload_get(fp, sizeof(*cp));
+		if (cp == NULL) {
+			FC_DBG("GPN_ID response payload too short.  len %d",
+			       fr_len(fp));
+			break;
+		}
+		rp->port_name = ntohll(cp->wwn);
+
+		fc_rport_enter_gnn_id(rp);
+		break;
+	case FC_FS_RJT:
+		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
+		fc_rport_destroy_dummy(rp);
+		break;
+	default:
+		FC_DBG("GPN_ID unexpected CT response cmd %x\n", cmd);
+		break;
+	}
+	fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_gpn_id_error - Handle error from GPN_ID
+ * @dp: Temporary discovery port for holding IDs and world wide names
+ * @fp: response frame
+ */
+static void fc_rport_gpn_id_error(struct fc_rport *rp, struct fc_frame *fp)
+{
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
+
+	switch (PTR_ERR(fp)) {
+	case -FC_EX_TIMEOUT:
+		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
+		fc_rport_destroy_dummy(rp);
+		break;
+	case -FC_EX_CLOSED:
+	default:
+		break;
+	}
+}
+
+/**
+ * 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
+ *
+ * XXX- Is the following statement still true?
+ * The remote port is held by the caller for us.
+ */
+void fc_rport_enter_gnn_id(struct fc_rport *rp)
+{
+	struct fc_rport_libfc_priv *rpp;
+	struct fc_lport *lp;
+	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;
+
+	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)
+		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);
+	lp->tt.exch_seq_send(lp, fp,
+			     fc_rport_gnn_id_resp,
+			     rp, lp->e_d_tov,
+			     lp->fid,
+			     lp->dns_rp->port_id,
+			     FC_FC_SEQ_INIT | FC_FC_END_SEQ);
+}
+
+/**
+ * fc_ns_gnn_id_resp - Handle response to GNN_ID
+ * @sp: Current sequence of GNN_ID exchange
+ * @fp: response frame
+ * @dp_arg: Temporary discovery port for holding IDs and world wide names
+ */
+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_identifiers ids;
+
+	struct resp {
+		struct fc_ct_hdr ct;
+		__be64 wwn;
+	} *cp;
+	unsigned int cmd;
+
+	rpp = rp->dd_data;
+
+	if (IS_ERR(fp)) {
+		fc_rport_gnn_id_error(rp, fp);
+		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) {
+		FC_DBG("GNN_ID response too short.  len %d", fr_len(fp));
+		return;
+	}
+
+	cmd = ntohs(cp->ct.ct_cmd);
+	switch (cmd) {
+	case FC_FS_ACC:
+		cp = fc_frame_payload_get(fp, sizeof(*cp));
+		if (cp == NULL) {
+			FC_DBG("GNN_ID response payload too short.  len %d",
+			       fr_len(fp));
+			break;
+		}
+		rp->node_name = ntohll(cp->wwn);
+
+		ids.port_id = rp->port_id;
+		ids.port_name = rp->port_name;
+		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);
+
+			if (!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);
+		break;
+	default:
+		FC_DBG("GNN_ID unexpected CT response cmd %x\n", cmd);
+		break;
+	}
+
+	fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_gnn_id_error - Handle error from GNN_ID
+ * @dp: Temporary discovery port for holding IDs and world wide names
+ * @fp: response frame
+ */
+static void fc_rport_gnn_id_error(struct fc_rport *rp, struct fc_frame *fp)
+{
+	struct fc_rport_libfc_priv *rpp = rp->dd_data;
+	struct fc_lport *lp = rpp->local_port;
+
+	switch (PTR_ERR(fp)) {
+	case -FC_EX_TIMEOUT:
+		lp->tt.lport_event_callback(lp, rp, LPORT_EV_RPORT_FAILED);
+		fc_rport_destroy_dummy(rp);
+		break;
+	case -FC_EX_CLOSED:
+	default:
+		break;
+	}
+}
+
 /**
  * fc_plogi_get_maxframe - Get max payload from the common service parameters
  * @flp: FLOGI payload structure
@@ -184,54 +499,40 @@ fc_lport_plogi_fill(struct fc_lport *lp,
 	}
 }
 
-static void fc_rport_state_enter(struct fc_rport *rport,
-				 enum fc_rport_state new)
-{
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	if (rp->rp_state != new)
-		rp->retries = 0;
-	rp->rp_state = new;
-}
-
 /**
  * fc_rport_login - Start the remote port login state machine
- * @rport: Fibre Channel remote port
+ * @work: member of the fc_rport_libfc_priv structure
  */
-int fc_rport_login(struct fc_rport *rport)
+void fc_rport_login(struct work_struct *work)
 {
-	struct fc_rport_libfc_priv *rp = rport->dd_data;
-	struct fc_lport *lp = rp->local_port;
+	struct fc_rport *rport;
+	struct fc_rport_libfc_priv *rp;
+	struct fc_lport *lp;
+
+	rp = container_of(work,
+			  struct fc_rport_libfc_priv,
+			  login_work);
+
+	rport = (((void *)rp) - sizeof(struct fc_rport));
+	lp = rp->local_port;
 
 	fc_rport_lock(rport);
-	if (rp->rp_state == RPORT_ST_INIT) {
-		fc_rport_unlock(rport);
-		if (rport == lp->dns_rp || fc_lport_test_ready(lp))
-			fc_rport_enter_plogi(rport);
-	} else if (rp->rp_state == RPORT_ST_ERROR) {
+	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", rport->port_id);
+			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);
-		}
-	} else
+		lp->tt.lport_event_callback(lp, rport, LPORT_EV_RPORT_FAILED);
+	} else {
 		fc_rport_unlock(rport);
-
-	return 0;
+		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);
+	}
 }
 
 /*
@@ -337,6 +638,8 @@ static void fc_rport_reject(struct fc_rport *rport)
 	struct fc_rport_libfc_priv *rp = rport->dd_data;
 	struct fc_lport *lp = rp->local_port;
 	switch (rp->rp_state) {
+	case RPORT_ST_GPN_ID:
+	case RPORT_ST_GNN_ID:
 	case RPORT_ST_PLOGI:
 	case RPORT_ST_PRLI:
 		fc_rport_state_enter(rport, RPORT_ST_ERROR);
@@ -400,6 +703,12 @@ static void fc_rport_timeout(struct work_struct *work)
 	struct fc_rport *rport = (((void *)rp) - sizeof(struct fc_rport));
 
 	switch (rp->rp_state) {
+	case RPORT_ST_GPN_ID:
+		fc_rport_enter_gpn_id(rport);
+		break;
+	case RPORT_ST_GNN_ID:
+		fc_rport_enter_gnn_id(rport);
+		break;
 	case RPORT_ST_PLOGI:
 		fc_rport_enter_plogi(rport);
 		break;
@@ -537,6 +846,9 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
 	struct fc_lport *lp;
 	struct fc_rport_libfc_priv *rp = rport->dd_data;
 
+	if (fc_rp_debug)
+		FC_DBG("Entered PLOGI for port (%6x)\n", rport->port_id);
+
 	lp = rp->local_port;
 	fc_rport_state_enter(rport, RPORT_ST_PLOGI);
 	rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
@@ -600,6 +912,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 		fc_rport_enter_rtv(rport);
 		fc_rport_unlock(rport);
+
 		fc_remote_port_rolechg(rport, roles);
 	} else {
 		FC_DBG("bad ELS response\n");
@@ -683,6 +996,9 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
 	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(rport, RPORT_ST_PRLI);
 
 	/*
@@ -784,6 +1100,10 @@ static void fc_rport_enter_rtv(struct fc_rport *rport)
 	struct fc_rport_libfc_priv *rp = rport->dd_data;
 
 	lp = rp->local_port;
+
+	if (fc_rp_debug)
+		FC_DBG("Entered RTV for port (%6x)\n", rport->port_id);
+
 	fc_rport_state_enter(rport, RPORT_ST_RTV);
 
 	fp = fc_frame_alloc(lp, sizeof(*rtv));
@@ -814,6 +1134,9 @@ static void fc_rport_enter_logo(struct fc_rport *rport)
 	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(rport, RPORT_ST_LOGO);
 
 	lp = rp->local_port;
@@ -1116,6 +1439,7 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 					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);
 				spp->spp_params =
 					htonl(rp->local_port->service_params);
diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h
index 1966a38..74ff4d1 100644
--- a/include/scsi/libfc/libfc.h
+++ b/include/scsi/libfc/libfc.h
@@ -109,6 +109,8 @@ enum fc_lport_event {
 enum fc_rport_state {
 	RPORT_ST_NONE = 0,
 	RPORT_ST_INIT,		/* initialized */
+	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 */
@@ -159,8 +161,8 @@ struct fc_rport_libfc_priv {
 	struct work_struct      login_work;
 };
 
-struct fc_rport *fc_ns_create_dummy_rport(struct fc_ns_port *);
-void fc_ns_destroy_dummy_rport(struct fc_rport *);
+struct fc_rport *fc_rport_create_dummy(struct fc_ns_port *);
+void fc_rport_destroy_dummy(struct fc_rport *);
 
 static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
 {
@@ -353,7 +355,7 @@ struct libfc_function_template {
 	 * - PRLI
 	 * - RTV
 	 */
-	int (*rport_login)(struct fc_rport *rport);
+	void (*rport_login)(struct work_struct *work);
 
 	/*
 	 * Logs the specified local port out of a N_Port identified




More information about the devel mailing list