diff options
-rw-r--r-- | drivers/scsi/libfc/fc_disc.c | 137 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_elsct.c | 2 | ||||
-rw-r--r-- | include/scsi/fc_encode.h | 16 |
3 files changed, 128 insertions, 27 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 *); | |||
47 | static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); | 47 | static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); |
48 | static void fc_disc_done(struct fc_disc *, enum fc_disc_event); | 48 | static void fc_disc_done(struct fc_disc *, enum fc_disc_event); |
49 | static void fc_disc_timeout(struct work_struct *); | 49 | static void fc_disc_timeout(struct work_struct *); |
50 | static void fc_disc_single(struct fc_disc *, struct fc_disc_port *); | 50 | static int fc_disc_single(struct fc_lport *, struct fc_disc_port *); |
51 | static void fc_disc_restart(struct fc_disc *); | 51 | static 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 | */ |
572 | static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp) | 576 | static 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)); | ||
630 | redisc: | ||
631 | fc_disc_restart(disc); | ||
587 | } | 632 | } |
588 | return; | ||
589 | out: | 633 | out: |
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 | */ | ||
647 | static 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 | */ | ||
671 | static 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 | ||
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index c5ee6bb79e05..27dad703824f 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h | |||
@@ -32,6 +32,7 @@ struct fc_ct_req { | |||
32 | struct fc_ns_gid_ft gid; | 32 | struct fc_ns_gid_ft gid; |
33 | struct fc_ns_rn_id rn; | 33 | struct fc_ns_rn_id rn; |
34 | struct fc_ns_rft rft; | 34 | struct fc_ns_rft rft; |
35 | struct fc_ns_fid fid; | ||
35 | } payload; | 36 | } payload; |
36 | }; | 37 | }; |
37 | 38 | ||
@@ -94,10 +95,16 @@ static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp, | |||
94 | } | 95 | } |
95 | 96 | ||
96 | /** | 97 | /** |
97 | * fc_ct_fill - Fill in a name service request frame | 98 | * fc_ct_fill() - Fill in a name service request frame |
99 | * @lport: local port. | ||
100 | * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries. | ||
101 | * @fp: frame to contain payload. | ||
102 | * @op: CT opcode. | ||
103 | * @r_ctl: pointer to FC header R_CTL. | ||
104 | * @fh_type: pointer to FC-4 type. | ||
98 | */ | 105 | */ |
99 | static inline int fc_ct_fill(struct fc_lport *lport, | 106 | static inline int fc_ct_fill(struct fc_lport *lport, |
100 | struct fc_frame *fp, | 107 | u32 fc_id, struct fc_frame *fp, |
101 | unsigned int op, enum fc_rctl *r_ctl, | 108 | unsigned int op, enum fc_rctl *r_ctl, |
102 | enum fc_fh_type *fh_type) | 109 | enum fc_fh_type *fh_type) |
103 | { | 110 | { |
@@ -109,6 +116,11 @@ static inline int fc_ct_fill(struct fc_lport *lport, | |||
109 | ct->payload.gid.fn_fc4_type = FC_TYPE_FCP; | 116 | ct->payload.gid.fn_fc4_type = FC_TYPE_FCP; |
110 | break; | 117 | break; |
111 | 118 | ||
119 | case FC_NS_GPN_ID: | ||
120 | ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_fid)); | ||
121 | hton24(ct->payload.fid.fp_fid, fc_id); | ||
122 | break; | ||
123 | |||
112 | case FC_NS_RFT_ID: | 124 | case FC_NS_RFT_ID: |
113 | ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft)); | 125 | ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft)); |
114 | hton24(ct->payload.rft.fid.fp_fid, | 126 | hton24(ct->payload.rft.fid.fp_fid, |