[Open-FCoE] [PATCH 1/1] fc class: add rogue rport support

James Smart James.Smart at Emulex.Com
Fri Sep 19 18:36:00 UTC 2008


Mike,

Spent some more time going through it. There were some additional bugs
in the code paths:
- left over references on the rport from add's searching and matching
- no moves off bindings list to rports lists
- no freeing of rogues on fc_remove_host
- dd_data handling not good
- leaked rogue rports on add matches

I reworked the patch and attached below. This still has one glaring
issue:
  Cleanup if transport/device routines fail leaks structures and
  reference counts.
(Note: I have done very little for this patch. I have only compiled it.
 and the patch was cut against scsi-misc-2.6)

I think it is now consistent enough to use, but I don't know that I like
everything in the model. It certainly needs documentation. Let me give
some background:

The original rport model was:
  1, call fc_remote_port_add() when you know a port exists. You will
     have WWN's, and address information. You may know role as well.
  2. If role changes, call fc_remote_port_rolechg()
  3. If you loose connectivity to the rport, call
     fc_remote_port_delete().
     This blocks the rport. From the point of view of the driver, the
     rport doesn't exist anymore, and the rport shouldn't be accessed.
     We cheat, in that accesses via scsi, etc will access the rport,
     but should be bounced by common functions (chkready, etc).
  4. If the transport rediscovers the device, it will call
     fc_remote_port_add(), which will remove all the block state, and
     happen to return the same rport structure the device had before.
  5. If the device isn't rediscovered, the block timeouts expired, 
     the device is torn down, the driver finally/officially notified
     that the rport is destroyed (via the dev_loss_tmo_callbk), and
     the rport is lost - excepting when it's kept around as a shell
     to hold the scsi target id and its binding value.

The above has a couple of headaches:
- The lldd's are forced to use their own data structures for the initial
  discovery ELS's, as rport->dd_data doesn't exist until _add is called.
- The "shouldn't access the rport" after _delete, but prior to the 
  callbk is hokey.

With the patch below, here's the new model:
  1. fc_remote_port_alloc() can be called to allocate an rport, mainly
     for the dd_data structure. This rport has no context or real
     meaning.
  2. As long as the rport is never passed to fc_remote_port_add(), the
     LLDD can free it at any time via fc_remote_port_free().
  3. After using the dd_data for internal state, and obtaining the names
     and address, fc_remote_port_add() can be called, passing the rport
     structure.
       The _add, will fail if an active non-blocked port exists already.
         (the driver must resolve this)
       The _add, will either:
        - If the device exists but is blocked, the blocked rport is
          unblocked, and the dd_data of the rogue port copied to the
          older rport. The older rport remains on the active list.
          Essentially, the rogue rport is replaced by the old-blocked
          rport.  The rogue rport is free'd.
        - If the device no longer exists, but there is a bindings shell,
          we likewise use the older bindings shell for the rport, copy
          the dd_data, move it to the active list, and free the rogue
          rport. In truth, it really wouldn't have mattered if we
          simply copied the scsi_target_id over to the rogue rport,
          then freed the bindings shell, and returned the rogue rport.
        - if no rport existed per above, we use the rogue rport and
          officially move it to the active list.
     The result of the _add() is that the lldd is given a new rport
     structure, and the lldd is to nolonger reference the rogue
     rport. (even though the new rport may actually be the same as
     the rogue).
  4  Steps 2-5 from above continue to apply

  Note: The above is compatible with the old behavior, as long as
   a NULL rogue rport pointer is passed in.

The difficulty in the new model is that the lldd must be careful. Once
it calls _delete on an rport, it shouldn't reference it again. If the
dd_data was on internal lists, etc, it really should have been removed 
from them. For all intents and purposes, as of delete, it was freed.
This is the part that is concerning.

Additionally, it is still up to the LLDD to resolve overlaps due to
address changes. Here's some general flow:
 - about to discovery what is at an address (ADISC/PDISC/PLOGI
   whatever),
   so allocate an rport for it's dd_data.
 - get the WWN's for the thing at the address.
 - call fc_remote_port_lookup() - once for the address, once for the WWN
 - If NULL rports for both - device is new, call _add()
 - If both respond, the rports should match - update the rport->dd_data
   state and free the rogue rport.
 - If an rport for 1 but not the other, or both and they mismatch,
   call _delete() on both, then call add with the rogue rport.

This should explain why the 2 search routines, one for the transport 
internally, and another for the LLDD, with the LLDD only returning 
things that are active and unblocked (no _delete call made).



-- james




 Signed-off-by: James Smart <james.smart at emulex.com>

 ---

 drivers/scsi/lpfc/lpfc_hbadisc.c |    2 
 drivers/scsi/qla2xxx/qla_init.c  |    2 
 drivers/scsi/scsi_transport_fc.c |  498 ++++++++++++++++++++++++---------------
 include/scsi/scsi_transport_fc.h |   26 +-
 4 files changed, 345 insertions(+), 183 deletions(-)


diff -upNr a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c	2008-09-06 08:12:22.000000000 -0400
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c	2008-09-19 14:26:00.000000000 -0400
@@ -1442,7 +1442,7 @@ lpfc_register_remote_port(struct lpfc_vp
 		"rport add:       did:x%x flg:x%x type x%x",
 		ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_type);
 
-	ndlp->rport = rport = fc_remote_port_add(shost, 0, &rport_ids);
+	ndlp->rport = rport = fc_remote_port_add(shost, 0, NULL, &rport_ids);
 	if (!rport || !get_device(&rport->dev)) {
 		dev_printk(KERN_WARNING, &phba->pcidev->dev,
 			   "Warning: fc_remote_port_add failed\n");
diff -upNr a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
--- a/drivers/scsi/qla2xxx/qla_init.c	2008-08-23 10:50:17.000000000 -0400
+++ b/drivers/scsi/qla2xxx/qla_init.c	2008-09-19 14:26:30.000000000 -0400
@@ -2267,7 +2267,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t 
 	rport_ids.port_id = fcport->d_id.b.domain << 16 |
 	    fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
 	rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
-	fcport->rport = rport = fc_remote_port_add(ha->host, 0, &rport_ids);
+	fcport->rport = rport = fc_remote_port_add(ha->host, 0, NULL, &rport_ids);
 	if (!rport) {
 		qla_printk(KERN_WARNING, ha,
 		    "Unable to allocate fc remote port!\n");
diff -upNr a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
--- a/drivers/scsi/scsi_transport_fc.c	2008-09-06 08:12:23.000000000 -0400
+++ b/drivers/scsi/scsi_transport_fc.c	2008-09-19 14:24:29.000000000 -0400
@@ -144,6 +144,7 @@ static struct {
 	{ FC_PORTSTATE_ERROR,		"Error" },
 	{ FC_PORTSTATE_LOOPBACK,	"Loopback" },
 	{ FC_PORTSTATE_DELETED,		"Deleted" },
+	{ FC_PORTSTATE_ROGUE,		"Rogue" },
 };
 fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
 #define FC_PORTSTATE_MAX_NAMELEN	20
@@ -387,6 +388,7 @@ static int fc_host_setup(struct transpor
 
 	INIT_LIST_HEAD(&fc_host->rports);
 	INIT_LIST_HEAD(&fc_host->rport_bindings);
+	INIT_LIST_HEAD(&fc_host->rogue_rports);
 	INIT_LIST_HEAD(&fc_host->vports);
 	fc_host->next_rport_number = 0;
 	fc_host->next_target_id = 0;
@@ -1518,7 +1520,7 @@ store_fc_private_host_tgtid_bind_type(st
 		while (!list_empty(&fc_host_rport_bindings(shost))) {
 			get_list_head_entry(rport,
 				&fc_host_rport_bindings(shost), peers);
-			list_del(&rport->peers);
+			list_del_init(&rport->peers);
 			rport->port_state = FC_PORTSTATE_DELETED;
 			fc_queue_work(shost, &rport->rport_delete_work);
 		}
@@ -2295,14 +2297,21 @@ fc_remove_host(struct Scsi_Host *shost)
 	/* Remove any remote ports */
 	list_for_each_entry_safe(rport, next_rport,
 			&fc_host->rports, peers) {
-		list_del(&rport->peers);
+		list_del_init(&rport->peers);
 		rport->port_state = FC_PORTSTATE_DELETED;
 		fc_queue_work(shost, &rport->rport_delete_work);
 	}
 
 	list_for_each_entry_safe(rport, next_rport,
 			&fc_host->rport_bindings, peers) {
-		list_del(&rport->peers);
+		list_del_init(&rport->peers);
+		rport->port_state = FC_PORTSTATE_DELETED;
+		fc_queue_work(shost, &rport->rport_delete_work);
+	}
+
+	list_for_each_entry_safe(rport, next_rport,
+			&fc_host->rogue_rports, peers) {
+		list_del_init(&rport->peers);
 		rport->port_state = FC_PORTSTATE_DELETED;
 		fc_queue_work(shost, &rport->rport_delete_work);
 	}
@@ -2351,6 +2360,23 @@ fc_starget_delete(struct work_struct *wo
 }
 
 
+void 
+__free_remote_port(struct fc_rport *rport)
+{
+	struct Scsi_Host *shost = rport_to_shost(rport);
+	unsigned long flags;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	if (!list_empty(&rport->peers))
+		list_del(&rport->peers);
+	put_device(&shost->shost_gendev); /* for fc_host->*rports list */
+	spin_unlock_irqrestore(shost->host_lock, flags);
+
+	/* for self-reference */
+	put_device(&rport->dev);
+}
+
+
 /**
  * fc_rport_final_delete - finish rport termination and delete it.
  * @work:	remote port to be deleted.
@@ -2407,51 +2433,43 @@ fc_rport_final_delete(struct work_struct
 	transport_remove_device(dev);
 	device_del(dev);
 	transport_destroy_device(dev);
-	put_device(&shost->shost_gendev);	/* for fc_host->rport list */
-	put_device(dev);			/* for self-reference */
+	__free_remote_port(rport);
 }
 
 
 /**
- * fc_rport_create - allocates and creates a remote FC port.
+ * fc_remote_port_alloc - allocates a remote FC port.
  * @shost:	scsi host the remote port is connected to.
  * @channel:	Channel on shost port connected to.
- * @ids:	The world wide names, fc address, and FC4 port
- *		roles for the remote port.
  *
- * Allocates and creates the remoter port structure, including the
- * class and sysfs creation.
+ * Allocates the remoter port structure. It has no binding
+ * and no sysfs representation at this time. It is just used to send
+ * discovery requests through.
  *
  * Notes:
  *	This routine assumes no locks are held on entry.
  */
-static struct fc_rport *
-fc_rport_create(struct Scsi_Host *shost, int channel,
-	struct fc_rport_identifiers  *ids)
+struct fc_rport *
+fc_remote_port_alloc(struct Scsi_Host *shost, int channel)
 {
 	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 	struct fc_internal *fci = to_fc_internal(shost->transportt);
 	struct fc_rport *rport;
 	struct device *dev;
 	unsigned long flags;
-	int error;
-	size_t size;
 
-	size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size);
-	rport = kzalloc(size, GFP_KERNEL);
-	if (unlikely(!rport)) {
+	rport = kzalloc(sizeof(struct fc_rport) + fci->f->dd_fcrport_size,
+			GFP_KERNEL);
+	if (!rport) {
 		printk(KERN_ERR "%s: allocation failure\n", __func__);
 		return NULL;
 	}
 
 	rport->maxframe_size = -1;
 	rport->supported_classes = FC_COS_UNSPECIFIED;
+	rport->roles = FC_PORT_ROLE_UNKNOWN;
 	rport->dev_loss_tmo = fc_dev_loss_tmo;
-	memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name));
-	memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name));
-	rport->port_id = ids->port_id;
-	rport->roles = ids->roles;
-	rport->port_state = FC_PORTSTATE_ONLINE;
+	rport->port_state = FC_PORTSTATE_ROGUE;
 	if (fci->f->dd_fcrport_size)
 		rport->dd_data = &rport[1];
 	rport->channel = channel;
@@ -2463,33 +2481,164 @@ fc_rport_create(struct Scsi_Host *shost,
 	INIT_WORK(&rport->stgt_delete_work, fc_starget_delete);
 	INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete);
 
+	dev = &rport->dev;
+	device_initialize(dev);                 /* takes self reference */
+	dev->parent = get_device(&shost->shost_gendev); /* parent reference */
+	dev->release = fc_rport_dev_release;
+	sprintf(dev->bus_id, "rport-%d:%d-%d",
+		shost->host_no, channel, rport->number);
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	list_add_tail(&rport->peers, &fc_host->rogue_rports);
+	get_device(&shost->shost_gendev); /* for fc_host->*rports list */
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return rport;
+}
+EXPORT_SYMBOL(fc_remote_port_alloc);
+
+/**
+ * fc_remote_port_free - drop reference from allocation
+ * @rport:	remote port
+ *
+ * This should be called if the LLD has not called
+ * add on the rport and wishes to free the resources for the rogue port.
+ * For example if discovery failed, then the LLD should call this
+ * function to free the rport that was allocated.
+ */
+int 
+fc_remote_port_free(struct fc_rport *rport)
+{
+	if (rport->port_state != FC_PORTSTATE_ROGUE)
+		return -EINVAL;
+
+	__free_remote_port(rport);
+	return 0;
+}
+EXPORT_SYMBOL(fc_remote_port_free);
+
+
+static struct fc_rport *
+rport_lookup(struct fc_host_attrs *fc_host, struct list_head *list, int channel,
+		enum fc_tgtid_binding_type tgtid_bind_type,
+		struct fc_rport_identifiers *ids)
+{
+	struct fc_rport *rport;
+
+	list_for_each_entry(rport, list, peers) {
+		if (rport->channel != channel)
+			continue;
+
+		switch (tgtid_bind_type) {
+		case FC_TGTID_BIND_BY_WWPN:
+		case FC_TGTID_BIND_NONE:
+			if (rport->port_name != ids->port_name)
+				continue;
+			break;
+		case FC_TGTID_BIND_BY_WWNN:
+			if (rport->node_name != ids->node_name)
+				continue;
+			break;
+		case FC_TGTID_BIND_BY_ID:
+			if (rport->port_id != ids->port_id)
+				continue;
+			break;
+		default:
+			continue;
+		}
+		return rport;
+	}
+	return NULL;
+}
+
+
+/**
+ * fc_remote_port_lookup - lookup a remote port by id
+ * @shost:		scsi host
+ * @channel:		channel
+ * @search_type:	search type
+ * @id:			port identifiers for search
+ *
+ * If a the port is found, it will be returned with a refcount on it.
+ * The caller must do a put_device on the rport when it is done.
+ * This function will check just the active rports list.
+ * The caller should checked the port_state before processing.
+ */
+struct fc_rport *
+fc_remote_port_lookup(struct Scsi_Host *shost, int channel,
+			enum fc_search_type search_type,
+			struct fc_rport_identifiers *ids)
+{
+	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+	struct fc_rport *rport;
+	unsigned long flags;
+
 	spin_lock_irqsave(shost->host_lock, flags);
+	list_for_each_entry(rport, &fc_host->rports, peers) {
+		if (rport->channel != channel)
+			continue;
+
+		switch (search_type) {
+		case FC_SRCH_BY_WWPN:
+			if (rport->port_name != ids->port_name)
+				continue;
+			break;
+		case FC_SRCH_BY_ID:
+			if (rport->port_id != ids->port_id)
+				continue;
+			break;
+		case FC_SRCH_BY_SCSITGT_ID:
+			if (rport->scsi_target_id != ids->scsi_target_id)
+				continue;
+			break;
+		default:
+			continue;
+		}
+		/* if blocked, pretend there's no device */
+		if (rport->port_state == FC_PORTSTATE_BLOCKED)
+			continue;
+		get_device(&rport->dev);
+		spin_unlock_irqrestore(shost->host_lock, flags);
+		return rport;
+	}
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return NULL;
+}
+EXPORT_SYMBOL(fc_remote_port_lookup);
+
+static int remote_port_add(struct fc_rport *rport,
+			struct fc_rport_identifiers *ids)
+{
+	struct Scsi_Host *shost = rport_to_shost(rport);
+	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+	struct device *dev;
+	unsigned long flags;
+	int error;
+
+	memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name));
+	memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name));
+	rport->port_id = ids->port_id;
+	rport->roles = ids->roles;
+	rport->port_state = FC_PORTSTATE_ONLINE;
+
+        spin_lock_irqsave(shost->host_lock, flags);
 
 	rport->number = fc_host->next_rport_number++;
 	if (rport->roles & FC_PORT_ROLE_FCP_TARGET)
 		rport->scsi_target_id = fc_host->next_target_id++;
 	else
 		rport->scsi_target_id = -1;
-	list_add_tail(&rport->peers, &fc_host->rports);
-	get_device(&shost->shost_gendev);	/* for fc_host->rport list */
+	list_move_tail(&rport->peers, &fc_host->rports);
 
 	spin_unlock_irqrestore(shost->host_lock, flags);
 
 	dev = &rport->dev;
-	device_initialize(dev);			/* takes self reference */
-	dev->parent = get_device(&shost->shost_gendev); /* parent reference */
-	dev->release = fc_rport_dev_release;
-	sprintf(dev->bus_id, "rport-%d:%d-%d",
-		shost->host_no, channel, rport->number);
-	transport_setup_device(dev);
-
 	error = device_add(dev);
 	if (error) {
 		printk(KERN_ERR "FC Remote Port device_add failed\n");
-		goto delete_rport;
+		put_device(&shost->shost_gendev);
+		return error;
 	}
-	transport_add_device(dev);
-	transport_configure_device(dev);
+	transport_register_device(dev);
 
 	if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
 		/* initiate a scan of the target */
@@ -2497,25 +2646,18 @@ fc_rport_create(struct Scsi_Host *shost,
 		scsi_queue_work(shost, &rport->scan_work);
 	}
 
-	return rport;
-
-delete_rport:
-	transport_destroy_device(dev);
-	spin_lock_irqsave(shost->host_lock, flags);
-	list_del(&rport->peers);
-	put_device(&shost->shost_gendev);	/* for fc_host->rport list */
-	spin_unlock_irqrestore(shost->host_lock, flags);
-	put_device(dev->parent);
-	kfree(rport);
-	return NULL;
+	/* JWS - cleanup on error is really bad now - leaks structs
+	 * and references */
+	return 0;
 }
 
 /**
  * fc_remote_port_add - notify fc transport of the existence of a remote FC port.
- * @shost:	scsi host the remote port is connected to.
- * @channel:	Channel on shost port connected to.
- * @ids:	The world wide names, fc address, and FC4 port
- *		roles for the remote port.
+ * @shost:             scsi host the remote port is connected to.
+ * @rogue_rport:       optional rogue rport.
+ * @channel:           Channel on shost port connected to.
+ * @ids:               The world wide names, fc address, and FC4 port
+ *                     roles for the remote port.
  *
  * The LLDD calls this routine to notify the transport of the existence
  * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn)
@@ -2545,156 +2687,122 @@ delete_rport:
  *
  * Should not be called from interrupt context.
  *
+ * The caller should have resolved rogue with active or deleted
+ * rports before calling.
+ *
  * Notes:
  *	This routine assumes no locks are held on entry.
  */
 struct fc_rport *
 fc_remote_port_add(struct Scsi_Host *shost, int channel,
-	struct fc_rport_identifiers  *ids)
+			struct fc_rport *rogue_rport,
+			struct fc_rport_identifiers  *ids)
 {
 	struct fc_internal *fci = to_fc_internal(shost->transportt);
 	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 	struct fc_rport *rport;
 	unsigned long flags;
-	int match = 0;
 
 	/* ensure any stgt delete functions are done */
 	fc_flush_work(shost);
 
-	/*
-	 * Search the list of "active" rports, for an rport that has been
-	 * deleted, but we've held off the real delete while the target
-	 * is in a "blocked" state.
-	 */
 	spin_lock_irqsave(shost->host_lock, flags);
+	rport = rport_lookup(fc_host, &fc_host->rports, channel,
+				fc_host->tgtid_bind_type, ids);
+	if (rport == NULL)
+		rport = rport_lookup(fc_host, &fc_host->rport_bindings, channel,
+					fc_host->tgtid_bind_type, ids);
+	if (rport) {
+		switch (rport->port_state) {
+		/*
+		 * rport is live and well - we shouldn't be re-adding.
+		 * But, if we do, we drop everything in the rogue and
+		 * return the active rport.
+		 */
+		case FC_PORTSTATE_ONLINE:
+			printk(KERN_WARNING "Re-add of active rport!!\n");
+			if (rogue_rport)
+				__free_remote_port(rogue_rport);
+			return NULL;
 
-	list_for_each_entry(rport, &fc_host->rports, peers) {
-
-		if ((rport->port_state == FC_PORTSTATE_BLOCKED) &&
-			(rport->channel == channel)) {
-
-			switch (fc_host->tgtid_bind_type) {
-			case FC_TGTID_BIND_BY_WWPN:
-			case FC_TGTID_BIND_NONE:
-				if (rport->port_name == ids->port_name)
-					match = 1;
-				break;
-			case FC_TGTID_BIND_BY_WWNN:
-				if (rport->node_name == ids->node_name)
-					match = 1;
-				break;
-			case FC_TGTID_BIND_BY_ID:
-				if (rport->port_id == ids->port_id)
-					match = 1;
-				break;
-			}
-
-			if (match) {
-
-				memcpy(&rport->node_name, &ids->node_name,
-					sizeof(rport->node_name));
-				memcpy(&rport->port_name, &ids->port_name,
-					sizeof(rport->port_name));
-				rport->port_id = ids->port_id;
-
-				rport->port_state = FC_PORTSTATE_ONLINE;
-				rport->roles = ids->roles;
-
-				spin_unlock_irqrestore(shost->host_lock, flags);
+		/*
+		 * Found rport on the list of "active" rports that has been
+		 * deleted, but we've held off the real delete while the target
+		 * is in a "blocked" state.
+		 */
+		case FC_PORTSTATE_BLOCKED:
+			memcpy(&rport->node_name, &ids->node_name,
+				sizeof(rport->node_name));
+			memcpy(&rport->port_name, &ids->port_name,
+				sizeof(rport->port_name));
+			rport->port_id = ids->port_id;
+			rport->port_state = FC_PORTSTATE_ONLINE;
+			rport->roles = ids->roles;
+			spin_unlock_irqrestore(shost->host_lock, flags);
 
-				if (fci->f->dd_fcrport_size)
+			if (fci->f->dd_fcrport_size) {
+				if (rogue_rport)
+					memcpy(rport->dd_data,
+						rogue_rport->dd_data,
+						fci->f->dd_fcrport_size);
+				else
 					memset(rport->dd_data, 0,
 						fci->f->dd_fcrport_size);
-
-				/*
-				 * If we were not a target, cancel the
-				 * io terminate and rport timers, and
-				 * we're done.
-				 *
-				 * If we were a target, but our new role
-				 * doesn't indicate a target, leave the
-				 * timers running expecting the role to
-				 * change as the target fully logs in. If
-				 * it doesn't, the target will be torn down.
-				 *
-				 * If we were a target, and our role shows
-				 * we're still a target, cancel the timers
-				 * and kick off a scan.
-				 */
-
-				/* was a target, not in roles */
-				if ((rport->scsi_target_id != -1) &&
-				    (!(ids->roles & FC_PORT_ROLE_FCP_TARGET)))
-					return rport;
-
-				/*
-				 * Stop the fail io and dev_loss timers.
-				 * If they flush, the port_state will
-				 * be checked and will NOOP the function.
-				 */
-				if (!cancel_delayed_work(&rport->fail_io_work))
-					fc_flush_devloss(shost);
-				if (!cancel_delayed_work(&rport->dev_loss_work))
-					fc_flush_devloss(shost);
-
-				spin_lock_irqsave(shost->host_lock, flags);
-
-				rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
-
-				/* if target, initiate a scan */
-				if (rport->scsi_target_id != -1) {
-					rport->flags |= FC_RPORT_SCAN_PENDING;
-					scsi_queue_work(shost,
-							&rport->scan_work);
-					spin_unlock_irqrestore(shost->host_lock,
-							flags);
-					scsi_target_unblock(&rport->dev);
-				} else
-					spin_unlock_irqrestore(shost->host_lock,
-							flags);
-
-				return rport;
 			}
-		}
-	}
+			
+			/*
+			 * If we were not a target, cancel the
+			 * io terminate and rport timers, and
+			 * we're done.
+			 *
+			 * If we were a target, but our new role
+			 * doesn't indicate a target, leave the
+			 * timers running expecting the role to
+			 * change as the target fully logs in. If
+			 * it doesn't, the target will be torn down.
+			 *
+			 * If we were a target, and our role shows
+			 * we're still a target, cancel the timers
+			 * and kick off a scan.
+			 */
+			
+			/* was a target, not in roles */
+			if ((rport->scsi_target_id != -1) &&
+			    (!(ids->roles & FC_PORT_ROLE_FCP_TARGET)))
+				return rport;
 
-	/*
-	 * Search the bindings array
-	 * Note: if never a FCP target, you won't be on this list
-	 */
-	if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) {
+			/*
+			 * Stop the fail io and dev_loss timers.
+			 * If they flush, the port_state will
+			 * be checked and will NOOP the function.
+			 */
+			if (!cancel_delayed_work(&rport->fail_io_work))
+				fc_flush_devloss(shost);
+			if (!cancel_delayed_work(&rport->dev_loss_work))
+				fc_flush_devloss(shost);
 
-		/* search for a matching consistent binding */
+			spin_lock_irqsave(shost->host_lock, flags);
 
-		list_for_each_entry(rport, &fc_host->rport_bindings,
-					peers) {
-			if (rport->channel != channel)
-				continue;
+			/* JSS rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | */
+			rport->flags &= ~(FC_RPORT_DEVLOSS_PENDING);
 
-			switch (fc_host->tgtid_bind_type) {
-			case FC_TGTID_BIND_BY_WWPN:
-				if (rport->port_name == ids->port_name)
-					match = 1;
-				break;
-			case FC_TGTID_BIND_BY_WWNN:
-				if (rport->node_name == ids->node_name)
-					match = 1;
-				break;
-			case FC_TGTID_BIND_BY_ID:
-				if (rport->port_id == ids->port_id)
-					match = 1;
-				break;
-			case FC_TGTID_BIND_NONE: /* to keep compiler happy */
-				break;
-			}
+			/* if target, initiate a scan */
+			if (rport->scsi_target_id != -1) {
+				rport->flags |= FC_RPORT_SCAN_PENDING;
+				scsi_queue_work(shost, &rport->scan_work);
+				spin_unlock_irqrestore(shost->host_lock, flags);
+				scsi_target_unblock(&rport->dev);
+			} else
+				spin_unlock_irqrestore(shost->host_lock, flags);
 
-			if (match) {
-				list_move_tail(&rport->peers, &fc_host->rports);
-				break;
-			}
-		}
+			if (rogue_rport)
+				if (fc_remote_port_free(rogue_rport))
+					printk(KERN_ERR
+						"non-Rogue rport added\n");
+			return rport;
 
-		if (match) {
+		/* must have been on rport_bindings */
+		case FC_PORTSTATE_NOTPRESENT:
 			memcpy(&rport->node_name, &ids->node_name,
 				sizeof(rport->node_name));
 			memcpy(&rport->port_name, &ids->port_name,
@@ -2703,9 +2811,17 @@ fc_remote_port_add(struct Scsi_Host *sho
 			rport->roles = ids->roles;
 			rport->port_state = FC_PORTSTATE_ONLINE;
 
-			if (fci->f->dd_fcrport_size)
-				memset(rport->dd_data, 0,
+			list_move_tail(&rport->peers, &fc_host->rports);
+
+			if (fci->f->dd_fcrport_size) {
+				if (rogue_rport)
+					memcpy(rport->dd_data,
+						rogue_rport->dd_data,
+						fci->f->dd_fcrport_size);
+				else
+					memset(rport->dd_data, 0,
 						fci->f->dd_fcrport_size);
+			}
 
 			if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
 				/* initiate a scan of the target */
@@ -2716,14 +2832,33 @@ fc_remote_port_add(struct Scsi_Host *sho
 			} else
 				spin_unlock_irqrestore(shost->host_lock, flags);
 
+			if (rogue_rport)
+				if (fc_remote_port_free(rogue_rport))
+					printk(KERN_ERR
+						"non-Rogue rport added\n");
 			return rport;
+
+		/* new rport */
+		default:
+			break;
 		}
 	}
 
 	spin_unlock_irqrestore(shost->host_lock, flags);
 
 	/* No consistent binding found - create new remote port entry */
-	rport = fc_rport_create(shost, channel, ids);
+	if (rogue_rport)
+		rport = rogue_rport;
+	else {
+		rport = fc_remote_port_alloc(shost, channel);
+		if (!rport)
+			return NULL;
+	}
+
+	if (remote_port_add(rport, ids)) {
+		__free_remote_port(rport);
+		return NULL;
+	}
 
 	return rport;
 }
@@ -2798,6 +2933,12 @@ fc_remote_port_delete(struct fc_rport  *
 
 	spin_lock_irqsave(shost->host_lock, flags);
 
+	if (rport->port_state == FC_PORTSTATE_ROGUE) {
+		spin_unlock_irqrestore(shost->host_lock, flags);
+		__free_remote_port(rport);
+		return;
+	}
+
 	if (rport->port_state != FC_PORTSTATE_ONLINE) {
 		spin_unlock_irqrestore(shost->host_lock, flags);
 		return;
@@ -2971,7 +3112,7 @@ fc_timeout_deleted_rport(struct work_str
 
 	if ((fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) ||
 	    (rport->scsi_target_id == -1)) {
-		list_del(&rport->peers);
+		list_del_init(&rport->peers);
 		rport->port_state = FC_PORTSTATE_DELETED;
 		dev_printk(KERN_ERR, &rport->dev,
 			"blocked FC remote port time out: removing"
@@ -3158,7 +3299,6 @@ fc_vport_setup(struct Scsi_Host *shost, 
 		goto delete_vport;
 	}
 	transport_add_device(dev);
-	transport_configure_device(dev);
 
 	error = fci->f->vport_create(vport, ids->disable);
 	if (error) {
diff -upNr a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
--- a/include/scsi/scsi_transport_fc.h	2008-09-06 08:12:23.000000000 -0400
+++ b/include/scsi/scsi_transport_fc.h	2008-09-19 14:23:14.000000000 -0400
@@ -82,6 +82,7 @@ enum fc_port_state {
 	FC_PORTSTATE_ERROR,
 	FC_PORTSTATE_LOOPBACK,
 	FC_PORTSTATE_DELETED,
+	FC_PORTSTATE_ROGUE,
 };
 
 
@@ -145,6 +146,15 @@ enum fc_tgtid_binding_type  {
 };
 
 /*
+ * fc_search_type: ways to look for rports in the transport
+ */
+enum fc_search_type  {
+	FC_SRCH_BY_WWPN,
+	FC_SRCH_BY_ID,			/* port_id - aka address */
+	FC_SRCH_BY_SCSITGT_ID,
+};
+
+/*
  * FC Port Roles
  * Note: values are not enumerated, as they can be "or'd" together
  * for reporting (e.g. report roles). If you alter this list,
@@ -279,12 +289,17 @@ struct fc_vport {
  * to report the existence of a remote FC port in the topology. Internally,
  * the transport uses this data for attributes and to manage consistent
  * target id bindings.
+ *
+ * Notes:
+ *   scsi_target_id: Is only used when ids are passed for searching
+ *       It is not used for "adds" or "role changes".
  */
 struct fc_rport_identifiers {
 	u64 node_name;
 	u64 port_name;
 	u32 port_id;
 	u32 roles;
+	u32 scsi_target_id;
 };
 
 
@@ -501,6 +516,7 @@ struct fc_host_attrs {
 	/* internal data */
 	struct list_head rports;
 	struct list_head rport_bindings;
+	struct list_head rogue_rports;
 	struct list_head vports;
 	u32 next_rport_number;
 	u32 next_target_id;
@@ -737,8 +753,14 @@ struct scsi_transport_template *fc_attac
 			struct fc_function_template *);
 void fc_release_transport(struct scsi_transport_template *);
 void fc_remove_host(struct Scsi_Host *);
-struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost,
-			int channel, struct fc_rport_identifiers  *ids);
+struct fc_rport *fc_remote_port_lookup(struct Scsi_Host *shost, int channel,
+                               enum fc_search_type search_type,
+                               struct fc_rport_identifiers *ids);
+struct fc_rport *fc_remote_port_alloc(struct Scsi_Host *shost, int channel);
+int fc_remote_port_free(struct fc_rport *rport);
+struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost, int channel,
+                       struct fc_rport *rogue_rport,
+                       struct fc_rport_identifiers  *ids);
 void fc_remote_port_delete(struct fc_rport  *rport);
 void fc_remote_port_rolechg(struct fc_rport  *rport, u32 roles);
 int scsi_is_fc_rport(const struct device *);





More information about the devel mailing list