Răsfoiți Sursa

[SCSI] libfc: do not log off rports before or after discovery

When receiving an RSCN, do not log off all rports.  This is
extremely disruptive.  If, after the GPN_FT response, some
rports haven't been listed, delete them.

Add field disc_id to structs fc_rport_priv and fc_disc.
disc_id is an arbitrary serial number used to identify the
rports found by the latest discovery.  This eliminates the need
to go through the rport list when restarting discovery.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Joe Eykholt 16 ani în urmă
părinte
comite
0f6c614987
2 a modificat fișierele cu 37 adăugiri și 13 ștergeri
  1. 34 13
      drivers/scsi/libfc/fc_disc.c
  2. 3 0
      include/scsi/libfc.h

+ 34 - 13
drivers/scsi/libfc/fc_disc.c

@@ -221,17 +221,19 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
  */
 static void fc_disc_restart(struct fc_disc *disc)
 {
-	struct fc_rport_priv *rdata, *next;
-	struct fc_lport *lport = disc->lport;
-
 	FC_DISC_DBG(disc, "Restarting discovery\n");
 
-	list_for_each_entry_safe(rdata, next, &disc->rports, peers)
-		lport->tt.rport_logoff(rdata);
-
 	disc->requested = 1;
-	if (!disc->pending)
-		fc_disc_gpn_ft_req(disc);
+	if (disc->pending)
+		return;
+
+	/*
+	 * Advance disc_id.  This is an arbitrary non-zero number that will
+	 * match the value in the fc_rport_priv after discovery for all
+	 * freshly-discovered remote ports.  Avoid wrapping to zero.
+	 */
+	disc->disc_id = (disc->disc_id + 2) | 1;
+	fc_disc_gpn_ft_req(disc);
 }
 
 /**
@@ -278,6 +280,7 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
 		}
 		kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
 	} else {
+		disc->disc_id = (disc->disc_id + 2) | 1;
 		fc_disc_gpn_ft_req(disc);	/* get ports by FC-4 type */
 	}
 
@@ -345,13 +348,30 @@ static int fc_disc_new_target(struct fc_disc *disc,
 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 {
 	struct fc_lport *lport = disc->lport;
+	struct fc_rport_priv *rdata;
 
 	FC_DISC_DBG(disc, "Discovery complete\n");
 
-	if (disc->requested)
-		fc_disc_gpn_ft_req(disc);
-	else
-		disc->pending = 0;
+	disc->pending = 0;
+	if (disc->requested) {
+		fc_disc_restart(disc);
+		return;
+	}
+
+	/*
+	 * Go through all remote ports.  If they were found in the latest
+	 * discovery, reverify or log them in.  Otherwise, log them out.
+	 * Skip ports which were never discovered.  These are the dNS port
+	 * and ports which were created by PLOGI.
+	 */
+	list_for_each_entry(rdata, &disc->rports, peers) {
+		if (!rdata->disc_id)
+			continue;
+		if (rdata->disc_id == disc->disc_id)
+			lport->tt.rport_login(rdata);
+		else
+			lport->tt.rport_logoff(rdata);
+	}
 
 	mutex_unlock(&disc->disc_mutex);
 	disc->disc_callback(lport, event);
@@ -496,7 +516,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 		    ids.port_name != lport->wwpn) {
 			rdata = lport->tt.rport_create(lport, &ids);
 			if (rdata)
-				lport->tt.rport_login(rdata);
+				rdata->disc_id = disc->disc_id;
 			else
 				printk(KERN_WARNING "libfc: Failed to allocate "
 				       "memory for the newly discovered port "
@@ -640,6 +660,7 @@ static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
 
 	rdata = lport->tt.rport_create(lport, &dp->ids);
 	if (rdata) {
+		rdata->disc_id = disc->disc_id;
 		kfree(dp);
 		lport->tt.rport_login(rdata);
 	}

+ 3 - 0
include/scsi/libfc.h

@@ -202,6 +202,7 @@ struct fc_rport_libfc_priv {
  * @ids: remote port identifiers and roles
  * @flags: REC and RETRY supported flags
  * @max_seq: maximum number of concurrent sequences
+ * @disc_id: discovery identifier
  * @maxframe_size: maximum frame size
  * @retries: retry count in current state
  * @e_d_tov: error detect timeout value (in msec)
@@ -218,6 +219,7 @@ struct fc_rport_priv {
 	struct fc_rport_identifiers ids;
 	u16			   flags;
 	u16		           max_seq;
+	u16			   disc_id;
 	u16			   maxframe_size;
 	unsigned int	           retries;
 	unsigned int	           e_d_tov;
@@ -678,6 +680,7 @@ struct fc_disc {
 	unsigned char		requested;
 	unsigned short		seq_count;
 	unsigned char		buf_len;
+	u16			disc_id;
 
 	void (*disc_callback)(struct fc_lport *,
 			      enum fc_disc_event);