aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc
diff options
context:
space:
mode:
authorJoe Eykholt <jeykholt@cisco.com>2009-08-25 17:03:58 -0400
committerJames Bottomley <James.Bottomley@suse.de>2009-09-10 13:08:03 -0400
commit2ab7e1ecb81ce35ed8e8df512e3fc6338a4c55bb (patch)
treecc4fea4717a66e7d1428505bb409146d37e52668 /drivers/scsi/libfc
parent8abbe3a42324264c9d5cc4e7c3d265b5be6d82d6 (diff)
[SCSI] libfc: send GPN_ID in reaction to single-port RSCNs.
When an RSCN indicates changes to individual remote ports, don't blindly log them out and then back in. Instead, determine whether they're still in the directory, by doing GPN_ID. If that is successful, call login, which will send ADISC and reverify, otherwise, call logoff. Perhaps we should just delete the rport, not send LOGO, but it seems safer. Also, fix a possible issue where if a mix of records in the RSCN cause us to queue disc_ports for disc_single and then we decide to do full rediscovery, we leak memory for those disc_ports queued. So, go through the list of disc_ports even if doing full discovery. Free the disc_ports in any case. If any of the disc_single() calls return error, do a full discovery. The ability to fill in GPN_ID requests was added to fc_ct_fill(). For this, it needs the FC_ID to be passed in as an arg. The did parameter for fc_elsct_send() is used for that, since the actual D_DID will always be 0xfffffc for all CT requests so far. 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.c137
-rw-r--r--drivers/scsi/libfc/fc_elsct.c2
2 files changed, 114 insertions, 25 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 4242894cce7c..c48799e9dd8e 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -47,7 +47,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *);
47static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); 47static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
48static void fc_disc_done(struct fc_disc *, enum fc_disc_event); 48static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
49static void fc_disc_timeout(struct work_struct *); 49static void fc_disc_timeout(struct work_struct *);
50static void fc_disc_single(struct fc_disc *, struct fc_disc_port *); 50static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
51static void fc_disc_restart(struct fc_disc *); 51static void fc_disc_restart(struct fc_disc *);
52 52
53/** 53/**
@@ -83,7 +83,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
83 struct fc_disc *disc) 83 struct fc_disc *disc)
84{ 84{
85 struct fc_lport *lport; 85 struct fc_lport *lport;
86 struct fc_rport_priv *rdata;
87 struct fc_els_rscn *rp; 86 struct fc_els_rscn *rp;
88 struct fc_els_rscn_page *pp; 87 struct fc_els_rscn_page *pp;
89 struct fc_seq_els_data rjt_data; 88 struct fc_seq_els_data rjt_data;
@@ -150,6 +149,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
150 } 149 }
151 } 150 }
152 lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); 151 lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
152
153 /*
154 * If not doing a complete rediscovery, do GPN_ID on
155 * the individual ports mentioned in the list.
156 * If any of these get an error, do a full rediscovery.
157 * In any case, go through the list and free the entries.
158 */
159 list_for_each_entry_safe(dp, next, &disc_ports, peers) {
160 list_del(&dp->peers);
161 if (!redisc)
162 redisc = fc_disc_single(lport, dp);
163 kfree(dp);
164 }
153 if (redisc) { 165 if (redisc) {
154 FC_DISC_DBG(disc, "RSCN received: rediscovering\n"); 166 FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
155 fc_disc_restart(disc); 167 fc_disc_restart(disc);
@@ -157,14 +169,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
157 FC_DISC_DBG(disc, "RSCN received: not rediscovering. " 169 FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
158 "redisc %d state %d in_prog %d\n", 170 "redisc %d state %d in_prog %d\n",
159 redisc, lport->state, disc->pending); 171 redisc, lport->state, disc->pending);
160 list_for_each_entry_safe(dp, next, &disc_ports, peers) {
161 list_del(&dp->peers);
162 rdata = lport->tt.rport_lookup(lport, dp->port_id);
163 if (rdata) {
164 lport->tt.rport_logoff(rdata);
165 }
166 fc_disc_single(disc, dp);
167 }
168 } 172 }
169 fc_frame_free(fp); 173 fc_frame_free(fp);
170 return; 174 return;
@@ -562,32 +566,117 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
562} 566}
563 567
564/** 568/**
565 * fc_disc_single() - Discover the directory information for a single target 569 * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
566 * @lport: FC local port 570 * @sp: exchange sequence
567 * @dp: The port to rediscover 571 * @fp: response frame
572 * @rdata_arg: remote port private data
568 * 573 *
569 * Locking Note: This function expects that the disc_mutex is locked 574 * Locking Note: This function is called without disc mutex held.
570 * before it is called.
571 */ 575 */
572static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) 576static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
577 void *rdata_arg)
573{ 578{
579 struct fc_rport_priv *rdata = rdata_arg;
580 struct fc_rport_priv *new_rdata;
574 struct fc_lport *lport; 581 struct fc_lport *lport;
575 struct fc_rport_priv *rdata; 582 struct fc_disc *disc;
583 struct fc_ct_hdr *cp;
584 struct fc_ns_gid_pn *pn;
585 u64 port_name;
576 586
577 lport = disc->lport; 587 lport = rdata->local_port;
588 disc = &lport->disc;
578 589
579 if (dp->port_id == fc_host_port_id(lport->host)) 590 mutex_lock(&disc->disc_mutex);
591 if (PTR_ERR(fp) == -FC_EX_CLOSED)
580 goto out; 592 goto out;
593 if (IS_ERR(fp))
594 goto redisc;
595
596 cp = fc_frame_payload_get(fp, sizeof(*cp));
597 if (!cp)
598 goto redisc;
599 if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
600 if (fr_len(fp) < sizeof(struct fc_frame_header) +
601 sizeof(*cp) + sizeof(*pn))
602 goto redisc;
603 pn = (struct fc_ns_gid_pn *)(cp + 1);
604 port_name = get_unaligned_be64(&pn->fn_wwpn);
605 if (rdata->ids.port_name == -1)
606 rdata->ids.port_name = port_name;
607 else if (rdata->ids.port_name != port_name) {
608 FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
609 "Port-id %x wwpn %llx\n",
610 rdata->ids.port_id, port_name);
611 lport->tt.rport_logoff(rdata);
581 612
582 rdata = lport->tt.rport_create(lport, dp->port_id); 613 new_rdata = lport->tt.rport_create(lport,
583 if (rdata) { 614 rdata->ids.port_id);
615 if (new_rdata) {
616 new_rdata->disc_id = disc->disc_id;
617 lport->tt.rport_login(new_rdata);
618 }
619 goto out;
620 }
584 rdata->disc_id = disc->disc_id; 621 rdata->disc_id = disc->disc_id;
585 kfree(dp);
586 lport->tt.rport_login(rdata); 622 lport->tt.rport_login(rdata);
623 } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
624 FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
625 cp->ct_reason, cp->ct_explan);
626 lport->tt.rport_logoff(rdata);
627 } else {
628 FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
629 ntohs(cp->ct_cmd));
630redisc:
631 fc_disc_restart(disc);
587 } 632 }
588 return;
589out: 633out:
590 kfree(dp); 634 mutex_unlock(&disc->disc_mutex);
635 kref_put(&rdata->kref, lport->tt.rport_destroy);
636}
637
638/**
639 * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
640 * @lport: local port
641 * @rdata: remote port private data
642 *
643 * Locking Note: This function expects that the disc_mutex is locked
644 * before it is called.
645 * On failure, an error code is returned.
646 */
647static int fc_disc_gpn_id_req(struct fc_lport *lport,
648 struct fc_rport_priv *rdata)
649{
650 struct fc_frame *fp;
651
652 fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
653 sizeof(struct fc_ns_fid));
654 if (!fp)
655 return -ENOMEM;
656 if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
657 fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
658 return -ENOMEM;
659 kref_get(&rdata->kref);
660 return 0;
661}
662
663/**
664 * fc_disc_single() - Discover the directory information for a single target
665 * @lport: local port
666 * @dp: The port to rediscover
667 *
668 * Locking Note: This function expects that the disc_mutex is locked
669 * before it is called.
670 */
671static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
672{
673 struct fc_rport_priv *rdata;
674
675 rdata = lport->tt.rport_create(lport, dp->port_id);
676 if (!rdata)
677 return -ENOMEM;
678 rdata->disc_id = 0;
679 return fc_disc_gpn_id_req(lport, rdata);
591} 680}
592 681
593/** 682/**
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index d655924d46b6..5cfa68732e9d 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -49,7 +49,7 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
49 rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type); 49 rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
50 else { 50 else {
51 /* CT requests */ 51 /* CT requests */
52 rc = fc_ct_fill(lport, fp, op, &r_ctl, &fh_type); 52 rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type);
53 did = FC_FID_DIR_SERV; 53 did = FC_FID_DIR_SERV;
54 } 54 }
55 55