summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc
diff options
context:
space:
mode:
authorJoe Eykholt <jeykholt@cisco.com>2009-08-25 14:02:11 -0700
committerJames Bottomley <James.Bottomley@suse.de>2009-09-10 12:07:48 -0500
commit0f6c6149870e03c722af6eae406758b28cb71320 (patch)
tree7dca629255539d73bd44fea4fb50a9c0a9ddd079 /drivers/scsi/libfc
parent8025b5db7e10cd90cadec940cc766be3bbda65e8 (diff)
[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>
Diffstat (limited to 'drivers/scsi/libfc')
-rw-r--r--drivers/scsi/libfc/fc_disc.c47
1 files changed, 34 insertions, 13 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index f6762a52147..ddf494424ff 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/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);
}